diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a71016a..bb90b91 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,11 +31,11 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 - - name: Run tests - run: ./gradlew :phosphor-core:jvmTest + - name: Run JVM tests + run: ./gradlew :phosphor-core:jvmTest :phosphor-lumos:jvmTest - name: Check formatting - run: ./gradlew :phosphor-core:ktlintCheck + run: ./gradlew :phosphor-core:ktlintCheck :phosphor-lumos:ktlintCheck ios_compile: name: iOS Compile @@ -54,8 +54,8 @@ jobs: - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 - - name: Compile iOS target - run: ./gradlew :phosphor-core:compileKotlinIosArm64 + - name: Compile iOS targets + run: ./gradlew :phosphor-core:compileKotlinIosArm64 :phosphor-lumos:compileKotlinIosArm64 test: name: Tests diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f41a654..f9db3fa 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -47,7 +47,7 @@ jobs: uses: gradle/actions/setup-gradle@v4 - name: Run tests before publish - run: ./gradlew :phosphor-core:jvmTest :phosphor-core:compileKotlinIosArm64 + run: ./gradlew :phosphor-core:jvmTest :phosphor-lumos:jvmTest :phosphor-core:compileKotlinIosArm64 :phosphor-lumos:compileKotlinIosArm64 - name: Decode signing key if: ${{ github.event.inputs.dry_run != 'true' }} @@ -64,13 +64,13 @@ jobs: ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }} run: | export ORG_GRADLE_PROJECT_signingInMemoryKey="$(cat /tmp/signing-key.asc)" - ./gradlew :phosphor-core:publishAllPublicationsToMavenCentralRepository + ./gradlew :phosphor-core:publishAllPublicationsToMavenCentralRepository :phosphor-lumos:publishAllPublicationsToMavenCentralRepository - name: Dry run publish if: ${{ github.event.inputs.dry_run == 'true' }} run: | echo "Dry run mode - publishing to Maven Local only" - ./gradlew :phosphor-core:publishToMavenLocal + ./gradlew :phosphor-core:publishToMavenLocal :phosphor-lumos:publishToMavenLocal - name: Create GitHub Release if: startsWith(github.ref, 'refs/tags/v') && github.event.inputs.dry_run != 'true' diff --git a/AGENTS.md b/AGENTS.md index 01a5921..19ebbf0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # AGENTS.md -Phosphor is a Kotlin Multiplatform rendering library that translates cognitive state into visible light — ASCII luminance, color ramps, particle physics, and 3D waveform surfaces. Read [SOUL.md](SOUL.md) for project philosophy and values. +Phosphor is a Kotlin Multiplatform rendering library that translates cognitive state into visible light — ASCII luminance, color ramps, particle physics, 3D waveform surfaces, and the Lumos voxel-orb companion visualization. Read [SOUL.md](SOUL.md) for project philosophy and values. ## Development Commands @@ -20,12 +20,6 @@ Phosphor is a Kotlin Multiplatform rendering library that translates cognitive s ./gradlew dokkaHtml # Generate API documentation ``` -### Demo -```bash -./gradlew :phosphor-demo:run # Run the desktop demo application -./gradlew :phosphor-demo-cli:installDist && ./phosphor-demo-cli/phosphor-demo # Run the terminal demo -``` - ### Publishing ```bash ./gradlew publishToMavenLocal # Publish to local Maven for integration testing @@ -45,13 +39,22 @@ Phosphor is a layered rendering pipeline: cognitive signals flow in, visible cha | Emitter | `emitter/` | EmitterEffect sealed hierarchy — transient visual events (SparkBurst, HeightPulse, Turbulence, ColorWash) | | Bridge | `bridge/` | Adapters connecting live runtime state to the animation field | -### Rendering Surface Adapters (separate modules) +### Active Modules + +| Module | Purpose | +|--------|---------| +| `phosphor-core/` | Shared rendering pipeline — signals, fields, palettes, cells, choreography, emitters, and runtime bridges | +| `phosphor-lumos/` | Framework-free voxel-orb companion visualization for cognitive state, built as a sibling output module over `phosphor-core` | + +### Future / Aspirational Modules | Module | Purpose | |--------|---------| | `phosphor-mosaic/` | Mosaic (Compose-for-terminal) adapter — AnnotatedString output | | `phosphor-compose/` | Compose Multiplatform Canvas adapter — DrawScope rendering | | `phosphor-ansi/` | Raw ANSI escape code output for non-Compose terminals | +| `phosphor-demo/` | Desktop demo app (Compose Desktop) | +| `phosphor-demo-cli/` | Terminal demo app | ## Key Paths @@ -59,10 +62,8 @@ Phosphor is a layered rendering pipeline: cognitive signals flow in, visible cha |--------|---------| | `phosphor-core/src/commonMain/` | Shared rendering pipeline — signals, fields, palettes, cells | | `phosphor-core/src/commonTest/` | Unit tests for palette mapping, projection math, particle physics | -| `phosphor-mosaic/src/jvmMain/` | Terminal rendering via Mosaic AnnotatedString | -| `phosphor-compose/src/commonMain/` | Compose Canvas rendering via DrawScope | -| `phosphor-demo/` | Desktop demo app (Compose Desktop) | -| `phosphor-demo-cli/` | Terminal demo app | +| `phosphor-lumos/src/commonMain/` | Lumos module boundary for framework-free voxel-orb visualization data | +| `phosphor-lumos/src/commonTest/` | Lumos smoke tests and future voxel-frame API tests | ## Before You Change Anything diff --git a/README.md b/README.md index 89e8819..12a96bc 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ Phosphor is a Kotlin Multiplatform rendering library that transduces cognitive s Just as CRT phosphor converts electron energy into visible light, this library converts an AI agent's internal state into something you can see: sparse floating dots coalescing into dense bright strokes as cognition shifts from perception to execution. +Phosphor ships `:phosphor-core` alongside `:phosphor-lumos`, its first separate output module. Lumos is a framework-free voxel-orb companion visualization for cognitive state; it complements the CRT-phosphor terminal aesthetic and does not replace it. + --- ## The Pipeline @@ -34,7 +36,14 @@ Signal → Field → Palette → Render → Choreography → Emitter → Surface | **Emitter** | `emitter/` | Transient effects — spark bursts, height pulses, turbulence — that decay naturally | | **Bridge** | `bridge/` | Connects runtime cognitive state to the animation field | -`phosphor-core` has zero UI framework dependencies. Surface adapters (phosphor-mosaic, phosphor-compose, phosphor-ansi) live in separate modules. +`phosphor-core` has zero UI framework dependencies. Platform-specific rendering belongs in separate surface modules; `:phosphor-lumos` is the first concrete sibling module and establishes the boundary for framework-free voxel-orb frame data. + +## Modules + +| Module | Purpose | +|--------|---------| +| `:phosphor-core` | Shared rendering pipeline — signals, fields, palettes, cells, choreography, emitters, and runtime bridges | +| `:phosphor-lumos` | Voxel-orb companion visualization module for cognitive state, scaffolded for downstream Lumos frame APIs | --- diff --git a/phosphor-core/build.gradle.kts b/phosphor-core/build.gradle.kts index d47b508..8005e97 100644 --- a/phosphor-core/build.gradle.kts +++ b/phosphor-core/build.gradle.kts @@ -1,6 +1,8 @@ import com.vanniktech.maven.publish.JavadocJar import com.vanniktech.maven.publish.KotlinMultiplatform import com.vanniktech.maven.publish.SonatypeHost +import org.gradle.plugins.signing.Sign +import org.gradle.plugins.signing.SigningExtension import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework @@ -18,6 +20,24 @@ val phosphorVersion: String by project group = "link.socket" version = phosphorVersion +val hasInMemorySigningCredentials = + providers.gradleProperty("signingInMemoryKey").isPresent && + providers.gradleProperty("signingInMemoryKeyId").isPresent && + providers.gradleProperty("signingInMemoryKeyPassword").isPresent +val hasFileSigningCredentials = + providers.gradleProperty("signing.secretKeyRingFile").isPresent && + providers.gradleProperty("signing.keyId").isPresent && + providers.gradleProperty("signing.password").isPresent +val hasGpgSigningCredentials = providers.gradleProperty("signing.gnupg.keyName").isPresent +val hasSigningCredentials = + hasInMemorySigningCredentials || + hasFileSigningCredentials || + hasGpgSigningCredentials +val isPublishingToMavenLocal = + gradle.startParameter.taskNames.any { + it == "publishToMavenLocal" || it.endsWith(":publishToMavenLocal") + } + kotlin { applyDefaultHierarchyTemplate() @@ -111,3 +131,15 @@ mavenPublishing { } } } + +plugins.withId("signing") { + extensions.configure("signing") { + if (hasGpgSigningCredentials) { + useGpgCmd() + } + } +} + +tasks.withType().configureEach { + enabled = hasSigningCredentials && !isPublishingToMavenLocal +} diff --git a/phosphor-lumos/build.gradle.kts b/phosphor-lumos/build.gradle.kts new file mode 100644 index 0000000..f5e1ee8 --- /dev/null +++ b/phosphor-lumos/build.gradle.kts @@ -0,0 +1,141 @@ +import com.vanniktech.maven.publish.JavadocJar +import com.vanniktech.maven.publish.KotlinMultiplatform +import com.vanniktech.maven.publish.SonatypeHost +import org.gradle.plugins.signing.Sign +import org.gradle.plugins.signing.SigningExtension +import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework + +plugins { + kotlin("multiplatform") + kotlin("plugin.serialization") + id("org.jlleitschuh.gradle.ktlint") + id("org.jetbrains.dokka") + id("com.vanniktech.maven.publish") +} + +val phosphorVersion: String by project + +group = "link.socket" +version = phosphorVersion + +val hasInMemorySigningCredentials = + providers.gradleProperty("signingInMemoryKey").isPresent && + providers.gradleProperty("signingInMemoryKeyId").isPresent && + providers.gradleProperty("signingInMemoryKeyPassword").isPresent +val hasFileSigningCredentials = + providers.gradleProperty("signing.secretKeyRingFile").isPresent && + providers.gradleProperty("signing.keyId").isPresent && + providers.gradleProperty("signing.password").isPresent +val hasGpgSigningCredentials = providers.gradleProperty("signing.gnupg.keyName").isPresent +val hasSigningCredentials = + hasInMemorySigningCredentials || + hasFileSigningCredentials || + hasGpgSigningCredentials +val isPublishingToMavenLocal = + gradle.startParameter.taskNames.any { + it == "publishToMavenLocal" || it.endsWith(":publishToMavenLocal") + } + +kotlin { + applyDefaultHierarchyTemplate() + + jvm { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_21) + } + } + + js(IR) { + browser() + binaries.executable() + } + + @OptIn(ExperimentalWasmDsl::class) + wasmJs { + browser() + binaries.executable() + } + + val xcf = XCFramework() + val iosTargets = listOf(iosX64(), iosArm64(), iosSimulatorArm64()) + + iosTargets.forEach { + it.binaries.framework { + baseName = "phosphor-lumos" + xcf.add(this) + } + } + + sourceSets { + val commonMain by getting { + dependencies { + implementation(project(":phosphor-core")) + } + } + val commonTest by getting { + dependencies { + implementation(kotlin("test")) + } + } + } +} + +mavenPublishing { + publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL) + signAllPublications() + + configure(KotlinMultiplatform(javadocJar = JavadocJar.Dokka("dokkaGeneratePublicationHtml"))) + + coordinates("link.socket", "phosphor-lumos", version.toString()) + + pom { + name.set("Phosphor Lumos") + description.set( + "Kotlin Multiplatform voxel-orb companion visualization for cognitive state, " + + "built on the Phosphor core signal and rendering substrate.", + ) + url.set("https://github.com/socket-link/phosphor") + inceptionYear.set("2025") + + licenses { + license { + name.set("The Apache Software License, Version 2.0") + url.set("https://www.apache.org/licenses/LICENSE-2.0.txt") + distribution.set("repo") + } + } + + developers { + developer { + id.set("socket-link") + name.set("Socket Link") + url.set("https://github.com/socket-link") + } + } + + scm { + connection.set("scm:git:git://github.com/socket-link/phosphor.git") + developerConnection.set("scm:git:ssh://git@github.com:socket-link/phosphor.git") + url.set("https://github.com/socket-link/phosphor") + } + + issueManagement { + system.set("GitHub Issues") + url.set("https://github.com/socket-link/phosphor/issues") + } + } +} + +plugins.withId("signing") { + extensions.configure("signing") { + if (hasGpgSigningCredentials) { + useGpgCmd() + } + } +} + +tasks.withType().configureEach { + enabled = hasSigningCredentials && !isPublishingToMavenLocal +} diff --git a/phosphor-lumos/src/commonMain/kotlin/link/socket/phosphor/lumos/Lumos.kt b/phosphor-lumos/src/commonMain/kotlin/link/socket/phosphor/lumos/Lumos.kt new file mode 100644 index 0000000..9550e16 --- /dev/null +++ b/phosphor-lumos/src/commonMain/kotlin/link/socket/phosphor/lumos/Lumos.kt @@ -0,0 +1,8 @@ +@file:Suppress("ktlint:standard:no-empty-file") + +/** + * Lumos provides framework-free voxel-orb visualization data for cognitive state, + * complementing Phosphor's CRT-terminal rendering pipeline without replacing it. + */ + +package link.socket.phosphor.lumos diff --git a/phosphor-lumos/src/commonTest/kotlin/link/socket/phosphor/lumos/LumosSmokeTest.kt b/phosphor-lumos/src/commonTest/kotlin/link/socket/phosphor/lumos/LumosSmokeTest.kt new file mode 100644 index 0000000..d4efad1 --- /dev/null +++ b/phosphor-lumos/src/commonTest/kotlin/link/socket/phosphor/lumos/LumosSmokeTest.kt @@ -0,0 +1,12 @@ +package link.socket.phosphor.lumos + +import kotlin.test.Test +import kotlin.test.assertEquals +import link.socket.phosphor.signal.CognitivePhase + +class LumosSmokeTest { + @Test + fun `lumos package compiles with core dependency`() { + assertEquals(CognitivePhase.NONE, CognitivePhase.valueOf("NONE")) + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 7438cb7..a60af27 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,6 +1,7 @@ rootProject.name = "Phosphor" include(":phosphor-core") +include(":phosphor-lumos") pluginManagement { repositories {