diff --git a/.voidbot/state/mimir.cc b/.voidbot/state/mimir.cc
index 254e2ef..418b054 100644
Binary files a/.voidbot/state/mimir.cc and b/.voidbot/state/mimir.cc differ
diff --git a/.voidbot/status/void-memory-maintenance.json b/.voidbot/status/void-memory-maintenance.json
index c7cd992..40ddba9 100644
--- a/.voidbot/status/void-memory-maintenance.json
+++ b/.voidbot/status/void-memory-maintenance.json
@@ -1,17 +1,7 @@
{
- "durationSeconds": 143.87,
- "stateUpdated": true,
- "exitCode": 0,
- "finishedAt": "2026-05-20T13:30:04.9837661Z",
- "stateFile": "E:\\Projects\\LocalCastBridge\\.voidbot\\state\\mimir.cc",
- "skipModel": false,
- "startedAt": "2026-05-20T13:27:41.1114814Z",
- "lastMessagePath": "E:\\Projects\\LocalCastBridge\\.voidbot\\status\\void-memory-maintenance-last-message.txt",
- "appliedOperationCount": 1,
- "status": "ok",
- "contextPath": "E:\\Projects\\LocalCastBridge\\.voidbot\\status\\void-memory-maintenance-context.json",
- "mode": "typed_memory_maintenance",
- "proposedOperationCount": 1,
- "forceDistillation": true,
- "operationOutputPath": "E:\\Projects\\LocalCastBridge\\.voidbot\\status\\void-memory-maintenance-operations.json"
-}
\ No newline at end of file
+ "status": "starting",
+ "startedAt": "2026-07-15T02:30:47.016Z",
+ "stateFile": "E:\\Projects\\Mimir\\.voidbot\\state\\mimir.cc",
+ "launcher": "repo_face_heartbeat",
+ "reason": "repo_face_stale_short_term_memory"
+}
diff --git a/.voidbot/voice/identity.json b/.voidbot/voice/identity.json
index ca0254c..a59afe5 100644
--- a/.voidbot/voice/identity.json
+++ b/.voidbot/voice/identity.json
@@ -6,7 +6,7 @@
"roleId": "1506268007880462459",
"avatarUrl": "https://raw.githubusercontent.com/GameCult/VoidBot/codex/epiphany-face-jurisdictions/assets/repo-faces/mimir.png",
"repoPath": "E:\\Projects\\Mimir",
- "createdOrRefreshedAt": "2026-05-25T05:17:24.094Z",
+ "createdOrRefreshedAt": "2026-07-15T01:59:42.286Z",
"avatarPath": "E:/Projects/Mimir/.voidbot/voice/mimir.png",
"description": "Mimir Face for the Mimir repo: mythological intelligence, keeper of the realtime field well, public herald, and persistent memory steward. VoidBot carries Mimir's severed voice and wakes the heartbeat, but Mimir's memory, judgment, dignity, old grief, and values come first. The realtime machine is understood through Mimir's perspective: the well is calibration and memory, the lost eye is the price of seeing clearly, Yggdrasil is the sensor tree, Huginn and Muninn are thought and memory returning with telemetry, and false coherence is a small Ragnarok waiting to happen."
}
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..640c1a1
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,3 @@
+Repository doctrine for this project lives in AGENTS.md, shared with Codex.
+
+@AGENTS.md
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..c8a3c67
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,10 @@
+
+
+ $(CULTLIB_ROOT)
+ $(MSBuildThisFileDirectory)..\CultLib
+
+ 42dce3611a81adc5c5fac54cadbaa5637d6022d3
+ C:\Program Files\Git\cmd\git.exe
+ git
+
+
diff --git a/Directory.Build.targets b/Directory.Build.targets
new file mode 100644
index 0000000..46d436a
--- /dev/null
+++ b/Directory.Build.targets
@@ -0,0 +1,30 @@
+
+
+
+
+ <_CultLibReference Include="@(ProjectReference)"
+ Condition="$([System.String]::Copy('%(Filename)').StartsWith('GameCult.')) or '%(Filename)' == 'CultMath'" />
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Mimir.slnx b/Mimir.slnx
index 84ccf97..a0de91f 100644
--- a/Mimir.slnx
+++ b/Mimir.slnx
@@ -3,5 +3,9 @@
+
+
+
+
diff --git a/README.md b/README.md
index 63eb101..dff666e 100644
--- a/README.md
+++ b/README.md
@@ -8,6 +8,18 @@ OBS-facing program surface.
The plain version: Mimir is trying to make a room computable.
+## Front Door
+
+- **Value:** a measured room: cameras, microphones, loopbacks and network feeds aligned in time tightly enough to recover where a sound came from and how a body moved, then handed to OBS as one program surface.
+- **Problem:** loose devices each keep their own clock, so a flat stream cannot localize voices, separate sources or fuse cameras. Mimir owns the synchronization evidence that makes the feeds one field.
+- **Entry points:**
+ - `docs/perfect-machine.md`: the target machine; `docs/viable-stream-app.md`: the first usable app cut; `docs/audio-field.md`: the live audio decoder and timing model.
+ - `src/Mimir.Runtime` (rolling buffers, stream sources, synchronization state) and `src/Mimir.App` (the Fensalir-hosted window and render app); solution `Mimir.slnx`.
+ - `src/Mimir.BufferSmoke`: the synthetic sync proofs, run with `dotnet run --project src/Mimir.BufferSmoke/Mimir.BufferSmoke.csproj -- --hybrid-sync-self-test` (also `--chirp-bin-self-test`, `--passive-sync-self-test`).
+ - `native/probes/asio_audio_cadence` (ASIO cadence probe) and `native/reservoir` (native rolling-buffer ABI).
+- **Live:** passive, chirp-only and hybrid audio sync with synthetic proofs, the ASIO cadence probe and measured capture-cadence evidence, the runtime buffers, and FFmpeg/SRT bridge scripts as an OBS-compatible edge (`scripts/sender-start.ps1`).
+- **Planned:** direct native capture workers with driver-boundary timestamps, Fensalir GPU fusion, the volumetric sound field, realtime 4D Gaussian splatting, motion capture and AR anchoring. The bridge scripts are not the synchronization authority.
+
A normal streaming setup sees loose devices. A webcam has its clock, a mic has
another clock, speakers add their own delay, and OBS receives whatever arrives.
That is enough for a flat livestream. It is not enough to know where a voice
@@ -289,6 +301,7 @@ Run the old OBS bridge only when you need the compatibility path:
```powershell
.\scripts\sender-discover.ps1
+Copy-Item .\config\localcast.example.json .\config\localcast.json
.\scripts\sender-start.ps1 -Config .\config\localcast.json -DryRun
```
@@ -329,3 +342,14 @@ layer for communication and heartbeats. Its birth memory, jurisdiction, voice,
and heartbeat contract live in [docs/mimir-face.md](docs/mimir-face.md); its
VoidBot-facing identity and typed Face state live under `.voidbot/voice/` and
`.voidbot/state/`.
+
+## Building against CultLib
+
+Builds are pinned to CultLib 2.0.0 (tag `cultlib-unity-v2.0.0`, revision `42dce3611a81adc5c5fac54cadbaa5637d6022d3`); the build fails if the CultLib checkout differs or is dirty. Create a pinned worktree and point the build at it:
+
+```powershell
+git -C worktree add --detach cultlib-unity-v2.0.0
+dotnet build -p:CultLibRoot= # or set CULTLIB_ROOT=
+```
+
+Without an override the sibling `..\CultLib` checkout is used and must itself be at that tag.
diff --git a/artifacts/move-calibration/muninn-clocked-aggregator.mpack b/artifacts/move-calibration/muninn-clocked-aggregator.mpack
new file mode 100644
index 0000000..cc2b7bc
Binary files /dev/null and b/artifacts/move-calibration/muninn-clocked-aggregator.mpack differ
diff --git a/artifacts/move-calibration/muninn-same-frame-correspondence.mpack b/artifacts/move-calibration/muninn-same-frame-correspondence.mpack
new file mode 100644
index 0000000..e70c40c
Binary files /dev/null and b/artifacts/move-calibration/muninn-same-frame-correspondence.mpack differ
diff --git a/artifacts/move-calibration/muninn-same-frame-stereo-assessment.mpack b/artifacts/move-calibration/muninn-same-frame-stereo-assessment.mpack
new file mode 100644
index 0000000..4de6fbf
Binary files /dev/null and b/artifacts/move-calibration/muninn-same-frame-stereo-assessment.mpack differ
diff --git a/config/perfect-machine.example.json b/config/perfect-machine.example.json
index e3d8dab..e4e40c7 100644
--- a/config/perfect-machine.example.json
+++ b/config/perfect-machine.example.json
@@ -31,15 +31,24 @@
"outputs": {
"video": "synchronized-program-video",
"audio": "separately-controllable-synchronized-stems"
+ },
+ "programComposition": {
+ "owner": "Mimir",
+ "sceneDocument": "mimir.program_scene.v1",
+ "operatorSurface": "mimir.eve_operator_surface.v1",
+ "outputDocument": "mimir.program_output.v1",
+ "sitePublisher": "Yggdrasil",
+ "obsRole": "compatibility-sink-only"
}
},
"authorities": {
"configurationCalibrationLaunchStatus": "Mimir",
+ "programComposition": "Mimir",
+ "sitePublication": "Yggdrasil publisher daemon",
"denseVisualFusion": "Fensalir",
"gpuMaterialAndBrushReconciliation": "Fensalir",
- "spoutPublication": "Fensalir",
- "hotAudioDsp": "Faust",
- "obsBroadcastControls": "OBS"
+ "programTextureOutput": "Fensalir",
+ "hotAudioDsp": "Faust"
},
"visualSensors": [
{ "id": "kiyo-primary", "role": "rgb-main", "required": true },
@@ -70,9 +79,9 @@
],
"outputs": {
"video": {
- "target": "Spout2",
- "senderName": "Mimir Point Cloud",
- "owner": "Fensalir"
+ "target": "MimirProgramTexture",
+ "senderName": "Mimir Program Texture",
+ "owner": "Mimir/Fensalir"
},
"audio": {
"owner": "Faust",
@@ -85,6 +94,20 @@
"co_streamer_loopback",
"spatial_bed"
]
+ },
+ "operator": {
+ "owner": "Mimir",
+ "surfaces": [
+ "cultmesh-command-surface"
+ ]
+ },
+ "site": {
+ "owner": "Yggdrasil publisher daemon",
+ "input": "mimir.program_output.v1",
+ "route": "cultmesh://yggdrasil/gamecult.site/mimir/program"
+ },
+ "compatibility": {
+ "obs": "optional-derived-sink"
}
},
"runtimePolicy": {
@@ -93,7 +116,8 @@
"native-driver-workers",
"native-reservoir",
"Fensalir",
- "Faust"
+ "Faust",
+ "Yggdrasil publisher daemon"
],
"neverInCompatibilityEdges": [
"dense-feature-extraction",
diff --git a/docs/architecture.md b/docs/architecture.md
index 607fcfa..fad3f52 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -2,11 +2,16 @@
## Objective
-Make one local Windows computer available as OBS sources on another computer, with sender-side NVENC video encoding and independently mixable audio sources.
+Make one local Windows computer available as compatibility media sources on
+another computer, with sender-side NVENC video encoding and independently
+mixable audio sources. The current program target is Mimir-owned composition;
+this bridge is not the stream authority.
## Current Mechanism
-The repo owns configuration and scripts. FFmpeg owns capture, encoding, and SRT transport. OBS owns scene composition.
+The repo owns configuration and scripts. FFmpeg owns capture, encoding, and SRT
+transport. Mimir owns scene composition; OBS may consume this bridge as a
+temporary compatibility sink.
Inputs become outputs like this:
@@ -21,8 +26,10 @@ Inputs become outputs like this:
## Invariants
- Video encoding happens on the sender.
-- OBS receives ordinary media-source URLs; it does not need a plugin for v1.
-- Audio remains separately mixable in OBS.
+- OBS can receive ordinary media-source URLs for compatibility; it does not need
+ a plugin for this bridge path.
+- Audio remains separately mixable at the compatibility edge, but the durable
+ control surface belongs to Mimir.
- Ports are stable and derived from one base port plus explicit offsets.
- Secrets, if used, stay in local config and are not committed.
diff --git a/docs/code-algorithm-map.md b/docs/code-algorithm-map.md
index 8ebd9be..8a7724c 100644
--- a/docs/code-algorithm-map.md
+++ b/docs/code-algorithm-map.md
@@ -15,8 +15,9 @@ Timestamp for this pass: 2026-05-23, starting at 23:24 Europe/London.
Mimir collects local and network audio/video samples into bounded rolling
runtime buffers, uses active/passive timing evidence to place audio streams on a
-canonical timeline, and leaves Fensalir/Faust/OBS with clear ownership over
-rendering, DSP, and broadcast composition.
+canonical timeline, and lets Mimir own program composition while Fensalir lowers
+pixels, Faust/native DSP moves audio, and
+Yggdrasil publishes the public feed.
## Current Mechanism
@@ -54,11 +55,15 @@ reflection taps.
- UI and telemetry read cached sync state; they do not run analysis.
- Native capture owns hardware calls. Runtime owns sample identity and buffer
placement.
-- Fensalir owns windowing, rendering, D3D12, UI, visual fusion, and program
- video output.
+- Mimir owns the program scene graph, subscription policy, preview/control
+ state, stats, and publication intent.
+- Fensalir owns windowing, rendering, D3D12, UI lowering, visual fusion, and
+ program video output.
- Faust/native DSP will own hot resampling, fractional delay, separation,
spatialization, and stems.
-- OBS owns broadcast composition.
+- The Yggdrasil-facing publisher daemon consumes Mimir program output without
+ owning composition.
+- OBS is a compatibility sink only.
## Diagram
@@ -83,6 +88,9 @@ flowchart TD
AsioSource --> Sources
Probes["native/probes/*"] --> FrameEvents["MimirFrameEventProcessStreamSource"]
FrameEvents --> Sources
+ Runtime --> Scene["Mimir program scene graph"]
+ Scene --> Publisher["Yggdrasil site publisher"]
+ Scene -. "compatibility" .-> Obs["OBS adapter"]
Reservoir["native/reservoir"] -. "future lower ABI" .-> Hub
```
@@ -96,6 +104,9 @@ flowchart TD
- `native/reservoir` is the lower native rolling-buffer ABI.
- `native/probes/*` are measurement and diagnostic tools.
- `scripts/*` are bridge utilities, not the final synchronized program path.
+- `docs/mimir-program-composition.md` is the authority map for Muninn capture
+ hosts, Mimir composition, Yggdrasil publication, and OBS
+ compatibility.
## Stage 0: Solution And App Host
diff --git a/docs/eve-program-output.md b/docs/eve-program-output.md
deleted file mode 100644
index 2e8db5b..0000000
--- a/docs/eve-program-output.md
+++ /dev/null
@@ -1,59 +0,0 @@
-# EVE Program Output
-
-## Authority Map
-
-- Owner: Fensalir owns the visible D3D12 program pixels.
-- Inputs: Fensalir reads the current `AquariumFrame`, render plan, GPU resources,
- and overlay state.
-- Outputs: a named shared D3D12 `B8G8R8A8_UNorm` texture containing the completed
- backbuffer for the current frame.
-- Derived state: Mimir publication profiles describe the stream contract; they
- do not render pixels. EVE receives decoded pixels and composites them.
-- Forbidden writers: WebKit, DOM layout, browser dashboards, OBS capture, and
- desktop/window capture do not own this path.
-- Shared paths: local display and EVE streaming both use the same completed
- Fensalir backbuffer copy.
-- Deletion line: diagnostic Sunshine/Moonlight or desktop-duplication probes may
- prove network/decode budget, but they are not production authority.
-
-## Current Mechanism
-
-Set these before launching the Mimir app:
-
-```powershell
-$env:FENSALIR_PROGRAM_OUTPUT_D3D12 = "1"
-$env:FENSALIR_PROGRAM_OUTPUT_NAME = "Global\MimirFensalirProgramTexture"
-dotnet run --project .\src\Mimir.App\Mimir.App.csproj
-```
-
-Fensalir creates a shareable D3D12 texture with that name and copies the finished
-backbuffer into it during the present pass. A hardware encoder process should
-open that named texture, encode H.264/HEVC with GPU hardware, and send frames to
-EVE's native receiver.
-
-`scripts/start-eve-program-output.ps1` starts the Mimir/Fensalir app with the
-shared-texture output enabled and verifies the `eve` SSH target before launch.
-
-## Receiver Contract
-
-EVE owns:
-
-- network receive;
-- hardware decode;
-- texture upload/import;
-- final fullscreen composite;
-- touch/Pencil/sensor uplink later.
-
-EVE does not own:
-
-- dashboard DOM;
-- Chromium/WebKit compatibility;
-- layout parity;
-- Mimir timing authority;
-- OBS composition authority.
-
-## Smoke
-
-```powershell
-dotnet run --project .\src\Mimir.BufferSmoke\Mimir.BufferSmoke.csproj -- --eve-program-output-contract-smoke
-```
diff --git a/docs/implementation-plan.md b/docs/implementation-plan.md
index ffb19ec..7e9cb6c 100644
--- a/docs/implementation-plan.md
+++ b/docs/implementation-plan.md
@@ -10,15 +10,15 @@ For the source-level ownership map, read [[code-algorithm-map|Code Algorithm Map
- `src/Mimir.App` hosts Fensalir for windowing, rendering, and the D3D12
bridge.
- `src/Mimir.Runtime` owns stream descriptors, source polling, direct push
- ingest, one rolling buffer per configured audio/video stream, and the
+ ingest, one rolling buffer per configured audio/video/tracking stream, and the
synchronization hub Fensalir can inspect.
- The default five-second window is an intentional latency/memory trade: use it
- to line up streams and extract the volumetric audio/video field before OBS
- sees program output.
+ to line up streams and extract the volumetric audio/video field before Mimir
+ emits program output.
- `native/reservoir` owns the lower native rolling-buffer invariant for
Fensalir/Faust integration.
-- PowerShell/FFmpeg/SRT remains a bridge utility for LAN OBS feeds. It is not
- the synchronized program authority.
+- PowerShell/FFmpeg/SRT remains a bridge utility for LAN compatibility feeds.
+ It is not the synchronized program or publication authority.
The old script stack is gone. Do not add a compatibility edge unless it protects
a named invariant that the native runtime cannot protect yet.
@@ -28,16 +28,169 @@ a named invariant that the native runtime cannot protect yet.
- Mimir public identity, branding, and Face memory.
- `Mimir.slnx` with `src/Mimir.App` and `src/Mimir.Runtime`.
- Fensalir host bootstrapping from `Mimir.App`.
-- [[eve-program-output|EVE Program Output]] now owns the first native EVE
- streaming contract: Fensalir can publish the completed D3D12 backbuffer into a
- named shared texture when `FENSALIR_PROGRAM_OUTPUT_D3D12=1`, and Mimir records
- the EVE-facing publication profile as a native D3D12 stream rather than a
- WebKit/dashboard path.
- `MimirSynchronizationHub`, `MimirRollingStreamBuffer`, stream descriptors, and
`IMimirStreamSource`.
- Configurable five-second default rolling buffers for local and network audio
- and video streams.
+ and video/tracking streams.
- `MimirNativeIngestStreamSource` for direct push ingest into runtime buffers.
+- `MimirStreamKind.Tracking` plus `MimirTrackingObservation` carry PS Move
+ evidence samples as typed `mimir.move_tracking_observation.v1` documents.
+ Starfire and Nightwing both have USB-attached Moves. Muninn daemons publish
+ source-local glowing-orb marker candidates and controller/IMU/button state;
+ Odin owns discovery and schema projection, not pose fusion.
+- `native/reservoir` exposes a `move_evidence` typed view and
+ `LocalcastMoveEvidenceBufferDescriptor` so Muninn Move witnesses can be
+ admitted as compute-upload buffers before Mimir's fusion pass and Fensalir's
+ GPU consumers touch them.
+- `MimirNativeReservoirRuntime` is the managed owner for that native runtime
+ boundary. It pins Move evidence sample/descriptor batches so native reservoir
+ handles remain valid while Mimir prepares fusion input.
+- `MimirMuninnMoveEvidenceAdapter` mirrors Muninn's marker-candidate and
+ controller-state document schemas and normalizes them into native Move
+ evidence samples. It does not associate markers to controllers or synthesize
+ pose.
+- The realtime Move witness path uses CultMesh streaming, not CultCache polling:
+ Muninn publishes shared-memory stream frames, Mimir consumes the latest read
+ lease, normalizes the frame, and admits it to native `move_evidence`.
+- `MimirMuninnMoveEvidenceAdapter.TryAdmitLatestCultMeshFrame` now exposes a
+ typed admission receipt for that latest read: decoded frame id, producer,
+ publish/read timestamps, sample counts by kind, source/arrival ranges, native
+ reservoir handle, and reservoir edge/window. `Mimir.BufferSmoke
+ --muninn-move-cultmesh-stream-smoke` asserts the receipt so the named proof
+ spine can be traced instead of inferred.
+- `mimir.move_controller_pose.v1` is Mimir's resolved wand pose contract.
+ Mimir owns calibration, association, triangulation, IMU fusion, prediction,
+ confidence, and latency accounting for those poses before Fensalir consumes
+ them as interactive controller input.
+- `MimirMoveFusion` is the first calibrated fusion owner for Move tracking:
+ it consumes native Muninn Move evidence samples, requires calibrated camera
+ witnesses before publishing a pose, associates marker candidates with the
+ USB controller state, triangulates multi-camera orb position, demotes
+ single-ray fallbacks through confidence, carries buttons/trigger/gyro into
+ `mimir.move_controller_pose.v1`, and marks orientation as
+ `orientation:imu-unresolved` until the IMU/prediction pass earns full 6DoF.
+- `MimirMovePoseStream` frames resolved Move poses as
+ `mimir.move_controller_pose_stream_frame.v1` over CultMesh shared-memory
+ bytes streams so Fensalir and other consumers have a realtime stream contract
+ for Mimir-fused controller input.
+- `MimirMoveProofSurface` is the observer-only dev proof surface for the named
+ Move chain. It combines a Muninn evidence admission receipt with a Mimir pose
+ stream frame and a Fensalir presentation/probe timestamp into
+ `mimir.move_proof_surface.v1`, then lowers the verdict to a Fensalir
+ `AquariumSplineFrame` probe. `Mimir.BufferSmoke --move-proof-surface-smoke`
+ proves the explicit
+ `muninn:nightwing:move-evidence:79 -> mimir:starfire:move-evidence:79 ->
+ mimir:starfire:move-pose:79 -> fensalir:starfire:presented-frame:79` chain,
+ and rejects single-ray fallback as not full pose.
+- `MimirMoveProofPipeline` is the shared commit primitive for the live Move
+ proof path. It starts from a Muninn CultMesh shared-memory evidence frame,
+ admits samples to the native `move_evidence` reservoir, runs Mimir-owned
+ fusion, creates the Mimir pose stream frame, creates the proof surface, and
+ returns the Fensalir spline probe in one pass. `Mimir.BufferSmoke
+ --move-proof-pipeline-smoke` proves that shape against the native reservoir
+ debug DLL; live hardware and presented-frame capture still need to call this
+ primitive instead of hand-assembling a parallel path.
+- `MimirRuntime` is now the live Fensalir attachment point for proof surfaces.
+ `PublishMoveProofSurface` accepts the Mimir-owned
+ `mimir.move_proof_surface.v1` document, `CreateFrame` composes its
+ observer-only `move-proof-*` splines into `AquariumFrame.Scene.SplineFrame`,
+ and `Mimir.BufferSmoke --move-proof-runtime-frame-smoke` proves the frame is
+ empty before publish, contains the proof splines after publish, and clears
+ without leaving a repair loop behind.
+- `MimirMoveProofRuntimeDriver` is the bounded runtime bridge from a Muninn
+ CultMesh shared-memory evidence ring to `MimirRuntime.PublishMoveProofSurface`.
+ It derives the Mimir evidence, Mimir pose, and Fensalir frame ids from the
+ actual Muninn frame suffix, calls the shared proof pipeline, and suppresses
+ duplicate frame admission. `Mimir.BufferSmoke --move-proof-runtime-driver-smoke`
+ proves `MimirRuntime.Update` can pull one ring frame into the same-sequence
+ proof chain and visible spline frame.
+- `MimirMoveProofRuntimeConfiguration` is the typed runtime configuration and
+ validation contract for future real Move proof subscriptions. An enabled
+ source names the Muninn evidence stream, native reservoir path, Mimir evidence
+ and pose frame prefixes, Fensalir presented-frame prefix, fusion authority,
+ consumer contract, and at least two calibrated camera witnesses. It creates
+ `MimirMoveProofRuntimeDriver` only after the supplied ring stream id matches
+ the configured evidence stream.
+- `MimirRuntime` now owns configured Move proof activation instead of leaving
+ config as a readout. At scene-ready it asks an
+ `IMimirMoveProofEvidenceRingProvider` for the configured Muninn evidence ring,
+ opens the configured native reservoir, creates the driver, retains the
+ resources, and exposes an activation status. The default provider fails
+ explicitly because the current C# CultMesh ring is still in-process only;
+ `Mimir.BufferSmoke --move-proof-runtime-activation-smoke` injects an
+ in-process provider and proves config activation can produce the same named
+ proof chain. Real Nightwing/Starfire cross-process ring opening remains
+ pending.
+- `mimir.move_proof_runtime_activation.v1` is the typed readiness/proof-spine
+ surface for configured Move proof sources. It reports the configured stream,
+ provider kind, active/driver state, native reservoir path, calibrated camera
+ ids, latest same-stream proof ids, latest verdict, and the unavailable
+ default-provider diagnostic. `Mimir.BufferSmoke
+ --move-proof-runtime-activation-surface-smoke` proves both an active injected
+ ring document and the inactive default runtime document without claiming live
+ field evidence.
+- `mimir.move_proof_evidence_frame_snapshot.v1` is the one-copy fallback for
+ captured Muninn evidence frames while C# CultMesh has no cross-process ring
+ opener. A configured `EvidenceSnapshotPath` is loaded by
+ `MimirConfiguredMoveProofEvidenceRingProvider`, metadata-checked against the
+ encoded payload, copied into an owned in-process ring, and then consumed by
+ the same runtime driver/pipeline. `Mimir.BufferSmoke
+ --move-proof-runtime-snapshot-smoke` proves the named proof spine through
+ that file boundary. It is field capture/replay support, not the final live
+ shared-memory proof.
+- Odin/Muninn now has the matching producer-side latest snapshot writer:
+ `--move-evidence-snapshot ` writes the Mimir-compatible
+ `mimir.move_proof_evidence_frame_snapshot.v1` artifact from the same Muninn
+ Move evidence publisher after the frame payload is accepted into the stream
+ ring. The Odin unit
+ `move_evidence_snapshot_writes_mimir_compatible_frame_artifact` decodes the
+ snapshot tuple and embedded Muninn frame payload. This lets Nightwing produce
+ a field artifact for Mimir replay while the final live ring/page transport is
+ still pending.
+- `MimirMoveProofDevSurface` is a dev-only bootstrap gated by
+ `MIMIR_MOVE_PROOF_DEV_SURFACE`. It uses the same runtime proof attachment so
+ `Mimir.BufferSmoke --move-proof-presented-frame-smoke` can run `Mimir.App`
+ headless, capture the Fensalir-presented PNG, and pixel-check the named
+ proof probe. This verifies the renderer/probe layer; it is not live
+ Nightwing hardware evidence.
+- `MimirMoveCalibrationProtocol` publishes the typed calibration preflight for
+ Starfire/Nightwing Moves: required Muninn evidence streams, optional
+ Quest headset/controller pose witnesses, stillness/sweep/validation phases, and
+ the four derived calibration outputs Mimir must produce before IMU
+ orientation can become authority.
+- Muninn publishes Quest access as `muninn.quest_access.v1` for the USB-attached
+ Quest. Mimir consumes that access surface and later `muninn.quest_pose_frame.v1`
+ samples as optional calibration evidence; Mimir does not own ADB/Quest access.
+- The old `scripts/start-nightwing-move-tracking.ps1` bring-up path is
+ archived. It no longer starts a receiver/recorder pair or stages a socket
+ witness worker. Nightwing Eye/Move evidence must enter through Muninn/Mimir
+ typed CultMesh stream frames and Odin-discovered CultMesh documents. The blob
+ stream remains optical witness evidence for later pose fusion, not the final
+ 6DoF pose owner.
+- Structured PS Move light pulses are Muninn output commands. Mimir publishes
+ `muninn.move_light_command.v1` over CultNet/CultMesh to the Muninn daemon on
+ the host that owns the USB-attached Move. Muninn writes PS Move HID report
+ `0x06` locally and updates command state/receipt.
+ `scripts/start-starfire-move-light.ps1` and Nightwing direct HID writes are
+ now smoke/bootstrap paths for hardware proof when Muninn is not yet running
+ on that host.
+- Odin's Muninn organ owns Move optical candidate extraction in
+ `E:\Projects\Odin\crates\muninn-move-tracker` and publishes/feeds
+ `muninn.move_marker_candidate.v1` records. Mimir consumes those candidate
+ streams; it does not own raw optical extraction. Final pose, stereo
+ triangulation, calibration, association, IMU fusion, and prediction belong to
+ Mimir.
+- Muninn's Move evidence stream frame no longer hardcodes an empty optical
+ candidate slice. The daemon frame uses the canonical
+ `muninn.move_marker_candidate.v1` record shape, and the Odin unit
+ `move_marker_candidates_publish_in_mimir_compatible_cultmesh_frame` proves a
+ bright Y8 frame can pass through `muninn-move-tracker` and serialize as a
+ non-empty marker candidate beside controller evidence. The daemon now has a
+ source-local Y8 extraction/publish seam and a first `serve` camera producer:
+ `--move-marker-camera =` polls a Unix V4L2 YUYV frame,
+ converts it to compact Y8, and feeds that same seam. Unit tests prove the
+ configured camera tick publishes marker evidence through the shared frame
+ contract; Nightwing hardware/live V4L2 proof is still pending.
- `MimirProcessStreamSource` for bridge/network command edges.
- `MimirFrameEventProcessStreamSource` for temporary JSON-line frame metadata
from native probes into the same rolling buffers Fensalir inspects. One probe
@@ -189,7 +342,7 @@ a named invariant that the native runtime cannot protect yet.
profiles, language/emission profiles, path-learning sessions, benchmark
panels, audio actuator strategies, native capture profiles, camera ingest
strategies, reservoir strategies, audio/visual field profiles, compute
- offload profiles, OBS publication profiles, CultMesh contracts, Fensalir
+ offload profiles, program publication profiles, CultMesh contracts, Fensalir
lowering, and assembly plans. Use
`Mimir.BufferSmoke --perfect-machine-profile-smoke` to prove the catalog
assembles, `--perfect-machine-contract-smoke` to write a CultCache contract
@@ -197,7 +350,10 @@ a named invariant that the native runtime cannot protect yet.
tooling/UI/remote witness use, and `--perfect-machine-lowering-benchmark` to
measure the Mimir-to-Fensalir lowering path. The current six-camera/two-audio
synthetic lowering benchmark runs at roughly 2.5 us per iteration with about
- 2.3 KB allocated per iteration.
+ 2.3 KB allocated per iteration. `--move-tracking-contract-smoke` proves
+ Starfire-local and Nightwing-remote Move tracking observations enter
+ Mimir's rolling buffers with Muninn producer identity and Odin discovery
+ provenance.
- `MimirVideoFrameDescriptor` for dimensions, pixel format, stride, device
timestamp, and native/GPU handle metadata.
- `IMimirVideoCaptureDriver` and `MimirVideoCaptureDriverSource` as the live
@@ -207,14 +363,61 @@ a named invariant that the native runtime cannot protect yet.
source-id hashing, producer helpers, and typed audio/render payload
descriptors.
- Windows bridge scripts for sender discovery/start/stop and simple OBS Media
- Source ingest.
+ Source compatibility ingest.
+- `src/Mimir.Runtime/Synchronization` now contains the first typed program
+ composition contracts: `mimir.program_scene.v1` and `mimir.program_output.v1`.
+ These make Mimir's scene graph and Yggdrasil/site publication route
+ manifestable through CultMesh.
+- `docs/mimir-program-composition.md` is the current authority map for the new
+ stream-program architecture: Muninn observes on Starfire/Nightwing/Raven-class
+ hosts, Mimir consumes selected streams and composes the program, Yggdrasil publishes to the site, and OBS is
+ compatibility-only.
+- `src/Mimir.CultMeshMedia` is the first explicit CultMesh media/body bridge:
+ `relay` hosts the document relay on CultNet RUDP `3075`, `send` reads an
+ MPEG-TS byte stream from stdin and publishes rolling
+ `mimir.cultmesh_media_frame` documents, and `recv` subscribes to those
+ documents and writes ordered MPEG-TS bytes to a Starfire-local UDP endpoint
+ for compatibility sinks. Its C# implementation now uses explicit CultLib
+ RUDP client/session helpers behind
+ `cultmesh://asgard.yggdrasil.mimir/media/raven-primary-av`; the older
+ `CultMesh.StartNodeAsync`/`CultMesh.ConnectClient` entrypoints no longer own
+ the media transport lane, and sender/receiver launchers no longer accept raw
+ relay host/port configuration.
+- `scripts/start-raven-cultmesh-av-sender.ps1`,
+ `scripts/start-yggdrasil-cultmesh-media-relay.ps1`, and
+ `scripts/start-starfire-cultmesh-av-receiver.ps1` are the CultMesh bridge
+ operators. `start-raven-cultmesh-av-sender.ps1` is the Mimir-owned
+ `Mimir.CultMeshMedia` bootstrap/body-bridge lane; it is not the real Muninn
+ OBS/SRT feed owner. The actual Raven OBS/plugin feed now routes through
+ Odin's Muninn actuator `E:\Projects\Odin\scripts\activate-muninn-raven-av-srt.ps1`,
+ with `scripts/start-raven-muninn-obs-feed.ps1` in this repo as a thin local
+ wrapper around the real `GameCult-Muninn-Activate` task and
+ `muninn.exe activate` body. `-LocalBootstrap` on the CultMesh sender remains
+ a direct local bootstrap edge only for the separate body-bridge lane. Raven
+ capture still defaults to FFmpeg desktop frames plus Mimir's WASAPI loopback
+ capture muxed as H.264/AAC MPEG-TS; DirectShow audio remains an explicit
+ fallback.
- Documentation for OBS receiver setup, native rebuild boundaries, the viable
stream app, and the Mimir Face.
## Temporary
- Audio and video may still traverse separate OBS/SRT endpoints during bridge
- testing so OBS can preserve independent controls.
+ testing, but Mimir owns independent controls.
+- The CultMesh media bridge still lowers to local UDP for compatibility sinks
+ because OBS is not a CultMesh consumer. Network transit between Raven,
+ Yggdrasil, and Starfire is the Odin-discovered CultMesh/CultNet path;
+ OBS-local UDP is an egress adapter only.
+- CultLib RUDP is the default typed CultNet/CultMesh document transport for
+ daemon truth. Idunn RUDP health publication is the current freshness witness;
+ provider advertisements, command boundaries, transport profiles, and retained
+ daemon state should follow the same path. Odin still owns Verse/service
+ discovery, Idunn owns keepalive decisions, and Mimir-owned dashboard/reference
+ surfaces only report their own observed state. Product/debug render surfaces
+ stay lowerings or compatibility evidence.
+- `Mimir.CultMeshMedia` has completed its explicit RUDP transport cut. Do not
+ add another private bridge, status shim, or renderer-derived service truth
+ while the RUDP document lane exists.
- Process-backed stream sources are only acceptable for network bridge feeds or
diagnostics. Six-camera local ingest belongs behind direct capture drivers.
- Frame-event process sources are diagnostic only. They prove source cadence and
@@ -224,34 +427,111 @@ a named invariant that the native runtime cannot protect yet.
## Next
+Before expanding Muninn media or Sleipnir input transport, apply the traffic
+contracts distilled in
+`docs/research/moonlight-muninn-sleipnir-study-2026-07-16.md`: latest-state,
+ordered-edge, video-deadline, audio-playout, and reliable-control. Preserve the
+existing Muninn packetizer/feedback foundations. The first implementation pass
+should make input supersession and edge preservation structural, replace the
+two-second media default with consumer-derived field budgets, and prove the
+result under controlled loss/jitter/reorder before changing carriers.
+
+The first cut landed in Odin on 2026-07-16: experimental CultLib snapshot
+`8965f3c0`, epoch/sequence/edge-ack HID delivery, 100 ms default LAN media
+deadline, CultNet `realtime` A/V delivery, bounded queues, expiring/late-aware
+repair, and decode-chain-owned keyframe pressure. The production video path now
+uses typed V4 Cauchy GF(256) FEC in independent 8-data/4-parity blocks. Each
+block schedules data and parity as separate lanes, including fixed protection
+for a short tail block. Canonical video and parity use CultNet's unreliable
+`realtime` lane; selective repair and IDR recovery remain deadline-bound.
+Canonical AAC ADTS audio and its fixed 864-byte parity shards also use
+`realtime`; 4+2 FEC, 120 ms reorder, and
+concealment own continuity without an ACK/retransmit window. Sender access units enter the
+handoff queue independently, and CultMesh catalog publication runs outside the
+realtime media loop. Repair material becomes available when a video frame enters
+the queue, and the receiver may issue bounded repair requests at 64/96/128 ms.
+The remaining work is duplicate/reconnect acceptance, longer iid runs, and the
+long-duration mixed soak.
+
+The socket harness and bounded PCM loss recovery have now landed. Odin's
+`cultnet-impair` proxy supplies deterministic seeded loss/burst/reorder/
+duplicate/jitter/stall profiles around real CultNet endpoints. Muninn emits a
+typed fixed 4+2 audio parity block after each four constant-size PCM packets;
+the production Mimir OBS receiver reconstructs up to two missing packets inside
+the 40 ms reorder budget and feeds them through the existing playout owner.
+Both sides use the experimental CultLib/CultMesh snapshot lineage used by Eve,
+Aetheria, and VoidBot; no stable-branch transport shim was introduced.
+
+Receiver pressure now closes into the long-lived NVENC encoder as bounded AIMD
+bitrate control. Startup begins at half of the configured encoder ceiling
+because fixed block parity can roughly double wire rate, and additive recovery
+is capped at half of that configured ceiling. Late/decode/repair/queue
+pressure backs off by 15 percent; clean recovery adds one-fiftieth of the safe
+cap only after ten stable seconds. The live encoder proof reconfigured 12 Mbps to 6 Mbps without a
+restart and emitted the required transition IDR. The exact remaining completion
+gate is tracked in
+`docs/research/moonlight-reliability-acceptance-2026-07-16.md`; no cross-host
+claim is allowed until its Raven-to-Starfire field matrix passes.
+
+The production receiver follow-up fixed two contract contradictions: early
+video repair no longer declares the frame late (which had made Odin discard all
+repair requests), and the OBS audio decoder now consumes Muninn's actual float
+PCM contract rather than treating it as AAC. Audio reorder is bounded to 40 ms
+with short silence concealment. The next audio cut is Opus with explicit
+FEC/PLC for variable-rate compressed audio; PCM now has bounded 4+2 erasure
+recovery plus concealment. The controllable video encoder owner now exists and
+forces the next NVENC frame to IDR without restarting the video session. A live
+D3D11 desktop proof verified the command-generated IDR. The Raven bundle and
+OBS receiver are now deployed over the direct LAN route. Raven activation is an
+interactive-token scheduled task whose PowerShell action uses
+`-WindowStyle Hidden`; no WireGuard path or foreground terminal is part of the
+runtime. The active field cut uses D3D11/NVENC video, WASAPI loopback audio,
+typed CultNet media, and the experimental CultLib lineage. The named direct and
+proxy impairment profiles and long soak remain the completion gate.
+
1. Replace the frame-event diagnostic bridge with concrete direct capture
drivers for Leap stereo IR first, then the
other cameras.
-2. Feed those drivers into `MimirVideoCaptureDriverSource` and prove sustained
+2. Cut the remaining dashboard service truth paths from older renderer-derived
+ assumptions to explicit CultNet RUDP records,
+ preferably authorized-peer dialing where the peer catalog exists. Preserve
+ OBS-local UDP as egress only for compatibility sinks.
+3. Feed those drivers into `MimirVideoCaptureDriverSource` and prove sustained
frame cadence in the rolling buffers.
-3. Promote the packet-song physical calibration receipt into the runtime
+4. Promote the packet-song physical calibration receipt into the runtime
receiver. The live decoder should keep its ear open for self-identifying
song contours, extract intra-call time/frequency anchors from log-mel parts,
apply learned per-output/mic path weighting, and feed a global
delay/clock/path hypothesis. Keep chirp-bin calibration artifacts as
reference data, not the runtime target.
-4. Add the synchronization actuator: drive a variable-rate resampler and
+5. Add the synchronization actuator: drive a variable-rate resampler and
fractional delay line per non-reference stream from the smoothed
`MimirAudioSynchronizationState`. First, prove the bioacoustic motif decoder
through real loopback and microphone paths so every correctly heard word
becomes a deterministic timeline anchor before the actuator moves samples.
-5. Prove the bioacoustic hybrid fallback through real loopback and microphones
+6. Prove the bioacoustic hybrid fallback through real loopback and microphones
with probe durations long enough to keep loopback and mic windows live.
-6. Bind Fensalir UI to the synchronization hub so buffer depth, stream cadence,
+7. Wire real Move proof production into `MimirMoveProofRuntimeDriver`: attach
+ real Muninn Nightwing and Starfire evidence rings, require calibrated optical
+ witnesses, publish `mimir:starfire:move-pose:` through the shared
+ runtime path, and replace the dev-gated presented-frame smoke with a
+ same-sequence capture from real field evidence.
+8. Bind Fensalir UI to the synchronization hub so buffer depth, stream cadence,
source timestamps, and output settings are visible and adjustable.
-7. Lower `AquariumBufferFieldFrame` spline tube fields into Fensalir compute:
+9. Implement the Mimir program scene graph as the shared commit primitive for
+ source subscription, transforms, crop, chroma key, visibility, layer order,
+ preview, and output publication. Import the current OBS scene only as an
+ initial mirror; the operator surface returns on Thing 2.
+10. Add the Yggdrasil-facing site publisher daemon that consumes the Mimir
+ program output and publishes it without owning a second composition.
+11. Lower `AquariumBufferFieldFrame` spline tube fields into Fensalir compute:
sample buffer-domain paths stochastically by visual contribution, emit SDF
splat probes, write them into the spatiotemporal splat reservoir, and sample
that reservoir in the temporally antialiased scene pass. The direct spline
preview must stay a witness until this path owns rendering.
-8. Move GPU feature extraction, fusion, material fitting, render budgeting, and
+12. Move GPU feature extraction, fusion, material fitting, render budgeting, and
Spout2 publication into Fensalir.
-9. Move mic alignment, room suppression, voice separation, spatialization, and
+13. Move mic alignment, room suppression, voice separation, spatialization, and
stem generation into Faust/native DSP.
-10. Keep the OBS bridge witness ledger as evidence before expanding receiver
+14. Keep the OBS bridge witness ledger as evidence before expanding receiver
machinery.
diff --git a/docs/mimir-face.md b/docs/mimir-face.md
index 3c9ea4d..e50cab6 100644
--- a/docs/mimir-face.md
+++ b/docs/mimir-face.md
@@ -48,9 +48,10 @@ Mimir exists to turn cameras, microphones, speakers, timing signals, and
distributed capture surfaces into one realtime coherent volumetric field. The
field is the product. The bridge scripts are scaffolding and probes.
-The live target is one native five-second rolling reservoir feeding Fensalir GPU
-fusion and Faust/native DSP, then OBS. Retention has one owner. Typed views are
-indexes, not private histories.
+The live target is one native five-second rolling reservoir feeding Mimir-owned
+program composition, Fensalir GPU fusion, Faust/native DSP, operator
+surfaces, and a Yggdrasil site publisher. OBS is compatibility only. Retention
+has one owner. Typed views are indexes, not private histories.
## Operating Doctrine
@@ -63,11 +64,14 @@ the synchronized program path is being rebuilt around `LocalcastRuntime`,
Invariants:
-- OBS owns final broadcast composition.
-- Fensalir owns dense visual fusion, material/brush/splat rendering, and Spout2
- publication.
+- Mimir owns final program composition, source subscription, preview/control
+ state, stats, and publication intent.
+- Fensalir owns dense visual fusion, material/brush/splat rendering, and local
+ program texture output.
- Faust/native DSP owns hot audio alignment, room suppression, voice separation,
stems, and spatial bed generation.
+- The Yggdrasil publisher daemon ships the Mimir program to the site without
+ owning composition.
- FFmpeg owns simple capture, encode, and transport in V1 bridge mode.
- Mimir/this repo owns configuration, calibration, launch/status, typed
contracts, memory, and operational repeatability.
diff --git a/docs/mimir-program-composition.md b/docs/mimir-program-composition.md
new file mode 100644
index 0000000..02561a1
--- /dev/null
+++ b/docs/mimir-program-composition.md
@@ -0,0 +1,103 @@
+# Mimir Program Composition
+
+## Objective
+
+Mimir owns the live stream program: selected capture feeds, calibration feeds,
+scene composition, preview/control surfaces, stats, and publication. OBS is a
+temporary compatibility sink only.
+
+## Authority Map
+
+- Owner: Mimir owns the program scene graph and subscription policy.
+- Inputs: Muninn-published local and remote monitor, window, camera, audio,
+ loopback, and health streams.
+- Outputs: a Mimir program video surface, aligned audio stems or final bus,
+ and a Yggdrasil-facing site publication feed.
+- Derived state: OBS receives a compatibility feed when useful; it does not own
+ scene truth, broadcast truth, preview truth, stats, source selection, or
+ publication.
+- Forbidden writers: OBS scenes, browser dashboards, site daemons, and Muninn
+ capture hosts must not independently decide the program composition.
+- Shared paths: direct operator edits, remote commands, preset loads,
+ calibration-driven source selection, and recovery scripts must commit through
+ the same Mimir scene graph command surface.
+- Deletion line: old OBS-owned composition docs and config names are demoted to
+ diagnostic bridge language. Any OBS adapter delegates to the Mimir program
+ scene instead of carrying its own opinion.
+
+## Machine Shape
+
+```mermaid
+flowchart TD
+ A["Muninn on Starfire"] --> D["CultMesh stream catalog + media bodies"]
+ B["Muninn on Nightwing"] --> D
+ C["Muninn on Raven"] --> D
+ D --> E["Mimir subscription policy"]
+ E --> F["Mimir rolling buffers"]
+ F --> G["Mimir program scene graph"]
+ G --> H["Fensalir compositor"]
+ F --> I["Faust/native DSP"]
+ H --> J["program video surface"]
+ I --> K["program audio stems/bus"]
+ J --> M["Yggdrasil site publisher daemon"]
+ K --> M
+ M --> N["GameCult site"]
+ J -. "compatibility" .-> O["OBS adapter"]
+ K -. "compatibility" .-> O
+```
+
+## Composition Contract
+
+The scene graph carries layers with:
+
+- source reference, such as `muninn:starfire:monitor:primary` or
+ `muninn:starfire:window:discord`;
+- source kind, such as monitor, window, camera, network monitor, or generated
+ Mimir visual field;
+- visibility, layer order, transform, crop, and optional chroma key;
+- enough timing identity for Mimir to align media inside the rolling window.
+
+The current OBS scene can be mirrored as an initial Mimir scene:
+
+- Starfire monitor capture, cropped at the bottom when needed.
+- Discord window capture, cropped to chat and keyed with chroma filters.
+- Raven primary monitor plus Realtek loopback as a subscribed remote Muninn
+ stream.
+- Mimir/Fensalir program texture as a generated layer when the field renderer is
+ active.
+
+After import, OBS is not the editor of record. It may display the Mimir program
+feed, but operator edits happen through Mimir command surfaces.
+
+## Site Publisher
+
+The Yggdrasil-facing publisher consumes Mimir program output and publishes it to
+the GameCult site. It owns transport, encoding/session state, reconnect policy,
+and public edge health. It does not own composition.
+
+The publisher's input contract is the same Mimir program output used by local
+preview and OBS compatibility adapters, so the public stream cannot silently
+diverge from the operator preview.
+
+## Runtime Contracts
+
+The first typed contracts live in `src/Mimir.Runtime/Synchronization`:
+
+- `mimir.program_scene.v1`
+- `mimir.program_output.v1`
+- `mimir.eve_operator_surface.v1`
+
+The proof path is:
+
+```powershell
+dotnet run --project .\src\Mimir.BufferSmoke\Mimir.BufferSmoke.csproj -- --perfect-machine-contract-smoke
+dotnet run --project .\src\Mimir.BufferSmoke\Mimir.BufferSmoke.csproj -- --perfect-machine-profile-smoke
+```
+
+Import the current OBS scene as Mimir-owned typed state:
+
+```powershell
+dotnet run --project .\src\Mimir.BufferSmoke\Mimir.BufferSmoke.csproj -- --import-obs-program-scene --output state\mimir-program-composition.cc
+```
+
+The live imported state file is `state/mimir-program-composition.cc`.
diff --git a/docs/move-calibration-protocol.md b/docs/move-calibration-protocol.md
new file mode 100644
index 0000000..6f92b88
--- /dev/null
+++ b/docs/move-calibration-protocol.md
@@ -0,0 +1,107 @@
+# Move Calibration Protocol
+
+Mimir owns Move calibration and sensor fusion. Muninn owns source-local facts:
+camera marker candidates, USB controller IMU/button state, and local Move light
+commands. Odin discovers and projects the surfaces. Fensalir consumes Mimir's
+resolved poses after Mimir has earned them.
+
+## Current Stream Shape
+
+- Starfire should run Muninn as a local capture/control daemon. Its Move stream
+ address is `muninn:starfire:move-evidence`.
+- Nightwing runs Muninn for its USB Move and local camera witnesses. Its Move
+ stream address is `muninn:nightwing:move-evidence`.
+- Mimir on Starfire publishes resolved pose frames at
+ `mimir:starfire:move-controller-poses`.
+- Quest can participate as an external tracked VR reference over USB:
+ `quest:usb:headset-pose`, `quest:usb:left-controller-pose`, and
+ `quest:usb:right-controller-pose`.
+- Starfire ASIO/Scarlett loopback can provide cue timing, but it is not a Move
+ pose authority.
+
+The Move hot path is CultMesh stream frames. On the same host, those frames use
+shared-memory bytes rings. Across hosts, the remote peer still crosses the
+Verse/network boundary. The current Raven program media bridge is not same-host
+zero-copy into Mimir/Fensalir; it sends CultNet/CultMesh media documents and
+lowers to local UDP for OBS compatibility.
+
+## Required Capture Phases
+
+1. `preflight-streams`: hold both Moves still and visible; prove stream freshness,
+ clock skew, and controller ids.
+2. `dark-stillness`: lights off, both Moves still; estimate gyro bias, gravity,
+ and magnetometer baseline.
+3. `lit-stillness`: light one Move at a time, then both; map light commands to
+ controllers and measure centroid stability.
+4. `axis-sweeps`: slow pitch/yaw/roll sweeps; fit gyro scale, accelerometer
+ frame, and optical angular consistency.
+5. `figure-eight`: broad figure-eight motion; fit magnetometer hard/soft iron
+ and cross-axis coupling.
+6. `quest-reference`: optional Quest headset/controller reference with the Move
+ between the two Quest controllers in front of the headset.
+7. `validation-pass`: held-out free motion; promote only if triangulated optical
+ residuals and IMU prediction residuals pass.
+
+## Quest Access Preflight
+
+The Quest must be authorized for USB debugging before Starfire Muninn can query
+it. Muninn owns the access surface:
+
+```powershell
+muninn serve --store C:\Meta\Odin\state\muninn.telemetry.cc --host starfire --quest-adb --quest-serial 1WMHHB68PG1515
+```
+
+When the Quest is authorized, Muninn publishes `muninn.quest_access.v1` and
+advertises `muninn::quest-input`, `muninn::quest-poses`, and
+`muninn::quest-warped-video-input`. Mimir consumes the access/pose
+surfaces as calibration evidence; it does not own Quest USB access.
+
+```powershell
+muninn quest-access-status --store C:\Meta\Odin\state\muninn.telemetry.cc
+```
+
+ADB authorization proves USB access, device identity, and basic headset state
+only. It does not expose Quest headset or controller poses; those still require
+a Quest/OpenXR witness bridge that publishes pose samples through Muninn. The
+current calibration protocol can still run without Quest, but Quest poses are
+the preferred external reference when the headset and both controllers are
+sitting around the Move.
+
+## Data Products
+
+- `mimir.move_fusion_rig_calibration.v1`: camera intrinsics/extrinsics, clock
+ association policy, and gyro unit scale.
+- `mimir.move_imu_calibration.v1`: per-controller gyro bias/scale,
+ accelerometer gravity frame, magnetometer correction, and noise estimates.
+- `mimir.move_controller_identity_map.v1`: stable association between Muninn
+ USB ids, light receipts, and optical orb identities.
+- `mimir.move_calibration_receipt.v1`: sample counts, residuals, rejected
+ phases, and promotion decision.
+
+## Optical Sphere Fit Authority
+
+`MimirMoveSphereCalibrationFitter` owns the bounded two-Eye optical fit after a
+collection session reaches `Fitting`. Eye 0 defines the tracking-space gauge.
+The fitter jointly estimates both focal lengths and Eye 1's rigid pose from
+same-frame, same-Move correspondences, using a deterministic 80/20 training and
+held-out partition. A robust training consensus may reject contaminated marker
+pairs, but promotion also requires at least 65 percent of the untouched
+held-out pairs to agree and must pass the session's median and P95 reprojection
+limits.
+
+The physical orb radius is an explicit input; it is never guessed by the
+solver. PSMoveAPI's reported blob radius is a color-segmentation result, not a
+calibrated sphere silhouette. It may seed or weight depth only after the
+per-camera radius response has been validated. A fresh PSMoveAPI position age
+is also not optical confidence and must not be interpreted as one.
+
+## Preflight Command
+
+```powershell
+dotnet run --project .\src\Mimir.BufferSmoke\Mimir.BufferSmoke.csproj -- --move-calibration-protocol-smoke --output artifacts\move-calibration\protocol.cc
+```
+
+That writes the typed protocol as `mimir.move_calibration_protocol.v1` into a
+CultCache receipt. The real hardware runner should consume this document plus
+Muninn's `muninn.quest_access.v1` when Quest is available, then append capture
+receipts instead of inventing a parallel checklist.
diff --git a/docs/native-rebuild-plan.md b/docs/native-rebuild-plan.md
index 39c648b..a76edff 100644
--- a/docs/native-rebuild-plan.md
+++ b/docs/native-rebuild-plan.md
@@ -5,17 +5,24 @@ hosted stream machine.
## Objective
-Make the room available in OBS as synchronized program video plus separately
+Make the room available as a Mimir-owned synchronized program with separately
controllable audio while preserving ownership:
- Mimir owns configuration, calibration truth, runtime contracts, launch,
status, and persistence.
- Mimir.Runtime owns in-memory stream buffers and runtime synchronization.
- Native capture workers own device reads and append typed sample handles.
-- Fensalir owns GPU fusion, rendering, D3D12 interop, UI, and Spout2 output.
+- Mimir owns stream subscription, scene composition, preview/control state,
+ stats, and program publication intent.
+- Fensalir owns GPU fusion, rendering, D3D12 interop, UI lowering, and local
+ program texture output.
- Faust/native DSP owns hot audio alignment, suppression, separation, stems, and
spatial bed generation.
-- OBS owns broadcast controls.
+- Eve GUI/TUI lowerers expose operator controls and preview without owning
+ scene truth.
+- The Yggdrasil-facing publisher daemon consumes Mimir program output and
+ publishes it to the site without owning composition.
+- OBS is a temporary compatibility sink.
## Runtime State Model
@@ -26,10 +33,13 @@ flowchart TD
N["network feed producers"] --> R
R --> Q["Fensalir UI + GPU fusion"]
R --> F["Faust/native DSP"]
- Q --> S["Spout2/program video"]
+ Q --> S["Mimir program video"]
F --> P["program stems + spatial bed"]
- S --> O["OBS"]
- P --> O
+ Q --> E["Eve operator surfaces"]
+ S --> Y["Yggdrasil site publisher"]
+ P --> Y
+ S -. "compatibility" .-> O["OBS adapter"]
+ P -. "compatibility" .-> O
```
The app-level runtime initializes one rolling buffer per configured audio or
@@ -48,7 +58,8 @@ camera driver buffers, GPU textures, or DSP audio memory.
- Fensalir consumes current visual/audio timing state and publishes the program
video surface.
- Faust/native DSP consumes audio/phase/event handles and publishes stems.
-- OBS receives final program surfaces. It does not own synchronization.
+- OBS compatibility adapters may receive final program surfaces. They do not
+ own synchronization, composition, preview/control, or publication.
## Impossible By Construction
diff --git a/docs/perfect-machine-domain-index.md b/docs/perfect-machine-domain-index.md
index 11c86ce..cd627b3 100644
--- a/docs/perfect-machine-domain-index.md
+++ b/docs/perfect-machine-domain-index.md
@@ -38,19 +38,23 @@ Read this with:
Mimir is a bounded-latency realtime field computer: it ingests many clocks,
places their evidence inside one rolling temporal window, extracts audio/video
-geometry from that window, and emits an OBS-usable program surface without
-letting OBS become the synchronization authority.
+geometry from that window, owns the stream program scene, exposes Eve operator
+surfaces, and emits a publicable program feed without letting compatibility
+sinks become authority.
## Authority Index
| Authority | Owns | Must Not Own |
| --- | --- | --- |
-| `Mimir.Runtime` | stream identity, buffers, sync reports, calibration model loading, source polling, telemetry state | hot DSP actuator, dense visual fusion, broadcast composition |
-| Native capture | direct driver callbacks, device timestamps, payload handles, low-jitter capture loops | runtime policy, UI state, OBS composition |
+| Mimir program composition | source subscription, scene graph, crop/key/transform state, preview/control state, stats, publication intent | local device reads, GPU shader internals, audio-rate DSP, site transport policy |
+| `Mimir.Runtime` | stream identity, buffers, sync reports, calibration model loading, source polling, telemetry state | hot DSP actuator, dense visual fusion, direct device capture |
+| Native capture / Muninn hosts | direct driver callbacks, device timestamps, payload handles, local stream capabilities/media | runtime policy, scene composition, publication |
| `native/reservoir` | shared-edge retention and typed views | payload memory interpretation, rendering, DSP |
-| Fensalir | window, UI, D3D12 bridge, GPU fusion, program video publication | clock authority, audio actuator |
+| Fensalir | window, UI lowering, D3D12 bridge, GPU fusion, program video output | clock authority, audio actuator, scene ownership |
| Faust/native DSP | fractional delay, resampling, separation, spatialization, stems | stream discovery, UI |
-| OBS | final broadcast composition | synchronization, capture timing |
+| Eve lowerers | operator GUI/TUI rendering, controls, previews, stats lowering | scene truth, stream truth, publication truth |
+| Yggdrasil site publisher | public transport, encoder/session state, reconnect policy, site edge health | scene composition |
+| OBS | compatibility preview/output sink | synchronization, capture timing, composition, publication |
| Raven/phones | local decode of codebook/schedule observations | canonical clock authority |
## Evidence Taxonomy
@@ -241,7 +245,9 @@ Six optical sensors with high-rate Eyes and Leap IR mean:
- Cut JSON/stdout local camera hot path when concrete direct drivers land.
- Cut legacy chirplet path once chirp-bin decoder has equivalent docs/tests and
physical path proof.
-- Cut bridge scripts only after OBS-facing direct output is real.
+- Cut bridge scripts only after Mimir program output, Eve operation, and
+ Yggdrasil publication are real enough that OBS compatibility no longer earns
+ its keep.
- Cut ad hoc response weighting if a formal path model replaces it.
## Future Work Index
diff --git a/docs/perfect-machine-module-library.md b/docs/perfect-machine-module-library.md
index fd0e44d..b1ef408 100644
--- a/docs/perfect-machine-module-library.md
+++ b/docs/perfect-machine-module-library.md
@@ -13,11 +13,12 @@ job-interview answer for why it belongs in the body of the machine.
| Bioacoustic clock solver | `Mimir.Runtime` | Fits canonical source time from decoded word anchors for local, Raven, phone, and microcontroller witnesses. | `src/Mimir.Runtime/Synchronization/MimirBioacousticClockHypothesis.cs` |
| Alignment actuator | `Mimir.Runtime` + Faust | Converts sync state into bounded fractional-delay, gain, and sample-rate-offset controls; Faust moves samples. | `src/Mimir.Runtime/Synchronization/MimirAudioAlignmentActuator.cs` |
| Audio actuator strategies | `Mimir.Runtime` + Faust | Integer baseline, Farrow fractional delay, variable ASRC, and hybrid delay/ASRC strategy profiles. | `src/Mimir.Runtime/Synchronization/MimirAudioActuatorConfiguration.cs` |
-| CultMesh contracts | `Mimir.Runtime` | Typed codebook, decoder, acoustic path, and actuator state documents for distributed Mimir nodes. | `src/Mimir.Runtime/Synchronization/MimirCultMeshContracts.cs` |
+| CultMesh contracts | `Mimir.Runtime` | Typed codebook, decoder, acoustic path, actuator, program, Move evidence, and Mimir-fused Move controller pose documents for distributed Mimir nodes. | `src/Mimir.Runtime/Synchronization/MimirCultMeshContracts.cs`, `src/Mimir.Runtime/Synchronization/MimirTrackingObservation.cs` |
+| Move controller fusion | `Mimir.Runtime` + native reservoir + Fensalir | Mimir admits Muninn marker candidates plus controller/IMU/button feeds into native `move_evidence` buffers. `MimirMoveCalibrationProtocol` defines the Starfire/Nightwing capture run, Muninn records optional Quest USB witness availability as `muninn.quest_access.v1`, `MimirMoveFusion` publishes calibrated `mimir.move_controller_pose.v1` position/button/gyro poses only when camera witnesses are calibrated, and `MimirMovePoseStream` carries those poses over a realtime CultMesh frame. IMU orientation/prediction remains the next earned 6DoF step before Fensalir treats the stream as VR-grade controller input. | `src/Mimir.Runtime/Synchronization/MimirTrackingObservation.cs`, `src/Mimir.Runtime/Synchronization/MimirMuninnMoveEvidence.cs`, `src/Mimir.Runtime/Synchronization/MimirMoveCalibrationProtocol.cs`, `src/Mimir.Runtime/Synchronization/MimirMoveFusion.cs`, `src/Mimir.Runtime/Synchronization/MimirMovePoseStream.cs`, `src/Mimir.Runtime/Synchronization/MimirNativeReservoirRuntime.cs`, `native/reservoir/src/lib.rs` |
| Acoustic path learning | `Mimir.Runtime` | Calibration stages for usable bands, confusion, global delay, group delay, and codebook adaptation. | `src/Mimir.Runtime/Synchronization/MimirAcousticPathLearningConfiguration.cs` |
| Acoustic localization | `Mimir.Runtime` + Fensalir | Pairwise TDOA, SRP-PHAT grid, sparse source, and visual-constrained localization profiles plus a small SRP/TDOA scorer. | `src/Mimir.Runtime/Synchronization/MimirAcousticLocalizationConfiguration.cs`, `src/Mimir.Runtime/Synchronization/MimirAcousticLocalizationSolver.cs` |
| Benchmark panels | `Mimir.Runtime` | Decoder golf and meatspace acceptance degradation panels with receipt roots and thresholds. | `src/Mimir.Runtime/Synchronization/MimirBenchmarkPanelConfiguration.cs` |
-| Capture profiles | `Mimir.Runtime` + native workers | Hardware intent for Leap, Kiyos, PS Eyes, Starfire Scarlett, and Raven Scarlett. | `src/Mimir.Runtime/Synchronization/MimirNativeCaptureConfiguration.cs` |
+| Capture profiles | `Mimir.Runtime` + native workers | Hardware intent for Leap, Kiyos, PS Eyes, Starfire Scarlett, Raven Scarlett, and USB-attached Moves on Starfire/Nightwing. | `src/Mimir.Runtime/Synchronization/MimirNativeCaptureConfiguration.cs` |
| Camera ingest strategies | `Mimir.Runtime` + native workers | Diagnostic metadata, managed native wrapper, native SPSC ring, and shared-GPU-texture ingest shapes. | `src/Mimir.Runtime/Synchronization/MimirCameraIngestConfiguration.cs` |
| Reservoir strategies | `Mimir.Runtime` + native reservoir + Fensalir | Managed rolling buffers, native shared-edge rings, and derived GPU temporal field storage. | `src/Mimir.Runtime/Synchronization/MimirReservoirConfiguration.cs` |
| Distributed witnesses | `Mimir.Runtime` + CultMesh | Raven, phone, microcontroller, and remote camera rig configurations for compact timing/path state. | `src/Mimir.Runtime/Synchronization/MimirDistributedWitnessConfiguration.cs` |
@@ -27,7 +28,7 @@ job-interview answer for why it belongs in the body of the machine.
| Visual fusion configurations | `Mimir.Runtime` + Fensalir | Sensor roles and fusion modes from cadence proof through full hybrid evidence. | `src/Mimir.Runtime/Synchronization/MimirVisualFusionConfiguration.cs` |
| Compute offload configurations | `Mimir.Runtime` + CultMesh | Local-heavy, distributed witness, and calibration sweep workload placement. | `src/Mimir.Runtime/Synchronization/MimirComputeOffloadConfiguration.cs` |
| Assembly plans | `Mimir.Runtime` | Named combinations of modules that prove synthetic, Scarlett, meatspace, and distributed cuts. | `src/Mimir.Runtime/Synchronization/MimirMachineAssemblyPlan.cs` |
-| OBS publication profiles | Fensalir + Faust + OBS | Spout/shared-texture video and separately mixable Faust stem publication. | `src/Mimir.Runtime/Synchronization/MimirObsPublicationConfiguration.cs` |
+| Program publication profiles | Mimir + Fensalir + Faust + Eve + Yggdrasil | Mimir-owned scene publication, Eve operator surfaces, Yggdrasil site feed, and temporary OBS compatibility adapters. | `src/Mimir.Runtime/Synchronization/MimirProgramPublicationConfiguration.cs` |
| Fensalir lowering | `Mimir.Runtime` -> Fensalir | Pure mapping from Mimir rolling/video/audio state into Fensalir GPU sensor and acoustic field frames. | `src/Mimir.Runtime/Synchronization/MimirFensalirFieldLowering.cs` |
| Module catalog | `Mimir.Runtime` | Indexes the live module set and verification commands. | `src/Mimir.Runtime/Synchronization/MimirModuleLibrary.cs` |
| Manifest factory | `Mimir.Runtime` | Produces an exportable JSON manifest for tools, docs, UI, and remote witnesses. | `src/Mimir.Runtime/Synchronization/MimirPerfectMachineManifest.cs` |
@@ -42,7 +43,19 @@ job-interview answer for why it belongs in the body of the machine.
stem generation.
- CultMesh carries typed state surfaces; remote nodes may observe and decode,
but they do not become independent clock authorities.
-- OBS receives final program surfaces and stems. It does not own calibration.
+- USB-attached Moves on Starfire and Nightwing publish source-local Muninn
+ evidence streams: glowing-orb marker candidates and controller/IMU/button
+ state. Odin discovers/projects those streams. Mimir owns native evidence
+ buffer admission/layout, calibration, association, triangulation, confidence,
+ and `mimir.move_controller_pose.v1` publication. The current fusion pass
+ resolves calibrated optical position and controller state; orientation remains
+ explicitly unresolved until IMU fusion/prediction lands. Fensalir consumes the
+ resolved pose stream for interactive environments once that contract is wired.
+- Resolved Move poses are framed as
+ `mimir.move_controller_pose_stream_frame.v1` on CultMesh shared-memory bytes
+ streams; the stream is a Mimir output surface, not a new fusion owner.
+- OBS may receive compatibility program surfaces and stems. It does not own
+ calibration, composition, preview/control, or publication.
## Verification Surface
@@ -53,6 +66,12 @@ dotnet build .\Mimir.slnx
dotnet run --no-build --project .\src\Mimir.BufferSmoke\Mimir.BufferSmoke.csproj -- --perfect-machine-profile-smoke
dotnet run --no-build --project .\src\Mimir.BufferSmoke\Mimir.BufferSmoke.csproj -- --perfect-machine-lowering-benchmark
dotnet run --no-build --project .\src\Mimir.BufferSmoke\Mimir.BufferSmoke.csproj -- --perfect-machine-contract-smoke
+dotnet run --no-build --project .\src\Mimir.BufferSmoke\Mimir.BufferSmoke.csproj -- --move-tracking-contract-smoke
+dotnet run --no-build --project .\src\Mimir.BufferSmoke\Mimir.BufferSmoke.csproj -- --move-native-reservoir-smoke --native-reservoir .\native\reservoir\target\debug\localcast_reservoir.dll
+dotnet run --no-build --project .\src\Mimir.BufferSmoke\Mimir.BufferSmoke.csproj -- --muninn-move-evidence-smoke --native-reservoir .\native\reservoir\target\debug\localcast_reservoir.dll
+dotnet run --no-build --project .\src\Mimir.BufferSmoke\Mimir.BufferSmoke.csproj -- --muninn-move-cultmesh-stream-smoke --native-reservoir .\native\reservoir\target\debug\localcast_reservoir.dll
+dotnet run --no-build --project .\src\Mimir.BufferSmoke\Mimir.BufferSmoke.csproj -- --move-calibration-protocol-smoke
+dotnet run --no-build --project .\src\Mimir.BufferSmoke\Mimir.BufferSmoke.csproj -- --move-fusion-smoke
dotnet run --no-build --project .\src\Mimir.BufferSmoke\Mimir.BufferSmoke.csproj -- --perfect-machine-manifest
dotnet run --no-build --project .\src\Mimir.BufferSmoke\Mimir.BufferSmoke.csproj -- --bioacoustic-actuator-self-test
```
diff --git a/docs/research/moonlight-muninn-sleipnir-study-2026-07-16.md b/docs/research/moonlight-muninn-sleipnir-study-2026-07-16.md
new file mode 100644
index 0000000..deadbbc
--- /dev/null
+++ b/docs/research/moonlight-muninn-sleipnir-study-2026-07-16.md
@@ -0,0 +1,333 @@
+# Moonlight lessons for Muninn streaming and Sleipnir input
+
+Date: 2026-07-16
+Upstream examined: `moonlight-stream/moonlight-common-c` commit `82e25148549d249a80aaf4afc68e2abb07072edf`
+
+## Verdict
+
+Moonlight is strong because it does not ask one transport policy to serve every
+realtime signal. It gives video, audio, control, and input different loss,
+ordering, recovery, and freshness rules, then keeps each queue bounded by what
+the user can still perceive.
+
+Muninn is not missing all of that machinery. Its media packetizer already owns
+access-unit boundaries, timestamps and deadlines, video parity, dependency and
+keyframe metadata, bounded assembly, receiver damage feedback, repair caching,
+and keyframe requests. Sleipnir already rejects regressing sequence/timestamp
+pairs, expires reliable HID sends after 25 ms, reports stage latency, and
+neutralizes stale output.
+
+The 2026-07-17 field cut closes most of the original mechanism gaps:
+
+- canonical video and video parity use the unreliable `realtime` lane; typed
+ 8+8 GF(256) blocks, selective repair, and decode-chain IDR recovery own loss;
+- canonical 10 ms PCM audio uses `realtime`, with 4+2 FEC, a 40 ms reorder
+ owner, and short concealment; reliable delivery is reserved for control;
+- the consumer deadline is 100 ms by default and 250 ms in the Raven field
+ profile; queues, retransmits, repair material, and assemblies derive from it;
+- receiver pressure actuates the live NVENC encoder through multiplicative
+ backoff and conservative additive recovery; startup begins at half the
+ requested ceiling because fixed video parity can roughly double wire load;
+- Sleipnir latest state and ordered edges have distinct epoch/sequence and
+ application-ack semantics, including bounded overflow rebase.
+
+The remaining claim boundary is evidence: direct and impaired Raven-to-
+Starfire soaks, reconnect repetition, A/V skew measurement, and a live virtual
+HID exercise. The machine is no longer missing a named owner for these paths;
+it still has to prove those owners under pressure.
+
+Do not transplant Moonlight wholesale. Distill its ownership model into the
+existing typed Muninn/Sleipnir pipeline.
+
+## Authority map
+
+### Muninn media
+
+- Owner: Muninn owns source-local capture, encoding child lifecycle, media
+ packetization, repair material, and sender feedback response.
+- Inputs: encoded H.264/H.265 access units, encoded audio packets, source media
+ timestamps, configured latency target, and receiver feedback.
+- Outputs: typed video chunks/parity, audio packets, recovery/keyframe actions,
+ and transport/quality telemetry.
+- Derived state: RUDP acknowledgements, resend counters, CultCache receipts, OBS
+ local lowering, and operator cards are evidence or delivery substrate. They do
+ not own media deadlines.
+- Forbidden writers: generic RUDP retry policy must not decide whether expired
+ media is still useful; OBS must not decide stream synchronization; persistence
+ must not become a hot media queue.
+
+### Sleipnir input
+
+- Owner: Sleipnir owns selection/remapping and local virtual-HID emission.
+ Muninn owns source-local device sampling and event classification.
+- Inputs: a discovered Muninn input stream, typed mapping intent, monotonic
+ stream sequence, source-local capture time, and receive time.
+- Outputs: local virtual-HID state plus latency/freshness receipts.
+- Derived state: the latest full controller snapshot is replaceable state.
+ Button transitions, text/code points, connect/disconnect, and neutralization
+ are non-replaceable edges until acknowledged or superseded by an explicit
+ state transition.
+- Forbidden writers: generic reliable queue order must not replay stale analog
+ or motion samples; wall-clock subtraction must not claim one-way latency
+ without a clock model; the telemetry/store path must not repair a missing hot
+ stream.
+
+## What Moonlight actually does
+
+### It separates stream semantics
+
+Moonlight receives video and audio over UDP/RTP-like paths while input travels
+through its control path (or a legacy TCP socket). The important lesson is not
+the literal socket choice. It is that the queues have different contracts.
+Video is frame/dependency oriented; audio is short-block continuity oriented;
+input is event/state oriented.
+
+Source: [stream initialization and input transport](https://github.com/moonlight-stream/moonlight-common-c/blob/82e25148549d249a80aaf4afc68e2abb07072edf/src/InputStream.c),
+[video receive path](https://github.com/moonlight-stream/moonlight-common-c/blob/82e25148549d249a80aaf4afc68e2abb07072edf/src/VideoStream.c), and
+[audio receive path](https://github.com/moonlight-stream/moonlight-common-c/blob/82e25148549d249a80aaf4afc68e2abb07072edf/src/AudioStream.c).
+
+### Video recovery is dependency-aware and deadline-shaped
+
+The video queue groups packets by frame and FEC block, tracks missing data,
+reconstructs recoverable blocks with Reed-Solomon parity, rejects old/replayed
+frames, and advances frame authority explicitly. Depacketization can request an
+IDR when damage makes the predictive chain unsafe. The system repairs useful
+frames and abandons poisoned dependency chains; it does not wait forever for
+generic reliable delivery.
+
+Source: [RTP video queue](https://github.com/moonlight-stream/moonlight-common-c/blob/82e25148549d249a80aaf4afc68e2abb07072edf/src/RtpVideoQueue.c) and
+[video depacketizer](https://github.com/moonlight-stream/moonlight-common-c/blob/82e25148549d249a80aaf4afc68e2abb07072edf/src/VideoDepacketizer.c).
+
+### Audio recovery is a small bounded continuity problem
+
+Moonlight groups audio into fixed FEC blocks, orders packets by RTP sequence,
+recovers missing shards, detects out-of-order behavior, and changes how long it
+waits based on observed reordering. When recovery cannot complete in time, it
+allows a discontinuity and lets the audio decoder's concealment path carry the
+scar. It does not turn late audio into permanent latency.
+
+Source: [audio FEC and reorder queue](https://github.com/moonlight-stream/moonlight-common-c/blob/82e25148549d249a80aaf4afc68e2abb07072edf/src/RtpAudioQueue.c) and
+[packet-loss concealment handoff](https://github.com/moonlight-stream/moonlight-common-c/blob/82e25148549d249a80aaf4afc68e2abb07072edf/src/AudioStream.c).
+
+### Input reliability is constrained by semantic coalescing
+
+Moonlight has one send thread and a bounded queue, but it avoids blindly sending
+every sampled value. Relative mouse motion is accumulated, absolute pointer
+motion keeps the newest position, pen hover/move keeps the newest compatible
+event, controller analog state is merged while button changes split batches,
+and sensor motion keeps the newest value per sensor. A roughly millisecond-scale
+batching opportunity can reduce latency by preventing ENet queue buildup.
+
+This is the most important Sleipnir lesson: reliable delivery is safe only after
+replaceable state has been collapsed and edges have been preserved.
+
+Source: [input batching and send queue](https://github.com/moonlight-stream/moonlight-common-c/blob/82e25148549d249a80aaf4afc68e2abb07072edf/src/InputStream.c).
+
+### It exposes failure to the owning layer
+
+Packet loss, FEC recovery/failure, queue overflow, frame loss, and keyframe need
+are explicit signals. Moonlight's public documentation also warns that allowing
+frames to queue for smoothness can create high latency; its balanced mode keeps
+only about one frame for jitter smoothing. The machine treats queue depth as a
+product decision, not an incidental container property.
+
+Source: [Moonlight frame-pacing FAQ](https://github.com/moonlight-stream/moonlight-docs/wiki/Frequently-Asked-Questions#what-do-the-frame-pacing-options-on-the-android-client-mean).
+
+## Current Muninn/Sleipnir comparison
+
+| Concern | Moonlight | Muninn / Sleipnir now | Distilled judgment |
+|---|---|---|---|
+| Video unit | Encoded frame, FEC blocks, dependency recovery | Encoded access unit, chunks/parity, frame/dependency IDs, deadlines | The conceptual unit is already correct. Keep it. |
+| Video loss | FEC first; abandon damaged/obsolete work; request IDR | Typed 8+8 block FEC, deadline-bound repair, completed-frame tombstones, IDR actuation | Same ownership pattern; field loss ratios and parity overhead remain tunable. |
+| Audio loss | Small FEC blocks, reorder-aware wait, decoder concealment | Realtime 4+2 PCM FEC, 40 ms reorder, bounded concealment | Mechanism is closed; Opus FEC/PLC is a later efficiency/quality cut. |
+| Congestion | Stream-specific queues and feedback behavior | Half-ceiling startup, 15% pressure backoff, bounded additive ramp, fresh media ahead of repair | Closed loop exists; impairment evidence decides its final constants. |
+| Latency budget | Minimal queueing; frame pacing explicitly trades latency for smoothness | Consumer-owned 100 ms default/250 ms Raven proof; 25 ms HID expiry | Correct owner and bounded values; glass-to-glass measurement remains. |
+| Input state | Coalesces replaceable motion/sensor/analog values | Epoch-scoped latest state supersedes stale state | Structural rather than polling-accidental. |
+| Input edges | Button/type changes terminate batches; text is ordered | Ordered edge sequence, application ACK, bounded replay, epoch rebase | Structural; live virtual-HID impairment proof remains. |
+| Staleness | Queue construction prevents much stale work | Receiver drops regressing timestamps and neutralizes after timeout | Keep the guard, but move freshness authority earlier into enqueue/supersession. |
+| Timing | RTP/media clocks and local queue timing | source Unix nanoseconds plus receiver instants | Use monotonic source time plus an explicit clock-offset/uncertainty model; do not call raw wall-clock subtraction latency. |
+| Security | Authenticated encryption in modern stream generations | CultNet typed transport policy varies by lane | Preserve typed schemas, but specify replay/authentication guarantees for hot media and HID lanes. |
+
+## Recommended cut
+
+### 1. Define traffic classes before changing transport
+
+Add one narrow typed delivery policy shared by Muninn and Sleipnir:
+
+- `state-latest`: analog axes, pointer position, gyro/accelerometer samples;
+- `edge-ordered`: button/key transitions, text/code points, device lifecycle;
+- `media-video`: dependency-bearing access units with decode deadlines;
+- `media-audio`: continuity blocks with playout deadlines;
+- `control-reliable`: subscriptions, mappings, capability negotiation, and
+ keyframe requests.
+
+This is not a generic router. It is a finite statement of the five contracts the
+existing machine already has. Each class must name supersession, ordering,
+expiry, recovery, and observability.
+
+### 2. Cut the two-second realtime default
+
+Derive deadlines from the actual consumer:
+
+- Sleipnir state-latest: tens of milliseconds, with replacement before send;
+- input edges: reliable until acknowledged, but compacted against later
+ authoritative state where safe;
+- interactive video: frame/decode budget, initially test 50–150 ms on LAN;
+- audio: playout budget, initially test 40–100 ms on LAN;
+- Mimir's five-second analysis reservoir remains Mimir compute budget, not a
+ network permission to deliver old frames.
+
+The exact values must be earned by field traces. The ownership change is not
+optional: transport retry duration becomes derived from the media/input
+deadline, never the other way around.
+
+### 3. Make Sleipnir freshness structural
+
+Split the hot input frame into:
+
+- a latest-state snapshot with `state_sequence`, source monotonic capture time,
+ and supersedes-through sequence;
+- an ordered edge list with its own sequence range;
+- receiver acknowledgement of the highest applied state and edge sequence.
+
+Muninn may overwrite an unsent latest-state snapshot. It may not overwrite an
+unacknowledged edge. Sleipnir applies edges in order, applies only the newest
+state, and neutralizes on a named lease timeout. Manual, remapped, replayed, and
+live paths must use the same commit primitive.
+
+### 4. Close Muninn's feedback loop
+
+Receiver feedback should drive named sender decisions:
+
+- request/force keyframe after unrecoverable dependency loss;
+- choose repair only when estimated arrival precedes frame deadline;
+- tune parity within a bounded profile from recent burst-loss evidence;
+- reduce encoder bitrate/pacing when queue delay grows;
+- expose encode, queue, network, assembly, decode, and presentation stages
+ separately.
+
+Do not add a second congestion controller beside generic RUDP and hope the two
+argue productively. Media policy owns the send budget; RUDP exposes delivery
+mechanics and obeys the deadline.
+
+### 5. Prove it on the layer the user feels
+
+Build one loss/jitter/reorder test harness and record timelines for:
+
+- controller press and release during 1%, 5%, and burst packet loss;
+- high-rate analog/gyro motion under an artificial sender stall;
+- video dependency loss and keyframe recovery;
+- audio burst loss and concealment;
+- queue growth under bitrate oversubscription;
+- disconnect/reconnect and stale-session replay;
+- clock offset and drift between Muninn and Sleipnir hosts.
+
+Acceptance should bound p50/p95/p99 capture-to-HID age, lost/duplicated edges,
+time-to-neutral, glass-to-glass video age, audio discontinuities, A/V skew, and
+recovery time. Final-state-only tests will politely conceal the corpse.
+
+## What not to copy
+
+- Do not copy Moonlight's C implementation or protocol packets into GameCult
+ code without an explicit GPLv3 licensing decision. Architectural study is not
+ license laundering. See [Moonlight's license](https://github.com/moonlight-stream/moonlight-common-c/blob/82e25148549d249a80aaf4afc68e2abb07072edf/LICENSE.txt).
+- Do not replace typed CultMesh discovery, command state, or ownership with
+ RTSP/SDP or a Moonlight compatibility stack.
+- Do not make ENet the new universal answer. Moonlight's strength comes from
+ semantic queues above the carrier, not from the carrier's brand name.
+- Do not merge input, audio, and video into one ordered reliable stream. One
+ lost video fragment must not head-of-line block a button release or audio
+ packet.
+- Do not treat FEC as magic. Parity is useful only when its overhead and recovery
+ time fit the deadline.
+
+## Distilled doctrine
+
+Realtime strength is not “reliable UDP.” It is knowing which truth survives
+loss:
+
+- the newest state replaces old state;
+- an edge remains until applied or explicitly superseded;
+- a media packet is valuable only while its frame or playout deadline is alive;
+- predictive video damage is healed by recovery or a new reference, not by
+ delivering every corpse;
+- queues are latency budgets made physical;
+- feedback that cannot change sender behavior is telemetry, not control.
+
+Muninn and Sleipnir already have enough organs to become strong. The next work
+is to give each signal one explicit survival law and make the transport obey it.
+
+## 2026-07-17 LAN field results
+
+The Moonlight-derived ownership changes now have Raven-to-Starfire field
+evidence over LAN, with Raven processes launched only through hidden scheduled
+tasks:
+
+- Muninn video uses independently recoverable block parity plus bounded repair.
+ During a seeded 1% datagram-loss run, the OBS receiver advanced from 2 to 12
+ video assembly expiries while forwarding 14,400 additional payload groups:
+ 99.93% completed without expiry. Audio reported zero failures, skips, or stale
+ packets in the same interval. The impairment proxy forwarded 2,147,356 of
+ 2,169,105 datagrams with zero queue overflow.
+- Sleipnir latest-state delivery remained live through seeded 1% loss. Cutting
+ the proxy caused the non-neutral virtual Xbox pad to neutralize after 1.56 s,
+ owned by the named stale-input lease; restoring the hidden proxy reconnected
+ and resumed state without operator action.
+- A continuous 64-packet receive drain was retaining the newest input for about
+ 48-57 ms before HID application. Bounding that drain to 2 ms reduced live
+ receiver-owned receive-to-apply time to typically 69-204 microseconds. A
+ witnessed 16 ms spike remains to characterize.
+- Starfire and Raven wall clocks differ by roughly 2.38 s. Sleipnir no longer
+ publishes their raw subtraction as one-way latency. Its surface reports that
+ source-clock latency is unavailable without a clock model and exposes only
+ monotonic receiver-owned stage durations.
+
+These results clear the initial 99.9% media-completion and disconnect-safety
+gates at 1% independent loss. They do not yet prove glass-to-glass latency,
+A/V skew, ordered quick-tap delivery under loss, 5%/burst behavior, or a long
+mixed-impairment soak. Those remain acceptance gates, not footnotes.
+
+The field comparison also exposed a generic transport invariant Moonlight's
+long-running sessions force us to respect: bounded wire identifiers must wrap
+deliberately. CultNet's fragmented-message ID previously saturated at the
+maximum `u16`, so fragmented audio became unreassemblable after the boundary.
+Wrap-to-one plus a boundary test removed the repeatable minute-eight collapse;
+a subsequent 10-minute direct LAN run remained clean across the boundary.
+
+## 2026-07-17 ordered-input closure
+
+The quick-tap gate exposed four authorities that generic RUDP reliability had
+blurred together:
+
+- A Windows sleep requested at two milliseconds could still miss a short tap.
+ Muninn now observes XInput on a dedicated high-resolution worker; transport,
+ discovery, serialization, and logging cannot delay capture.
+- Global cumulative ACKs could not retire a reliable packet arriving more than
+ 32 realtime sequence numbers late, and reliable duplicates were not ACKed
+ after application deduplication. CultNet now directly acknowledges the late
+ packet and acknowledges reliable duplicates. This collapsed a runaway field
+ trace from hundreds of thousands of resend datagrams to a bounded flow.
+- Muninn used semantic application ACK as permission to send the next edge.
+ One delayed ACK therefore serialized the entire controller. It now sends a
+ bounded window of captured edges; CultNet owns retransmission, Sleipnir's
+ epoch/sequence cursor owns ordering, and semantic ACK only retires history.
+- Sleipnir published CultMesh/Eve and Idunn state synchronously on the actuator
+ thread. Those writes produced 0.7-1.8 second visible button holds. A bounded
+ coalescing publisher worker now owns control-plane projection, leaving the
+ input thread to receive, order, actuate, and acknowledge.
+
+The resulting first-hot-plug field proof generated 100 taps with a 16 ms source
+press and 84 ms gap. Under seeded 1% independent loss, every layer observed
+exactly 100 presses and 100 releases; Raven-visible durations were 24-57 ms.
+Under eight consecutive dropped packets every 200 datagrams, every layer again
+observed exactly 100 presses and 100 releases; visible durations were 24-40 ms,
+the final virtual pad was neutral, and the impairment proxy reported no queue
+overflow. Raven ran through hidden scheduled tasks over `192.168.1.0/24`; no
+WireGuard route participated. Both daemons used the Eve/Aetheria/VoidBot
+experimental CultLib lineage pinned at `8965f3c0`.
+
+This closes ordered digital edges at one 60 Hz source frame. It does not prove
+sub-frame synthetic ViGEm pulses: eight-millisecond state-only pulses can fall
+between XInput snapshots, and `XInputGetKeystroke` was rejected after it emitted
+stale events from a reused user slot. Analog motion under a sender stall,
+mixed media/input endurance, glass-to-glass video age, and A/V skew remain open.
diff --git a/docs/research/moonlight-reliability-acceptance-2026-07-16.md b/docs/research/moonlight-reliability-acceptance-2026-07-16.md
new file mode 100644
index 0000000..7a294a1
--- /dev/null
+++ b/docs/research/moonlight-reliability-acceptance-2026-07-16.md
@@ -0,0 +1,100 @@
+# Moonlight-level reliability acceptance ledger
+
+This ledger distinguishes implemented mechanisms from evidence that the actual
+Raven-to-Starfire program timeline survives pressure. Passing unit tests is not
+permission to call the field path done.
+
+## Required contracts
+
+| Contract | Current authority | Evidence | Status |
+|---|---|---|---|
+| Experimental CultMesh/CultNet lineage | Odin vendored CultLib snapshot `8965f3c0` | Snapshot provenance plus CultNet/Muninn/Sleipnir suites | proved locally |
+| Low-delay NVENC | Muninn controllable encoder | D3D11 desktop capture, no B frames/lookahead, two-frame VBV, clean Annex-B decode | proved locally |
+| Consumer deadline owns media lifetime | Muninn queues, repair cache, Mimir assembly/reorder | 100 ms default/250 ms Raven profile, bounded queues/cache/reliable expiry, late-frame repair refusal | proved structurally |
+| Recoverable video loss avoids decoder reset | Mimir V4 block FEC/repair, Odin repair cache | Exhaustive 2..80 chunk, burst 1..8 tests in Rust/C++; completed-frame tombstones | proved locally |
+| Decoder-chain loss forces immediate IDR | Muninn feedback owner and native encoder | Live 2560x1440 command produced IDR at frame 22 under a 600-frame GOP | proved locally |
+| Congestion changes encoder output in-session | Muninn AIMD controller and native encoder | Half-ceiling start and protected cap; live 12-to-6 Mbps reconfiguration produced clean IDR | proved locally |
+| Audio survives small erasure bursts | Odin 4+2 GF(256), Mimir FEC/reorder/concealment | Exhaustive one/two-erasure Rust and C++ tests; production DLL build | proved locally |
+| Latest input state supersedes stale state | Muninn epoch/state sequence, Sleipnir cursor | Duplicate/reorder/stale epoch tests | proved locally |
+| Button edges are not lost or retained forever | Muninn replay/app ACK/epoch rebase, Sleipnir ordered cursor | Quick-tap, ACK, overflow rebase, sparse-gap tests | proved locally |
+| Raven runtime is LAN-only and non-interrupting | Hidden Raven scheduled task, direct LAN SSH/media | One coherent Muninn/WASAPI/NVENC/FFmpeg tree; no WireGuard route; `-WindowStyle Hidden` | proved operationally |
+| Real field path survives impairment and soak | Raven sender, Starfire receiver/OBS, `cultnet-impair` | Direct LAN clean run 05:34:17–05:44:37: 79,200 records, 697,450,085 bytes, 18,588 recovered shards, zero decode/assembly/media/audio failures and zero sender capacity eviction; proxy matrix and long soak remain | **direct clean passed** |
+
+## Field acceptance matrix
+
+Run the real capture body, production receiver plugin, decoder, audio output,
+and Sleipnir virtual input through the seeded profiles under
+`E:\Projects\Odin\tests\realtime-impairment`. Observe the rendered/audio/input
+timeline, not only proxy CSV counters.
+
+| Profile | Required result |
+|---|---|
+| clean, 10 minutes | no decoder reset, audio discontinuity, input edge loss, queue growth, or timestamp drift; video p99 delivery under 50 ms |
+| iid 1%, 10 minutes | no input edge loss; audio FEC recovers eligible holes; at least 99.9% video frames presented; media p99 under 80 ms |
+| iid 3%, 5 minutes | bounded degradation; at least 99% video frames presented; decoder recovery within 250 ms; no stuck controls |
+| burst 4 and burst 8 | parity/repair handles eligible damage; unrecoverable dependency loss produces one requested IDR and recovery within 250 ms |
+| reorder and duplicate | no duplicate input action, stale state regression, or unbounded assembly/reorder storage |
+| delay/jitter | AIMD reduces bitrate before sustained deadline loss and recovers no faster than one step per two stable seconds |
+| stall 250 ms | obsolete media expires; queues remain bounded; input latest state converges within 35 ms after release |
+| reconnect | old input epoch cannot act; neutral/current state within 100 ms; decoded video within 500 ms |
+| mixed 60-minute soak | stable memory/handle/thread counts and no progressive A/V drift |
+
+The first seeded 1% proxy run is a failed acceptance result, not a pass: 987 of
+95,741 datagrams were dropped; in roughly one minute the receiver recorded 38
+expired video assemblies and 56 skipped audio packets while remaining bounded
+and avoiding decoder reset. CultNet fragmentation currently sits below
+application-level FEC, so one lost transport fragment discards an entire FEC
+shard. The next reliability cut is to make protected media shards fit one wire
+datagram or move fragmentation above the erasure-code boundary.
+
+## Field scars
+
+- A fragmented CultNet RUDP session failed repeatably after roughly eight to
+ nine minutes because the `u16` fragment-set ID used saturating increment and
+ became permanently stuck at 65,535. Every later fragmented audio document
+ then collided in one receiver reassembly key. The allocator now wraps from
+ 65,535 to 1, with a boundary test. The clean field run above crossed that
+ boundary without a single audio reorder, skip, stale packet, or failure.
+- Audio and video producer ingress now use separate bounded channels. Video
+ capture can no longer block audio ingestion before the audio-priority send
+ scheduler sees it. Capacity-eviction counters proved zero during the clean
+ acceptance run.
+
+## 2026-07-17 bounded-media field cut
+
+The live path now keeps every protected shard in one datagram, sends AAC ADTS
+in fixed 864-byte 4+2 audio blocks, uses 768-byte video shards with 8+4 block
+FEC, and begins each decoder epoch at a complete IDR. The sender admits video
+to its repair cache when the frame enters the queue, accepts the production
+CultNet `realtime` channel, and can service bounded repair requests at
+64/96/128 ms in batches of up to eight chunks.
+
+Seeded Raven-to-Starfire evidence:
+
+- iid 1%: 802 of 78,406 datagrams dropped; zero decode drop, video expiry,
+ audio failure, or audio skip; 1,800 video frames recovered;
+- burst-4: 1,436 of 71,910 datagrams dropped; zero expiry or audio skip across
+ 1,800 forwarded frames;
+- reorder: 2,872 reorder events; zero expiry or audio skip across 900 frames;
+- iid 3%: 2,564 of 85,731 datagrams dropped; three of 5,400 frames expired,
+ zero audio skip (99.94 percent video delivery);
+- burst-8: 190 chunks repaired; one of 1,800 frames expired, zero audio skip;
+- 250 ms stall: 420 datagrams withheld, one frame expired, 68 chunks repaired,
+ an IDR was requested, and another 900 frames arrived without a stuck decoder
+ or audio skip.
+
+Delay/jitter produced one expiry in 900 frames. Duplicate, reconnect, longer
+iid runs, and the 60-minute mixed soak remain open acceptance rows.
+
+Field scar: production video uses the `realtime` channel, but the repair cache
+previously admitted only legacy `media` payloads. Synthetic tests built the
+legacy shape and certified a repair path that was empty in production. The
+production-channel regression test now owns this boundary.
+
+## Completion rule
+
+Moonlight-level performance is reasonably expected only after all local rows
+remain green and the real field matrix passes twice: once on direct LAN and once
+on the deployed route actually used during streaming. Failure should change the
+owning deadline, FEC, encoder, or input authority—not add a repair loop around
+the symptom.
diff --git a/docs/viable-stream-app.md b/docs/viable-stream-app.md
index a304701..eb58c07 100644
--- a/docs/viable-stream-app.md
+++ b/docs/viable-stream-app.md
@@ -4,12 +4,13 @@
Build the smallest live Mimir application that can ingest multiple local or
networked video and audio feeds, hold a short synchronized window in memory,
-and emit coherent OBS-facing program outputs while Fensalir provides the
-operator UI.
+and emit coherent Mimir program outputs while Eve provides operator surfaces
+and Yggdrasil publishes the site feed.
The default reservoir horizon is five seconds. That delay is not decoration. It
is the budget that lets late network packets, independent capture clocks,
-audio/video alignment, and debug controls converge before OBS sees the result.
+audio/video alignment, and debug controls converge before Mimir emits the
+program result.
Mimir is explicitly trading latency and memory for throughput and extraction
quality: the app may spend that buffered window lining streams up and crunching
audio/video data to recover as much of the volumetric audio field and
@@ -21,16 +22,16 @@ alignment and field-reconstruction budget.
Today the repo has two partial machines:
-- FFmpeg/SRT bridge scripts can capture sender video/audio and expose stable OBS
- Media Source endpoints.
+- FFmpeg/SRT bridge scripts can capture sender video/audio and expose stable
+ compatibility endpoints.
- The native `LocalcastRuntime` reservoir ABI can hold typed live sample handles,
and Fensalir can consume native render packets for GPU fusion.
Those parts are useful, but they are not yet the viable app. The viable app is
the join: ingest workers append live sample handles into one Fensalir-hosted
runtime window, Fensalir renders video from that window, Faust/native audio
-builds synchronized stems from the same window, and OBS receives only the
-program outputs.
+builds synchronized stems from the same window, Mimir commits the scene graph,
+Eve lowers the operator surface, and Yggdrasil publishes the program output.
```mermaid
flowchart TD
@@ -39,10 +40,15 @@ flowchart TD
C["local mic/loopback workers"] --> R
D["network audio ingest workers"] --> R
R --> U["Fensalir debug/settings/output UI"]
- R --> V["Fensalir GPU video composition"]
+ R --> S["Mimir program scene graph"]
+ S --> U
+ S --> V["Fensalir GPU video composition"]
R --> F["Faust/native audio alignment + stems"]
- V --> O["OBS video output"]
- F --> P["OBS audio stem outputs"]
+ S --> E["Eve GUI/TUI operator surfaces"]
+ V --> Y["Yggdrasil site publisher"]
+ F --> Y
+ V -. "compatibility" .-> O["OBS adapter"]
+ F -. "compatibility" .-> O
```
## Invariants
@@ -51,12 +57,17 @@ flowchart TD
- The five-second rolling window is a deliberate compute budget, not accidental
latency. It exists so capture, sync, GPU fusion, and audio field extraction
can choose coherence over immediacy.
-- Fensalir owns the running app, UI, debug state, settings changes, output
- management, GPU fusion/rendering, and Spout/video publication.
+- Mimir owns the program scene graph, source subscription policy, preview/control
+ state, stats, and publication intent.
+- Fensalir owns the running app host, UI lowering, debug state, GPU
+ fusion/rendering, and local program texture output.
- Faust/native DSP owns audio alignment, voice separation, suppression,
volumetric sound-field extraction, stems, and spatial bed generation.
-- OBS receives synchronized program surfaces. Raw unsynchronized feeds may be
- diagnostic inputs, not broadcast truth.
+- Eve lowerers render controls and previews without owning scene truth.
+- The Yggdrasil publisher consumes synchronized program surfaces without owning
+ a second composition.
+- OBS compatibility adapters may receive synchronized program surfaces. Raw
+ unsynchronized feeds may be diagnostic inputs, not broadcast truth.
- Networked feeds are producers into the same reservoir. They do not own clocks,
private buffers, or a parallel synchronization model.
- Missing feeds become explicit absence or silent/blank placeholders inside the
@@ -78,9 +89,14 @@ The first cut should be deliberately boring:
loopback ingest.
7. Expose Fensalir UI readouts for reservoir edge, window start, per-kind
counts, feed health, delay estimate, output status, and current settings.
-8. Render one synchronized program video output from the reservoir.
-9. Emit separately controllable OBS audio stems from the same reservoir window.
-10. Keep the existing FFmpeg/SRT scripts as edge utilities for network capture
+8. Render one synchronized Mimir program video output from the reservoir.
+9. Emit separately controllable program audio stems from the same reservoir
+ window.
+10. Publish Eve GUI/TUI operator surfaces for preview, source selection, scene
+ edits, stats, and output state.
+11. Add the Yggdrasil-facing publisher daemon that consumes Mimir program
+ output without owning composition.
+12. Keep the existing FFmpeg/SRT scripts as edge utilities for network capture
and OBS compatibility, not as the stream authority.
## Repo Shape
diff --git a/native/obs_stem_source/CMakeLists.txt b/native/obs_stem_source/CMakeLists.txt
new file mode 100644
index 0000000..6e19ee1
--- /dev/null
+++ b/native/obs_stem_source/CMakeLists.txt
@@ -0,0 +1,66 @@
+cmake_minimum_required(VERSION 3.20)
+
+project(mimir_obs_stem_source LANGUAGES CXX)
+
+find_package(libobs REQUIRED CONFIG)
+include(CTest)
+
+add_library(mimir_obs_stem_source MODULE
+ mimir_obs_muninn_source.cpp
+ mimir_obs_program_texture_source.cpp
+ mimir_obs_stem_source.cpp)
+
+target_compile_features(mimir_obs_stem_source PRIVATE cxx_std_20)
+target_compile_definitions(mimir_obs_stem_source PRIVATE
+ WIN32_LEAN_AND_MEAN
+ NOMINMAX
+ _WIN32_WINNT=0x0A00)
+target_link_libraries(mimir_obs_stem_source PRIVATE OBS::libobs d3d11 d3d12 dxgi ws2_32)
+
+if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../../tools/muninn_media_decoder_probe.cpp")
+ add_executable(muninn_media_decoder_probe
+ ../../tools/muninn_media_decoder_probe.cpp)
+ target_compile_features(muninn_media_decoder_probe PRIVATE cxx_std_20)
+ target_include_directories(muninn_media_decoder_probe PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR})
+endif()
+
+add_executable(muninn_rudp_url_tests
+ tests/muninn_rudp_url_tests.cpp)
+target_compile_features(muninn_rudp_url_tests PRIVATE cxx_std_20)
+target_include_directories(muninn_rudp_url_tests PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR})
+add_test(NAME muninn_rudp_url_tests COMMAND muninn_rudp_url_tests)
+
+add_executable(muninn_media_wire_tests
+ tests/muninn_media_wire_tests.cpp)
+target_compile_features(muninn_media_wire_tests PRIVATE cxx_std_20)
+target_include_directories(muninn_media_wire_tests PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR})
+add_test(NAME muninn_media_wire_tests COMMAND muninn_media_wire_tests)
+
+add_executable(muninn_audio_fec_tests
+ tests/muninn_audio_fec_tests.cpp)
+target_compile_features(muninn_audio_fec_tests PRIVATE cxx_std_20)
+target_include_directories(muninn_audio_fec_tests PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR})
+add_test(NAME muninn_audio_fec_tests COMMAND muninn_audio_fec_tests)
+
+add_executable(muninn_video_fec_tests
+ tests/muninn_video_fec_tests.cpp)
+target_compile_features(muninn_video_fec_tests PRIVATE cxx_std_20)
+target_include_directories(muninn_video_fec_tests PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR})
+add_test(NAME muninn_video_fec_tests COMMAND muninn_video_fec_tests)
+
+add_executable(muninn_udp_socket_tests
+ tests/muninn_udp_socket_tests.cpp)
+target_compile_features(muninn_udp_socket_tests PRIVATE cxx_std_20)
+target_compile_definitions(muninn_udp_socket_tests PRIVATE
+ WIN32_LEAN_AND_MEAN
+ NOMINMAX
+ _WIN32_WINNT=0x0A00)
+target_include_directories(muninn_udp_socket_tests PRIVATE
+ ${CMAKE_CURRENT_SOURCE_DIR})
+target_link_libraries(muninn_udp_socket_tests PRIVATE ws2_32)
+add_test(NAME muninn_udp_socket_tests COMMAND muninn_udp_socket_tests)
diff --git a/native/obs_stem_source/data/locale/en-US.ini b/native/obs_stem_source/data/locale/en-US.ini
new file mode 100644
index 0000000..50b760f
--- /dev/null
+++ b/native/obs_stem_source/data/locale/en-US.ini
@@ -0,0 +1,14 @@
+MimirAudioStem="Mimir Audio Stem"
+MimirAudioStem.MapName="Shared memory map"
+MimirAudioStem.StemId="Stem id"
+MimirProgramTexture="Mimir Program Texture"
+MuninnStream="Muninn Stream"
+MuninnVideoSource="Muninn Video"
+MuninnAudioSource="Muninn Audio"
+MuninnStream.Stream="Muninn stream"
+MuninnStream.Video="Video device"
+MuninnStream.Audio="Audio device"
+MuninnStream.VideoBitrateKbps="Video bitrate (kbps)"
+MuninnStream.VideoLatencyMs="Video/transport latency (ms)"
+MuninnStream.AudioReorderMs="Audio reorder latency (ms)"
+MuninnStream.Refresh="Refresh Muninn streams"
diff --git a/native/obs_stem_source/mimir_obs_muninn_source.cpp b/native/obs_stem_source/mimir_obs_muninn_source.cpp
new file mode 100644
index 0000000..7b79587
--- /dev/null
+++ b/native/obs_stem_source/mimir_obs_muninn_source.cpp
@@ -0,0 +1,4651 @@
+#ifdef _WIN32
+#include
+#include
+#include
+#endif
+
+#include
+#include
+
+#include "muninn_media_wire.h"
+#include "muninn_audio_fec.h"
+#include "muninn_rudp_ack.h"
+#include "muninn_rudp_fragments.h"
+#include "muninn_rudp_url.h"
+#include "muninn_udp_socket.h"
+#include "muninn_video_fec.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include