From d27f0d2d37bd95d9969d6c3fa6840ad0acbb438a Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Mon, 13 Jul 2026 02:39:41 +0100 Subject: [PATCH] Gate Move stereo calibration on observable evidence --- .../muninn-same-frame-correspondence.mpack | Bin 0 -> 341181 bytes .../muninn-same-frame-stereo-assessment.mpack | Bin 0 -> 551 bytes notes/current-system-map.md | 7 + src/Mimir.BufferSmoke/Program.cs | 90 +++++++++++- .../MimirMoveStereoCalibrationAssessment.cs | 139 ++++++++++++++++++ state/evidence.jsonl | 1 + state/map.yaml | 8 + 7 files changed, 237 insertions(+), 8 deletions(-) create mode 100644 artifacts/move-calibration/muninn-same-frame-correspondence.mpack create mode 100644 artifacts/move-calibration/muninn-same-frame-stereo-assessment.mpack create mode 100644 src/Mimir.Runtime/Synchronization/MimirMoveStereoCalibrationAssessment.cs 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 0000000000000000000000000000000000000000..e70c40c1492db939df2b97e43626cfa94507b861 GIT binary patch literal 341181 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�&2$_CS|+>asdZ`xPSp*T)+VkE(lON-c)H$2-6D$ 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..4de6fbf2cfe5f26019740fc841b1ad349d670b74 GIT binary patch literal 551 zcma)(KT88K7{*KK=par`VsR2lx%28(J7~Ux?rnOPUW0duBx%bzwSELaPzNbEIf#=Y zF8*0#(e=Ph5FDL#@V#2A)WtVLe!O{}C*jRgW-?ibSj1!@5-nO$no24v9tl}Z63qm! z5T&S65#_qjvXj+RhQ4Iz=csI#mYo&Lb&IxBELnpZ^_E=69rmt&-PK}>)mX@MTxm1z zi#FsxOy8cLOuctA_dDszn#{09uzPsMS6hhXJBtB~@6j2zxO6G}1AslPu zSQb(-_Zm`gwhdxWNJ-U};6CLZRY`Gd4rLF%Mv{P{QHF~|9odU0kPQ4Z4F9<7f4M#Z DrzFGQ literal 0 HcmV?d00001 diff --git a/notes/current-system-map.md b/notes/current-system-map.md index 4065333..c22b776 100644 --- a/notes/current-system-map.md +++ b/notes/current-system-map.md @@ -215,6 +215,13 @@ both eyes. The same parent PID survived the run; transport health reported 5,302 produced frames, 5,301 handoffs, 771 sends, and zero local-ring admissions. Full stereo calibration is still not implied: intrinsics and spatially diverse correspondences must earn the camera matrices. +`MimirMoveStereoCalibrationAssessment` is the typed observability gate before +any fit. It accepts only same aggregate-frame, same-Move correspondences and +reports synchronized pair count, Move diversity, per-Eye 4x3 grid coverage, +radius range, and missing physical inputs. The corrected live capture produced +130 same-frame pairs with zero frame skew, but only 4/12 Eye 0 cells and 2/12 +Eye 1 cells; intrinsics and measured orb radius are also absent, so promotion is +correctly false. `MimirMoveProofSurface` is the first Fensalir-visible proof surface for this chain. It consumes the Mimir admission receipt and the Mimir-owned pose stream frame, emits `mimir.move_proof_surface.v1`, and lowers an observer-only diff --git a/src/Mimir.BufferSmoke/Program.cs b/src/Mimir.BufferSmoke/Program.cs index a09172a..a48c4c4 100644 --- a/src/Mimir.BufferSmoke/Program.cs +++ b/src/Mimir.BufferSmoke/Program.cs @@ -154,6 +154,17 @@ if (args.Any(arg => string.Equals(arg, "--muninn-move-visibility-window-smoke", ParseStringOption(args, "--output", "artifacts/move-calibration/muninn-visibility-window.mpack")); } +if (args.Any(arg => string.Equals(arg, "--move-stereo-calibration-assessment", StringComparison.OrdinalIgnoreCase))) +{ + return RunMoveStereoCalibrationAssessment( + ParseStringOption(args, "--input", "artifacts/move-calibration/muninn-clocked-aggregator.mpack"), + ParseStringOption(args, "--output", "artifacts/move-calibration/stereo-assessment.mpack"), + ParseIntOption(args, "--image-width", 640), + ParseIntOption(args, "--image-height", 480), + args.Any(arg => string.Equals(arg, "--intrinsics-available", StringComparison.OrdinalIgnoreCase)), + ParseDoubleOption(args, "--orb-radius-meters", 0.0)); +} + if (args.Any(arg => string.Equals(arg, "--move-fusion-smoke", StringComparison.OrdinalIgnoreCase))) { return RunMoveFusionSmoke(); @@ -2071,16 +2082,20 @@ static int RunMuninnMoveVisibilityWindowSmoke(string odinCultMeshUri, int durati } var correspondences = observations - .GroupBy(observation => observation.MoveId, StringComparer.Ordinal) - .SelectMany(group => + .GroupBy(observation => (observation.FrameId, observation.MoveId)) + .Select(group => { - var first = group.Where(value => value.CameraId == cameraIds[0]).ToArray(); - var second = group.Where(value => value.CameraId == cameraIds[1]).ToArray(); - return first.Select(left => second - .Select(right => new MoveCrossCameraCorrespondence(left.MoveId, left, right, Math.Abs(left.PublishedAtNs - right.PublishedAtNs))) - .OrderBy(pair => pair.AbsoluteSkewNs).FirstOrDefault()) - .Where(pair => pair is not null)!; + var first = group.FirstOrDefault(value => value.CameraId == cameraIds[0]); + var second = group.FirstOrDefault(value => value.CameraId == cameraIds[1]); + return first is null || second is null + ? null + : new MoveCrossCameraCorrespondence( + group.Key.MoveId, + first, + second, + Math.Abs(first.PublishedAtNs - second.PublishedAtNs)); }) + .Where(pair => pair is not null) .Cast() .ToArray(); var receipt = new MoveVisibilityWindowReceipt( @@ -2115,6 +2130,65 @@ static int RunMuninnMoveVisibilityWindowSmoke(string odinCultMeshUri, int durati } } +static int RunMoveStereoCalibrationAssessment( + string inputPath, + string outputPath, + int imageWidth, + int imageHeight, + bool intrinsicsAvailable, + double orbRadiusMeters) +{ + if (!File.Exists(inputPath)) + { + Console.Error.WriteLine($"move-stereo-calibration-assessment missing-input path={inputPath}"); + return 1; + } + + var receipt = MessagePackSerializer.Deserialize(File.ReadAllBytes(inputPath)); + var cameraIds = receipt.Observations + .Select(value => value.CameraId) + .Distinct(StringComparer.Ordinal) + .Order(StringComparer.Ordinal) + .ToArray(); + var assessment = MimirMoveStereoCalibrationAssessment.Assess( + receipt, + imageWidth, + imageHeight, + cameraIds, + intrinsicsAvailable, + orbRadiusMeters); + Directory.CreateDirectory(Path.GetDirectoryName(Path.GetFullPath(outputPath))!); + File.WriteAllBytes(outputPath, MessagePackSerializer.Serialize(assessment)); + Console.WriteLine(JsonSerializer.Serialize(new + { + assessment.Schema, + inputPath, + outputPath, + assessment.CorrespondenceCount, + assessment.SynchronizedCorrespondenceCount, + assessment.SameFrameCorrespondenceCount, + assessment.DistinctMoveCount, + assessment.MedianAbsoluteSkewMilliseconds, + assessment.MaximumAbsoluteSkewMilliseconds, + assessment.IntrinsicsAvailable, + assessment.OrbRadiusMetersAvailable, + assessment.ReadyForRelativePoseFit, + assessment.Promoted, + assessment.MissingRequirements, + cameras = assessment.Cameras.Select(camera => new + { + camera.CameraId, + camera.ObservationCount, + camera.OccupiedGridCells, + camera.GridCellCount, + spanX = camera.MaximumXPx - camera.MinimumXPx, + spanY = camera.MaximumYPx - camera.MinimumYPx, + radiusRatio = camera.MinimumRadiusPx > 0.0 ? camera.MaximumRadiusPx / camera.MinimumRadiusPx : double.NaN + }) + })); + return assessment.Promoted ? 0 : 2; +} + static int RunMuninnMoveCultMeshStreamSmoke(string nativeReservoirPath) { if (!File.Exists(nativeReservoirPath)) diff --git a/src/Mimir.Runtime/Synchronization/MimirMoveStereoCalibrationAssessment.cs b/src/Mimir.Runtime/Synchronization/MimirMoveStereoCalibrationAssessment.cs new file mode 100644 index 0000000..e43bd03 --- /dev/null +++ b/src/Mimir.Runtime/Synchronization/MimirMoveStereoCalibrationAssessment.cs @@ -0,0 +1,139 @@ +using GameCult.Caching; +using MessagePack; + +namespace Mimir.Runtime.Synchronization; + +[MessagePackObject] +public sealed record MimirMoveCameraCoverageAssessment( + [property: Key(0)] string CameraId, + [property: Key(1)] int ObservationCount, + [property: Key(2)] double MinimumXPx, + [property: Key(3)] double MaximumXPx, + [property: Key(4)] double MinimumYPx, + [property: Key(5)] double MaximumYPx, + [property: Key(6)] int OccupiedGridCells, + [property: Key(7)] int GridCellCount, + [property: Key(8)] double MinimumRadiusPx, + [property: Key(9)] double MaximumRadiusPx); + +[CultDocument("mimir.move_stereo_calibration_assessment", "mimir.move_stereo_calibration_assessment.v1")] +[MessagePackObject] +public sealed record MimirMoveStereoCalibrationAssessmentDocument( + [property: Key(0)] string Schema, + [property: Key(1)] [property: CultName] string AssessmentId, + [property: Key(2)] string SourceSchema, + [property: Key(3)] long AssessedAtNs, + [property: Key(4)] int CorrespondenceCount, + [property: Key(5)] int SynchronizedCorrespondenceCount, + [property: Key(6)] int SameFrameCorrespondenceCount, + [property: Key(7)] int DistinctMoveCount, + [property: Key(8)] double MedianAbsoluteSkewMilliseconds, + [property: Key(9)] double MaximumAbsoluteSkewMilliseconds, + [property: Key(10)] MimirMoveCameraCoverageAssessment[] Cameras, + [property: Key(11)] bool IntrinsicsAvailable, + [property: Key(12)] bool OrbRadiusMetersAvailable, + [property: Key(13)] bool ReadyForRelativePoseFit, + [property: Key(14)] bool Promoted, + [property: Key(15)] string[] MissingRequirements); + +public static class MimirMoveStereoCalibrationAssessment +{ + public const string Schema = "mimir.move_stereo_calibration_assessment.v1"; + + public static MimirMoveStereoCalibrationAssessmentDocument Assess( + MoveVisibilityWindowReceipt receipt, + int imageWidth, + int imageHeight, + IReadOnlyCollection cameraIds, + bool intrinsicsAvailable, + double orbRadiusMeters, + double maximumAssociationSkewMilliseconds = 20.0, + DateTimeOffset? assessedAt = null) + { + ArgumentNullException.ThrowIfNull(receipt); + ArgumentNullException.ThrowIfNull(cameraIds); + if (imageWidth <= 0 || imageHeight <= 0) throw new ArgumentOutOfRangeException(nameof(imageWidth)); + if (cameraIds.Count != 2) throw new ArgumentException("Stereo calibration requires exactly two camera IDs.", nameof(cameraIds)); + + var cameras = cameraIds.Distinct(StringComparer.Ordinal).ToArray(); + if (cameras.Length != 2) throw new ArgumentException("Stereo camera IDs must be distinct.", nameof(cameraIds)); + var maximumSkewNs = maximumAssociationSkewMilliseconds * 1_000_000.0; + var correspondences = receipt.Correspondences + .Where(value => cameras.Contains(value.First.CameraId, StringComparer.Ordinal) && + cameras.Contains(value.Second.CameraId, StringComparer.Ordinal)) + .ToArray(); + var synchronized = correspondences.Where(value => value.AbsoluteSkewNs <= maximumSkewNs).ToArray(); + var sameFrame = synchronized.Where(value => string.Equals(value.First.FrameId, value.Second.FrameId, StringComparison.Ordinal)).ToArray(); + var coverage = cameras.Select(cameraId => BuildCoverage( + cameraId, + receipt.Observations.Where(value => string.Equals(value.CameraId, cameraId, StringComparison.Ordinal)).ToArray(), + imageWidth, + imageHeight)).ToArray(); + var missing = new List(); + if (!intrinsicsAvailable) missing.Add("calibrated-intrinsics-for-both-eyes"); + if (!(double.IsFinite(orbRadiusMeters) && orbRadiusMeters > 0.0)) missing.Add("measured-orb-radius-meters"); + if (synchronized.Length < 300) missing.Add("at-least-300-synchronized-correspondences"); + if (sameFrame.Length < 100) missing.Add("at-least-100-same-frame-correspondences"); + if (synchronized.Select(value => value.MoveId).Distinct(StringComparer.Ordinal).Count() < 2) missing.Add("at-least-two-stable-move-identities"); + foreach (var camera in coverage) + { + if (camera.OccupiedGridCells < 6) missing.Add($"{camera.CameraId}:coverage-at-least-6-of-12-grid-cells"); + if (camera.MaximumRadiusPx <= camera.MinimumRadiusPx * 1.25) missing.Add($"{camera.CameraId}:radius-range-at-least-1.25x"); + } + + var skews = correspondences.Select(value => value.AbsoluteSkewNs / 1_000_000.0).Order().ToArray(); + var ready = missing.Count == 0; + return new MimirMoveStereoCalibrationAssessmentDocument( + Schema, + $"move-stereo-assessment:{receipt.StartedAtNs}:{receipt.EndedAtNs}", + receipt.Schema, + (assessedAt ?? DateTimeOffset.UtcNow).ToUnixTimeMilliseconds() * 1_000_000, + correspondences.Length, + synchronized.Length, + sameFrame.Length, + synchronized.Select(value => value.MoveId).Distinct(StringComparer.Ordinal).Count(), + Median(skews), + skews.Length == 0 ? double.NaN : skews[^1], + coverage, + intrinsicsAvailable, + double.IsFinite(orbRadiusMeters) && orbRadiusMeters > 0.0, + ready, + Promoted: false, + missing.Distinct(StringComparer.Ordinal).ToArray()); + } + + private static MimirMoveCameraCoverageAssessment BuildCoverage( + string cameraId, + MoveVisibilityObservation[] observations, + int imageWidth, + int imageHeight) + { + var valid = observations.Where(value => + float.IsFinite(value.CenterXPx) && float.IsFinite(value.CenterYPx) && + float.IsFinite(value.RadiusPx) && value.RadiusPx > 0.0f).ToArray(); + var cells = valid.Select(value => + { + var column = Math.Clamp((int)(value.CenterXPx * 4.0 / imageWidth), 0, 3); + var row = Math.Clamp((int)(value.CenterYPx * 3.0 / imageHeight), 0, 2); + return row * 4 + column; + }).Distinct().Count(); + return new MimirMoveCameraCoverageAssessment( + cameraId, + valid.Length, + valid.Length == 0 ? double.NaN : valid.Min(value => value.CenterXPx), + valid.Length == 0 ? double.NaN : valid.Max(value => value.CenterXPx), + valid.Length == 0 ? double.NaN : valid.Min(value => value.CenterYPx), + valid.Length == 0 ? double.NaN : valid.Max(value => value.CenterYPx), + cells, + 12, + valid.Length == 0 ? double.NaN : valid.Min(value => value.RadiusPx), + valid.Length == 0 ? double.NaN : valid.Max(value => value.RadiusPx)); + } + + private static double Median(double[] sorted) => sorted.Length switch + { + 0 => double.NaN, + _ when sorted.Length % 2 == 1 => sorted[sorted.Length / 2], + _ => (sorted[sorted.Length / 2 - 1] + sorted[sorted.Length / 2]) * 0.5 + }; +} diff --git a/state/evidence.jsonl b/state/evidence.jsonl index cecedf0..53aca53 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -184,3 +184,4 @@ {"ts": "2026-07-13T01:29:15+01:00", "type": "verification", "status": "partial", "note": "Per-camera optical evidence and exposure ownership restored Eye 0. At exposure 0.3 Eye 0 peak RGB rose from [72,25,43] to [178,119,112], calibrated 4 Moves, and produced 3,312 observations/latest 4; Eye 1 remains at exposure 0.1. Mimir's persistent window then saw all 4 IDs on Eye 0, 2 on Eye 1, and 2 same-ID cross-camera correspondences. Do not claim full stereo: Eye 1 overlap is incomplete and only 2 aggregate RUDP frames arrived in 20s and 60s windows."} {"ts": "2026-07-13T01:41:29+01:00", "type": "verification", "status": "partial", "note": "After per-eye exposure tuning and latest-state RUDP handoff, Mimir's 15s dual-eye receipt saw all four stable Move IDs on both cameras: 16 camera observations and 8 pair records across 2 aggregate frames. Full overlap is proven; calibration is not. Only 2 unique frame IDs arrived, so directly instrument parent publication and RUDP send/receive counters before camera-matrix fitting."} {"ts": "2026-07-13T02:32:31+01:00", "type": "verification", "status": "ok", "note": "2026-07-13 Nightwing single-provider dual-Eye field proof: one stable Muninn parent received 622 unique evidence frames in 15 s through Mimir, with 2,647 observations, 1,384 same-ID cross-camera correspondences, and all four Move IDs visible on both Eyes. Producer transport health reported 5,302 produced, 5,301 handoffs, 771 sends, and zero local-ring admissions. Artifact: artifacts/move-calibration/muninn-clocked-aggregator.mpack. Odin owner commits: 39c839a, a340c33, 8aefd1c, f82ca7a."} +{"ts": "2026-07-13T02:39:32+01:00", "type": "verification", "status": "rejected", "note": "2026-07-13 typed Move stereo assessment rejected calibration promotion. Corrected same-frame collector captured 798 frames and 130 same-frame/same-Move pairs at zero published-frame skew. Eye 0 covered 4/12 image grid cells; Eye 1 covered 2/12 and saw only two Move IDs in this window. Both calibrated intrinsics and measured orb radius were absent. No camera matrices were emitted. Artifacts: artifacts/move-calibration/muninn-same-frame-correspondence.mpack and muninn-same-frame-stereo-assessment.mpack."} diff --git a/state/map.yaml b/state/map.yaml index 9869434..1ef68ed 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -267,6 +267,14 @@ accepted_design: remote handoffs, 771 sends during the subscription, and zero local-ring admissions. Cadence and full overlap are earned; calibrated camera matrices still require intrinsics and spatially diverse correspondence capture. + - `MimirMoveStereoCalibrationAssessment` owns the pre-fit observability gate + as `mimir.move_stereo_calibration_assessment.v1`. It consumes a typed + visibility receipt, counts only same-frame/same-Move stereo pairs, measures + per-Eye image-grid and radius coverage, and refuses promotion when + intrinsics, measured orb radius, synchronized sample count, or spatial + diversity are missing. The corrected 2026-07-13 live receipt contained 130 + honest same-frame pairs at zero frame skew, but Eye 0 occupied only 4/12 + grid cells and Eye 1 only 2/12; no camera matrices were promoted. - `MimirMuninnMoveEvidenceAdapter.TryAdmitLatestCultMeshFrame` now exposes a typed admission receipt with decoded frame id, producer, publish/read timestamps, sample counts by kind, source/arrival ranges, native reservoir