Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions docs/COGNITIVE_SCENE_RUNTIME_MIGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# CognitiveSceneRuntime Migration Notes

`CognitiveSceneRuntime` centralizes scene orchestration in one deterministic `update(dt)` call.

## Before

Consumers manually advanced every subsystem in the frame loop:

1. choreographer
2. substrate animator
3. agent layer
4. emitters
5. particles
6. flow
7. waveform
8. camera

## After

Consumers call `runtime.update(dtSeconds)` and render from the returned `SceneSnapshot`.

```kotlin
val runtime = CognitiveSceneRuntime(sceneConfiguration)

fun onFrame(deltaSeconds: Float) {
val snapshot = runtime.update(deltaSeconds)
renderSnapshot(snapshot)
}
```

## Thin Adapter Pattern

Use an adapter at the application boundary to translate runtime snapshots into your UI model.

```kotlin
data class DashboardFrame(
val agents: List<AgentVisualState>,
val particles: List<ParticleState>,
val waveform: FloatArray?,
)

class RuntimeAdapter(
private val runtime: CognitiveSceneRuntime,
) {
fun nextFrame(dtSeconds: Float): DashboardFrame {
val snapshot = runtime.update(dtSeconds)
return DashboardFrame(
agents = snapshot.agentStates,
particles = snapshot.particleStates,
waveform = snapshot.waveformHeightField,
)
}
}
```

## Notes

- `CognitiveSceneRuntime` is timing-agnostic. You own timers and frame pacing.
- Existing subsystem APIs remain available for direct use.
- For deterministic replay, reuse identical `SceneConfiguration.seed` and `dt` sequence.
- Disable unused systems with configuration toggles to avoid allocations and update work.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.roundToInt
import kotlin.math.sin
import kotlin.random.Random
import link.socket.phosphor.field.BurstEmitter
import link.socket.phosphor.field.EmitterConfig
import link.socket.phosphor.field.ParticleAttractor
Expand Down Expand Up @@ -38,6 +39,7 @@ import link.socket.phosphor.signal.CognitivePhase
class CognitiveChoreographer(
private val particles: ParticleSystem,
private val substrateAnimator: SubstrateAnimator,
private val random: Random = Random.Default,
) {
private val previousPhases = mutableMapOf<String, CognitivePhase>()

Expand Down Expand Up @@ -205,7 +207,7 @@ class CognitiveChoreographer(

// Direction points inward toward agent
val inwardAngle = angle + PI // Reverse direction
val emitter = StreamEmitter()
val emitter = StreamEmitter(random)
val config =
EmitterConfig(
type = ParticleType.MOTE,
Expand Down Expand Up @@ -272,7 +274,7 @@ class CognitiveChoreographer(
agent.position.y + sin(angle).toFloat() * PLAN_CLUSTER_RADIUS,
)

val emitter = BurstEmitter()
val emitter = BurstEmitter(random)
val config =
EmitterConfig(
type = ParticleType.MOTE,
Expand Down Expand Up @@ -323,7 +325,7 @@ class CognitiveChoreographer(
): SubstrateState {
if (canSpawn(agent.id)) {
// High-speed focused stream outward from agent
val emitter = StreamEmitter()
val emitter = StreamEmitter(random)
val config =
EmitterConfig(
type = ParticleType.SPARK,
Expand Down Expand Up @@ -393,7 +395,7 @@ class CognitiveChoreographer(

// Spawn single ambient mote (subtle sign of life)
if (canSpawn(agent.id)) {
val emitter = BurstEmitter()
val emitter = BurstEmitter(random)
val config =
EmitterConfig(
type = ParticleType.MOTE,
Expand All @@ -418,7 +420,7 @@ class CognitiveChoreographer(
position: Vector2,
count: Int,
) {
val emitter = BurstEmitter()
val emitter = BurstEmitter(random)
val config =
EmitterConfig(
type = ParticleType.SPARK,
Expand All @@ -433,7 +435,7 @@ class CognitiveChoreographer(

private fun spawnTrailFromAgent(position: Vector2) {
// Spawn trail particles radiating gently outward (executed action afterimage)
val emitter = BurstEmitter()
val emitter = BurstEmitter(random)
val config =
EmitterConfig(
type = ParticleType.TRAIL,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package link.socket.phosphor.field

import kotlin.math.roundToInt
import kotlin.random.Random
import link.socket.phosphor.choreography.AgentLayer
import link.socket.phosphor.math.Vector2

Expand All @@ -20,6 +21,7 @@ import link.socket.phosphor.math.Vector2
class FlowLayer(
val width: Int,
val height: Int,
private val random: Random = Random.Default,
) {
private val connections = mutableMapOf<String, FlowConnection>()
private var trailParticles = mutableListOf<Particle>()
Expand Down Expand Up @@ -187,7 +189,7 @@ class FlowLayer(

if (currentToken != null) {
// Spawn trail particle
if (kotlin.random.Random.nextFloat() < trailSpawnRate) {
if (random.nextFloat() < trailSpawnRate) {
val trailParticle =
Particle(
position = currentToken.position,
Expand Down
Loading