Skip to content

T2 · Oscilloscope — deterministic headless capture of VoxelFrame streams #72

Description

@wow-miley

Suggested model tier: Opus 4.x medium for Task A (determinism recon — judgment-heavy); Sonnet 4.6 low effort for B/C

Context

The recorder. An oscilloscope captures a signal over time as a trace — Oscilloscope drives the simulation headlessly (no renderer, no UI clock) and captures the VoxelFrame stream into a VoxelTrace.

The load-bearing assumption of the whole epic is determinism — and right now that's an assumption, not a verified property. If CognitiveSceneRuntime reads wall-clock time or uses unseeded Random, captures won't reproduce, and Workbench tuning becomes unreliable. This ticket converts the assumption into a tested guarantee. Expect Task A to find at least one non-injected time or RNG source; that refactor is in scope here, not a surprise.

Objective

Oscilloscope.capture(...) produces byte-identical VoxelTrace output for identical inputs, with no renderer attached.

Expected outcomes

  • Oscilloscope in :phosphor-trace: capture(config: CaptureConfig): Result<VoxelTrace> where CaptureConfig(params, seed: Long, durationMs, fps, segments: List<TraceSegment>).
  • Fixed-timestep drive: dt = 1/fps exactly; no wall-clock reads during capture.
  • Any wall-clock/unseeded-RNG sources in the simulation path refactored to injected TimeSource/seeded Random (default behavior unchanged for existing live consumers).

Technical constraints

JVM-first target is acceptable for v1 (capture runs on the Mac mini); the API stays in commonMain. No changes to live-rendering behavior — injection must default to current behavior. Result-typed errors.

Tasks

A — Determinism recon (comment before code): Audit the capture path (CognitiveSceneRuntime, VoxelFrameBuilder, choreographers, AtmosphereChoreographer, emitter/particle systems) for: wall-clock reads, Random without injected seed, iteration over unordered collections affecting output, float accumulation order. List every offender with file:line. STOP and wait — Miley ranks which to refactor vs. defer.

B — Injection refactor + Oscilloscope: Apply approved refactors (injected clock/dt + seeded Random). Implement Oscilloscope driving the runtime at fixed dt, collecting frames, assembling VoxelTrace via T1's codec.

C — Determinism proof: Test that runs capture twice with identical CaptureConfig and asserts the encoded bytes are identical. Second test: different seeds produce different traces (guards against a frozen RNG).

Validation / DoD

Byte-identical test green in CI (JVM); seed-divergence test green; existing live-path tests untouched and green (proves injection defaults preserved behavior); capture of a 5s clip completes headlessly with no Compose dependency on the classpath.

Out of scope

Cross-platform float determinism (capture happens on one machine — replay is data, so devices never re-run the math); playback (T3); UI (T4).

Human review gate

Miley reviews the Task A offender list and approves the refactor set before Task B begins.

Agent handoff prompt

You are implementing PHO-36 in socket-link/phosphor. Depends on PHO-35 (merged).
1. RECON FIRST: audit CognitiveSceneRuntime + VoxelFrameBuilder + choreographers
   + emitters for wall-clock reads, unseeded Random, unordered iteration.
   Comment every offender with file:line. STOP for approval of the refactor set.
2. Refactor approved offenders to injected TimeSource + Random(seed); defaults
   must preserve current live behavior exactly (existing tests stay green).
3. Implement Oscilloscope.capture(CaptureConfig): Result<VoxelTrace> — fixed dt,
   headless, assembles via TraceCodec.
4. Tests: (a) two captures, same config → identical encoded bytes;
   (b) different seeds → different bytes.
5. DoD: all green on JVM; no Compose imports anywhere in :phosphor-trace.
If recon contradicts any assumption in this ticket, STOP and comment.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions