Route Move light ownership through Muninn

This commit is contained in:
Meta Vens 2026-06-11 07:58:26 +01:00
commit 4d661967ae
6 changed files with 38 additions and 20 deletions

View file

@ -41,15 +41,18 @@ a named invariant that the native runtime cannot protect yet.
schema projection, not pose capture or Mimir fusion. schema projection, not pose capture or Mimir fusion.
- `scripts/start-nightwing-move-tracking.ps1` is the narrow live Nightwing - `scripts/start-nightwing-move-tracking.ps1` is the narrow live Nightwing
bring-up path: it starts the `/eve/periwinkle` receiver/recorder on Starfire, bring-up path: it starts the `/eve/periwinkle` receiver/recorder on Starfire,
stages the Nightwing Eye/Move Python worker, lights configured USB Moves via stages the Nightwing Eye/Move Python worker, keeps a heartbeat for field
HID report `0x06`, and publishes `mimir.psmove_light_state.v1` plus liveness, and publishes `mimir.psmove_light_state.v1` plus
`mimir.move_controller_observation_state.v1` blob observations from both PS3 `mimir.move_controller_observation_state.v1` blob observations from both PS3
Eyes. The blob stream is optical witness evidence for later pose fusion, not Eyes. The blob stream is optical witness evidence for later pose fusion, not
the final 6DoF pose owner. the final 6DoF pose owner.
- `scripts/start-starfire-move-light.ps1` is the Starfire-local USB Move light - Structured PS Move light pulses are Muninn output commands. Mimir publishes
owner. It launches `Mimir.PsMoveProbe` in steady hold mode so the locally `muninn.move_light_command.v1` over CultNet/CultMesh to the Muninn daemon on
attached `VID_054C/PID_03D5` Move sphere stays lit for calibration/tracking the host that owns the USB-attached Move. Muninn writes PS Move HID report
receipts. `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 - Odin's Muninn organ owns Move optical candidate extraction in
`E:\Projects\Odin\crates\muninn-move-tracker` and publishes/feeds `E:\Projects\Odin\crates\muninn-move-tracker` and publishes/feeds
`muninn.move_marker_candidate.v1` records. Mimir consumes those candidate `muninn.move_marker_candidate.v1` records. Mimir consumes those candidate

View file

@ -110,6 +110,11 @@ Authority:
- Starfire-local Move tracking is a local Mimir/Muninn tracking stream. - Starfire-local Move tracking is a local Mimir/Muninn tracking stream.
- Nightwing Move tracking is emitted by Muninn on Nightwing over the typed - Nightwing Move tracking is emitted by Muninn on Nightwing over the typed
observation path. observation path.
- Structured PS Move light pulses are Muninn output commands. Mimir requests
them by sending `muninn.move_light_command.v1` over CultNet/CultMesh to the
Muninn daemon on the host with the USB-attached Move; Mimir does not write
remote HID directly except through temporary smoke scripts used to prove
hardware behavior before Muninn is available on that host.
- Odin discovers and projects the stream/schema surface for operator and agent - Odin discovers and projects the stream/schema surface for operator and agent
access. access.
- Mimir owns subscription, rolling retention, clock alignment, and later fusion - Mimir owns subscription, rolling retention, clock alignment, and later fusion
@ -123,18 +128,19 @@ Move observation into two tracking buffers.
Live Nightwing bring-up uses `scripts/start-nightwing-move-tracking.ps1`. Live Nightwing bring-up uses `scripts/start-nightwing-move-tracking.ps1`.
Starfire starts `Mimir.EveSensorReceiver` on `/eve/periwinkle`, stages Starfire starts `Mimir.EveSensorReceiver` on `/eve/periwinkle`, stages
`nw_eye_cap.py`, `nw_move_hint.py`, and `nightwing_typed_witness_publisher.py` `nw_eye_cap.py`, `nw_move_hint.py`, and `nightwing_typed_witness_publisher.py`
onto Nightwing, and launches the publisher with `--track-eyes` plus explicit onto Nightwing, and launches the publisher with `--track-eyes` plus a heartbeat
`--move-light name=/dev/hidrawN:#rrggbb` HID ownership. The worker keeps the freshness file. The publisher can still accept
USB Move sphere lit with PS Move report `0x06`, emits `--move-light name=/dev/hidrawN:#rrggbb` as an explicit smoke/bootstrap edge,
`mimir.psmove_light_state.v1`, and publishes compact but runtime structured light commands belong to Muninn. The worker publishes
compact
`mimir.move_controller_observation_state.v1` blob observations from both PS3 `mimir.move_controller_observation_state.v1` blob observations from both PS3
Eyes. Those Eye/Move observations are the live optical witness feeding the later Eyes. Those Eye/Move observations are the live optical witness feeding the later
pose/fusion owner; they are not themselves the final 6DoF pose authority. pose/fusion owner; they are not themselves the final 6DoF pose authority.
Starfire-local Move illumination uses `scripts/start-starfire-move-light.ps1`. Starfire-local Move illumination smoke uses `scripts/start-starfire-move-light.ps1`.
That launcher runs `Mimir.PsMoveProbe` against the local Windows HID col01 That launcher runs `Mimir.PsMoveProbe` against the local Windows HID col01
output collection and refreshes the PS Move LED report so the USB-attached Move output collection and refreshes the PS Move LED report for hardware proof when
stays visible as a tracking/calibration marker. Muninn is not available on Starfire yet.
Odin's Muninn organ owns the Move optical extraction stage for sensor stream Odin's Muninn organ owns the Move optical extraction stage for sensor stream
exposure. Its Rust crate lives in `E:\Projects\Odin\crates\muninn-move-tracker` exposure. Its Rust crate lives in `E:\Projects\Odin\crates\muninn-move-tracker`

View file

@ -4,9 +4,7 @@ param(
[string]$StarfireHost = "192.168.1.66", [string]$StarfireHost = "192.168.1.66",
[string]$Path = "/eve/periwinkle", [string]$Path = "/eve/periwinkle",
[string]$EyeDevices = "/dev/video2,/dev/video3", [string]$EyeDevices = "/dev/video2,/dev/video3",
[string[]]$MoveLight = @( [string[]]$MoveLight = @(),
"move-usb=/dev/hidraw1:#35ff6c"
),
[string]$RecorderRoot = "C:\Users\Meta\Videos\Mimir\VerseCaptures", [string]$RecorderRoot = "C:\Users\Meta\Videos\Mimir\VerseCaptures",
[switch]$SkipRecorder, [switch]$SkipRecorder,
[switch]$DryRun [switch]$DryRun
@ -18,6 +16,9 @@ $runId = Get-Date -Format "yyyyMMdd-HHmmss"
$runDir = Join-Path $repo "artifacts\runtime\nightwing-move-tracking-$runId" $runDir = Join-Path $repo "artifacts\runtime\nightwing-move-tracking-$runId"
$witnessUrl = "ws://$StarfireHost`:$Port$Path" $witnessUrl = "ws://$StarfireHost`:$Port$Path"
$subscribeUrl = "ws://127.0.0.1:$Port$Path/subscribe" $subscribeUrl = "ws://127.0.0.1:$Port$Path/subscribe"
$remoteLog = "~/.local/state/gamecult/mimir-nightwing-move-tracking-$runId.log"
$remotePid = "~/.local/state/gamecult/nightwing-typed-witness.pid"
$remoteHeartbeat = "~/.local/state/gamecult/nightwing-typed-witness.heartbeat"
New-Item -ItemType Directory -Force -Path $runDir | Out-Null New-Item -ItemType Directory -Force -Path $runDir | Out-Null
New-Item -ItemType Directory -Force -Path $RecorderRoot | Out-Null New-Item -ItemType Directory -Force -Path $RecorderRoot | Out-Null
@ -126,13 +127,11 @@ if ($DryRun) {
scp (Join-Path $repo "tools\nw_move_hint.py") "${NightwingHost}:/tmp/nw_move_hint.py" | Out-Null scp (Join-Path $repo "tools\nw_move_hint.py") "${NightwingHost}:/tmp/nw_move_hint.py" | Out-Null
scp (Join-Path $repo "tools\nightwing_typed_witness_publisher.py") "${NightwingHost}:/tmp/nightwing_typed_witness_publisher.py" | Out-Null scp (Join-Path $repo "tools\nightwing_typed_witness_publisher.py") "${NightwingHost}:/tmp/nightwing_typed_witness_publisher.py" | Out-Null
$remoteLog = "~/.local/state/gamecult/mimir-nightwing-move-tracking-$runId.log"
$remotePid = "~/.local/state/gamecult/mimir-nightwing-move-tracking-$runId.pid"
$moveArgs = ($MoveLight | ForEach-Object { "--move-light '$($_ -replace "'", "'\''")'" }) -join " " $moveArgs = ($MoveLight | ForEach-Object { "--move-light '$($_ -replace "'", "'\''")'" }) -join " "
$remoteScript = @" $remoteScript = @"
mkdir -p ~/.local/state/gamecult mkdir -p ~/.local/state/gamecult
pkill -f '/tmp/nightwing_typed_witness_publisher.py.*--track-eyes' || true pkill -f '/tmp/nightwing_typed_witness_publisher.py.*--track-eyes' || true
nohup python3 /tmp/nightwing_typed_witness_publisher.py --url '$witnessUrl' --track-eyes --track-builtin-camera --track-builtin-mic --interval 0.10 --eye-devices '$EyeDevices' --eye-window-seconds 0.08 --tracking-stride 6 $moveArgs > $remoteLog 2>&1 < /dev/null & nohup python3 /tmp/nightwing_typed_witness_publisher.py --url '$witnessUrl' --track-eyes --track-builtin-camera --track-builtin-mic --interval 0.10 --heartbeat-path '$remoteHeartbeat' --eye-devices '$EyeDevices' --eye-window-seconds 0.08 --tracking-stride 6 $moveArgs > $remoteLog 2>&1 < /dev/null &
sleep 0.5 sleep 0.5
pgrep -n -f '/tmp/nightwing_typed_witness_publisher.py.*--track-eyes' > $remotePid pgrep -n -f '/tmp/nightwing_typed_witness_publisher.py.*--track-eyes' > $remotePid
cat $remotePid cat $remotePid
@ -150,8 +149,9 @@ $manifest = [ordered]@{
runDir = $runDir runDir = $runDir
witnessUrl = $witnessUrl witnessUrl = $witnessUrl
subscribeUrl = $subscribeUrl subscribeUrl = $subscribeUrl
heartbeatPath = $remoteHeartbeat
eyeDevices = $EyeDevices eyeDevices = $EyeDevices
moveLights = $MoveLight directHidSmokeMoveLights = $MoveLight
startedAt = (Get-Date).ToString("o") startedAt = (Get-Date).ToString("o")
} }
$manifest | ConvertTo-Json -Depth 5 | Set-Content -Path (Join-Path $runDir "supervisor.json") $manifest | ConvertTo-Json -Depth 5 | Set-Content -Path (Join-Path $runDir "supervisor.json")

View file

@ -128,3 +128,4 @@
{"ts": "2026-06-10T18:11:18+01:00", "type": "verification", "status": "ok", "note": "Started the Starfire-local USB Move light owner. Windows sees Sony VID_054C/PID_03D5 with col01 output reports; scripts/start-starfire-move-light.ps1 launched Mimir.PsMoveProbe PID 27344 with --rgb #35ff6c --hold-seconds 0 --refresh-ms 250, and artifacts/runtime/starfire-move-light-20260610-180943/move-light-events.jsonl shows repeated mimir.psmove_usb_pulse_event.v1 hold writes succeeding with ok rgb=#35FF6C bytes=49."} {"ts": "2026-06-10T18:11:18+01:00", "type": "verification", "status": "ok", "note": "Started the Starfire-local USB Move light owner. Windows sees Sony VID_054C/PID_03D5 with col01 output reports; scripts/start-starfire-move-light.ps1 launched Mimir.PsMoveProbe PID 27344 with --rgb #35ff6c --hold-seconds 0 --refresh-ms 250, and artifacts/runtime/starfire-move-light-20260610-180943/move-light-events.jsonl shows repeated mimir.psmove_usb_pulse_event.v1 hold writes succeeding with ok rgb=#35FF6C bytes=49."}
{"ts": "2026-06-10T20:33:54+01:00", "type": "implementation", "status": "ok", "note": "Added native/move_tracker as Rust-orchestrated PS Move optical candidate extraction: Rust owns dispatch/FFI/CPU mirror; HLSL owns 16px-tile luma reduction. Scope is marker candidates only, before calibration/triangulation/IMU fusion/prediction."} {"ts": "2026-06-10T20:33:54+01:00", "type": "implementation", "status": "ok", "note": "Added native/move_tracker as Rust-orchestrated PS Move optical candidate extraction: Rust owns dispatch/FFI/CPU mirror; HLSL owns 16px-tile luma reduction. Scope is marker candidates only, before calibration/triangulation/IMU fusion/prediction."}
{"ts": "2026-06-10T20:49:06+01:00", "type": "decision", "status": "ok", "note": "Corrected Move optical candidate ownership: the Rust/HLSL tracker moved out of Mimir and into Odin's Muninn organ at E:\\Projects\\Odin\\crates\\muninn-move-tracker. Mimir consumes muninn.move_marker_candidate.v1 streams and no longer owns raw optical extraction."} {"ts": "2026-06-10T20:49:06+01:00", "type": "decision", "status": "ok", "note": "Corrected Move optical candidate ownership: the Rust/HLSL tracker moved out of Mimir and into Odin's Muninn organ at E:\\Projects\\Odin\\crates\\muninn-move-tracker. Mimir consumes muninn.move_marker_candidate.v1 streams and no longer owns raw optical extraction."}
{"ts": "2026-06-11T07:57:05+01:00", "type": "decision", "status": "ok", "note": "PS Move structured light ownership moved to Odin/Muninn: Mimir requests muninn.move_light_command.v1 over CultNet/CultMesh; Muninn serve consumes host-addressed pending commands, writes local HID report 0x06, and updates command state. Nightwing direct HID launcher path is opt-in smoke/bootstrap only."}

View file

@ -69,6 +69,10 @@ invariants:
- Move tracking observations are stream samples, not program controls or - Move tracking observations are stream samples, not program controls or
dashboard truth. Starfire-local and Nightwing-remote Move samples must enter dashboard truth. Starfire-local and Nightwing-remote Move samples must enter
Mimir.Runtime as typed tracking buffers before Fensalir or Eve acts on them. Mimir.Runtime as typed tracking buffers before Fensalir or Eve acts on them.
- Structured PS Move light pulses are Muninn output commands. Mimir requests
`muninn.move_light_command.v1` over CultNet/CultMesh from the Muninn daemon
on the host with the USB-attached Move; direct HID writes in Mimir scripts
are temporary smoke/bootstrap edges only.
accepted_design: accepted_design:
- `Mimir.slnx` owns the app surface. - `Mimir.slnx` owns the app surface.

View file

@ -447,6 +447,9 @@ def run(args: argparse.Namespace) -> None:
if args.track_eyes: if args.track_eyes:
for payload in eye_tracking_observations(args, sequence): for payload in eye_tracking_observations(args, sequence):
ws.send_binary(payload) ws.send_binary(payload)
if args.heartbeat_path:
with open(args.heartbeat_path, "w", encoding="utf-8") as handle:
handle.write(f"{int(time.time())} {sequence}\n")
sequence += 1 sequence += 1
if args.once: if args.once:
return return
@ -464,6 +467,7 @@ def main() -> int:
parser.add_argument("--interval", type=float, default=0.25) parser.add_argument("--interval", type=float, default=0.25)
parser.add_argument("--reconnect-seconds", type=float, default=2.0) parser.add_argument("--reconnect-seconds", type=float, default=2.0)
parser.add_argument("--once", action="store_true") parser.add_argument("--once", action="store_true")
parser.add_argument("--heartbeat-path", default="", help="Write a small freshness witness after successful sends.")
parser.add_argument("--track-eyes", action="store_true", help="Publish compact Nightwing-local PS3 Eye Move-sphere observations.") parser.add_argument("--track-eyes", action="store_true", help="Publish compact Nightwing-local PS3 Eye Move-sphere observations.")
parser.add_argument("--track-builtin-camera", action="store_true", help="Publish compact status for Nightwing-local non-Eye camera devices.") parser.add_argument("--track-builtin-camera", action="store_true", help="Publish compact status for Nightwing-local non-Eye camera devices.")
parser.add_argument("--track-builtin-mic", action="store_true", help="Publish compact ALSA microphone level observations.") parser.add_argument("--track-builtin-mic", action="store_true", help="Publish compact ALSA microphone level observations.")