diff --git a/docs/fensalir-rendering-rebuild-migration.md b/docs/fensalir-rendering-rebuild-migration.md index a111309..ac9fa2d 100644 --- a/docs/fensalir-rendering-rebuild-migration.md +++ b/docs/fensalir-rendering-rebuild-migration.md @@ -426,12 +426,13 @@ Let Fensalir fuse Mimir observations into field claims: Acceptance: -- one synthetic multi-camera marker creates a stable world-space claim; +- implemented synthetic multi-camera marker proof: Mimir lowers a deterministic + camera marker candidate with calibration/marker identity and a world-space + confidence envelope, and Fensalir plans it as a `DebugOverlay` FieldEvidence + packet; - implemented synthetic audio source proof: Mimir lowers an SRP/PHAT-style source candidate with calibration/source identity and a world-space confidence envelope, and Fensalir plans it as a `DebugOverlay` FieldEvidence packet; -- next remaining acceptance item: one synthetic multi-camera marker creates a - stable world-space claim; - claims can be inspected by source observation and calibration id. ### Phase 6: Program Output Receipts diff --git a/docs/implementation-plan.md b/docs/implementation-plan.md index 0a46b50..3de281e 100644 --- a/docs/implementation-plan.md +++ b/docs/implementation-plan.md @@ -274,6 +274,9 @@ a named invariant that the native runtime cannot protect yet. Phase 5 synthetic audio-source candidate: an SRP/PHAT-style localization result is lowered with calibration/source identity and a world-space confidence envelope, then planned as a FieldEvidence `DebugOverlay` packet. + It also proves a synthetic multi-camera marker candidate using the same + FieldEvidence authority: deterministic marker features plan as + `DebugOverlay` packets while ambiguous raw camera features still defer. - Fensalir now owns the first in-process D3D12 field resource resolver cut: shared structured/curve buffer resources import/alias GPU-resident handles, and Fensalir-owned resources allocate GPU slots only when Fensalir is the diff --git a/notes/current-system-map.md b/notes/current-system-map.md index 4e0d29e..2ae8bc5 100644 --- a/notes/current-system-map.md +++ b/notes/current-system-map.md @@ -467,7 +467,10 @@ Mimir lowers an SRP/PHAT-style localized source with calibration/source identity and a world-space confidence envelope, and Fensalir plans it as a FieldEvidence `DebugOverlay` packet. Live room geometry must still come from an explicit measured coordinate frame, not inferred from the recent two-distance -monitor notes. +monitor notes. Phase 5's synthetic marker proof now exists too: Mimir lowers a +deterministic multi-camera marker candidate with calibration/marker identity and +world-space support, and Fensalir plans it as `DebugOverlay` evidence while +ambiguous raw camera features still defer. The current teardown/migration map is `docs/fensalir-rendering-rebuild-migration.md`, paired with Fensalir's diff --git a/src/Mimir.BufferSmoke/Program.cs b/src/Mimir.BufferSmoke/Program.cs index eccff97..4d57a5b 100644 --- a/src/Mimir.BufferSmoke/Program.cs +++ b/src/Mimir.BufferSmoke/Program.cs @@ -2192,6 +2192,20 @@ static int RunPerfectMachineProfileSmoke() 1_000_000_000L) ]); var acousticSourcePlan = AquariumFieldLoweringPlanner.Plan(acousticSourceFrame); + var visualMarkerFrame = lowerer.BuildVisualMarkerCandidateFrame( + [ + new MimirVisualMarkerFieldCandidate( + "marker-a", + "synthetic-camera-rig-calibration", + "synthetic-marker-a", + "mimir-multi-camera-marker", + ["kiyo-pro-rgb:1", "leap-stereo-ir:1"], + new Vector3(0.0f, 1.2f, 2.5f), + 0.05, + 0.88, + 1_000_000_000L) + ]); + var visualMarkerPlan = AquariumFieldLoweringPlanner.Plan(visualMarkerFrame); var authority = new MimirAuthorityPolicyEvaluator().Evaluate(new MimirAuthorityEvaluationInput( "raven-scarlett-witness", new HashSet(["known-node", "matching-codebook", "clock-fit-confidence", "response-profile"], StringComparer.Ordinal), @@ -2218,6 +2232,8 @@ static int RunPerfectMachineProfileSmoke() $"perfect-machine-fensalir cameraResources={cameraFieldFrame.Resources.Count} cameraClaims={cameraFieldFrame.Claims.Count} cameraPlanned={cameraPlan.Packets.Count} cameraDeferred={cameraPlan.DeferredRequests.Count} acousticClaims={acousticFieldEvidence.Claims.Count} acousticPlanned={acousticPlan.Packets.Count} acousticDeferred={acousticPlan.DeferredRequests.Count}"); Console.WriteLine( $"perfect-machine-localization candidates={localizationGrid.Count} best=({localization?.PositionMeters.X ?? float.NaN:0.000},{localization?.PositionMeters.Y ?? float.NaN:0.000},{localization?.PositionMeters.Z ?? float.NaN:0.000}) score={localization?.Score ?? 0.0:0.000} sourceClaims={acousticSourceFrame.Claims.Count} sourcePlanned={acousticSourcePlan.Packets.Count} sourceDeferred={acousticSourcePlan.DeferredRequests.Count}"); + Console.WriteLine( + $"perfect-machine-visual-marker claims={visualMarkerFrame.Claims.Count} planned={visualMarkerPlan.Packets.Count} deferred={visualMarkerPlan.DeferredRequests.Count}"); Console.WriteLine( $"perfect-machine-authority appliesTo=raven-scarlett-witness decision={authority.Decision} rule={authority.RuleId}"); Console.WriteLine( @@ -2257,6 +2273,11 @@ static int RunPerfectMachineProfileSmoke() acousticSourcePlan.DeferredRequests.Count == 0 && acousticSourceFrame.Claims[0].ClaimKey.Contains("synthetic-tdoa-calibration", StringComparison.Ordinal) && acousticSourceFrame.Claims[0].ClaimKey.Contains("synthetic-audio-source", StringComparison.Ordinal) && + visualMarkerFrame.Claims.Count == 1 && + visualMarkerPlan.Packets.Count == 1 && + visualMarkerPlan.DeferredRequests.Count == 0 && + visualMarkerFrame.Claims[0].ClaimKey.Contains("synthetic-camera-rig-calibration", StringComparison.Ordinal) && + visualMarkerFrame.Claims[0].ClaimKey.Contains("synthetic-marker-a", StringComparison.Ordinal) && localization is { Score: > 0.90 } && authority.Decision == MimirAuthorityDecision.TrustedEvidence && transport.Transport?.Id == MimirNetworkTransportConfigurations.CultMeshTimingState.Id && diff --git a/src/Mimir.Runtime/Synchronization/MimirFensalirBridgeContracts.cs b/src/Mimir.Runtime/Synchronization/MimirFensalirBridgeContracts.cs index 7002b39..0328661 100644 --- a/src/Mimir.Runtime/Synchronization/MimirFensalirBridgeContracts.cs +++ b/src/Mimir.Runtime/Synchronization/MimirFensalirBridgeContracts.cs @@ -150,6 +150,17 @@ public sealed record MimirAcousticSourceFieldCandidate( double Confidence, long ObservedTimeNs); +public sealed record MimirVisualMarkerFieldCandidate( + string CandidateKey, + string CalibrationId, + string MarkerId, + string ProducerKey, + IReadOnlyList SourceObservationKeys, + System.Numerics.Vector3 PositionMeters, + double RadiusMeters, + double Confidence, + long ObservedTimeNs); + public readonly record struct MimirSurfaceAxes( string X, string Y, diff --git a/src/Mimir.Runtime/Synchronization/MimirFensalirFieldLowering.cs b/src/Mimir.Runtime/Synchronization/MimirFensalirFieldLowering.cs index 62a54a0..e8b557b 100644 --- a/src/Mimir.Runtime/Synchronization/MimirFensalirFieldLowering.cs +++ b/src/Mimir.Runtime/Synchronization/MimirFensalirFieldLowering.cs @@ -147,6 +147,39 @@ public sealed class MimirFensalirFieldLowering(MimirFensalirLoweringOptions? opt }; } + public AquariumFieldEvidenceFrame BuildVisualMarkerCandidateFrame( + IEnumerable markerCandidates) + { + var domains = new List(); + var claims = new List(); + var candidates = new List(); + var seenDomains = new HashSet(StringComparer.Ordinal); + + foreach (var markerCandidate in markerCandidates) + { + if (string.IsNullOrWhiteSpace(markerCandidate.CandidateKey)) + { + continue; + } + + var domain = DomainForVisualMarkerCandidate(markerCandidate); + AddDomain(domains, seenDomains, domain); + + var claim = ClaimForVisualMarkerCandidate(markerCandidate, domain.DomainKey); + claims.Add(claim); + candidates.Add(CandidateForClaim(claim, AquariumFieldGuide.Valid(claim.Confidence))); + } + + return new AquariumFieldEvidenceFrame + { + Domains = domains, + Claims = claims, + Candidates = candidates, + AccumulationWindowSeconds = (float)options.AccumulationWindowSeconds, + PresentationDelaySeconds = (float)options.PresentationDelaySeconds + }; + } + private static void AddDomain( ICollection domains, ISet seenDomains, @@ -304,6 +337,23 @@ public sealed class MimirFensalirFieldLowering(MimirFensalirLoweringOptions? opt "Mimir.Runtime"); } + private static AquariumFieldDomain DomainForVisualMarkerCandidate(MimirVisualMarkerFieldCandidate candidate) + { + var radius = Math.Max(0.001f, (float)candidate.RadiusMeters); + var center = candidate.PositionMeters; + var boundsRadius = new Vector3(radius); + return new AquariumFieldDomain( + DomainKeyForVisualMarkerCandidate(candidate), + "", + AquariumFieldDomainKind.CameraSensor, + Matrix4x4.Identity, + Matrix4x4.Identity, + center - boundsRadius, + center + boundsRadius, + Vector3.Zero, + "Mimir.Runtime"); + } + private static AquariumFieldDomain DomainForSurfaceIntent(MimirSurfaceIntent intent) => new( DomainKeyForSurfaceIntent(intent.IntentKey), @@ -387,6 +437,31 @@ public sealed class MimirFensalirFieldLowering(MimirFensalirLoweringOptions? opt Confidence: confidence); } + private AquariumFieldClaim ClaimForVisualMarkerCandidate( + MimirVisualMarkerFieldCandidate markerCandidate, + string domainKey) + { + var confidence = Clamp01((float)markerCandidate.Confidence); + var proposal = new AquariumFieldProposalPolicy( + AquariumFieldProposalKind.DeterministicStructural, + SourcePdf: 1.0f, + TargetContribution: confidence, + RepresentedCandidateCount: Math.Max(1, markerCandidate.SourceObservationKeys.Count), + Seed: StableSeed(markerCandidate.CandidateKey)); + + return new AquariumFieldClaim( + ClaimKey: $"visual-marker:{markerCandidate.CalibrationId}:{markerCandidate.MarkerId}:{markerCandidate.CandidateKey}", + DomainKey: domainKey, + ProducerKey: markerCandidate.ProducerKey, + Layer: AquariumFieldLayer.Form, + Encoding: AquariumFieldEncoding.Feature, + Support: SupportForVisualMarkerCandidate(markerCandidate), + Proposal: proposal, + PayloadHandle: markerCandidate.CalibrationId, + ObservedTimeNs: markerCandidate.ObservedTimeNs, + Confidence: confidence); + } + private AquariumFieldClaim ClaimForSurfaceIntent( MimirSurfaceIntent intent, string domainKey, @@ -515,6 +590,19 @@ public sealed class MimirFensalirFieldLowering(MimirFensalirLoweringOptions? opt TemporalUncertainty: 0.0f); } + private AquariumFieldSupport SupportForVisualMarkerCandidate(MimirVisualMarkerFieldCandidate markerCandidate) + { + var radius = Math.Max(options.DefaultSupportRadius, (float)markerCandidate.RadiusMeters); + return new AquariumFieldSupport( + Center: markerCandidate.PositionMeters, + Radius: new Vector3(radius), + LocalFrame: Matrix4x4.Identity, + ConservativeRadius: radius, + ProjectedError: radius, + Curvature: 0.0f, + TemporalUncertainty: 0.0f); + } + private AquariumFieldSupport SupportForSurfaceIntent(MimirSurfaceIntent intent) { var age = Math.Max(0.0f, (float)intent.SupportPolicy.MaximumAgeSeconds); @@ -681,6 +769,9 @@ public sealed class MimirFensalirFieldLowering(MimirFensalirLoweringOptions? opt private static string DomainKeyForAcousticSourceCandidate(MimirAcousticSourceFieldCandidate candidate) => $"mimir:acoustic-source:{candidate.CalibrationId}:{candidate.SourceId}:{candidate.CandidateKey}"; + private static string DomainKeyForVisualMarkerCandidate(MimirVisualMarkerFieldCandidate candidate) => + $"mimir:visual-marker:{candidate.CalibrationId}:{candidate.MarkerId}:{candidate.CandidateKey}"; + private static string DomainKeyForSurfaceIntent(string intentKey) => $"mimir:intent:{intentKey}"; private static float Clamp01(float value) => Math.Clamp(value, 0.0f, 1.0f); diff --git a/state/evidence.jsonl b/state/evidence.jsonl index 52bb47d..5fdd2a4 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -206,3 +206,4 @@ {"ts": "2026-05-28T08:20:48+01:00", "type": "verification", "status": "blocked", "note": "ASIO monitor split proof 20260528-081813 added --play-output-channel and captured output 0/output 1 separately. Loopback channels confirmed distinct ASIO output buffers, but physical mic delays were nearly identical across outputs (shotgun delta about 20.885 us; cardioid delta about 24.789 us), inconsistent with declared left/right monitor geometry. Downstream monitor routing/isolation must be verified before learning left/right acoustic paths."} {"ts": "2026-05-28T12:06:53+01:00", "type": "implementation", "status": "ok", "note": "Fensalir now selects FieldEvidence AudioPath Phase/Confidence claims as DebugOverlay backend packets, so Mimir acoustic calibration constraints are planned rather than deferred. Mimir profile smoke now reports acousticPlanned=1 acousticDeferred=0; acoustic source volumes remain Phase 5 fusion work."} {"ts": "2026-05-28T12:10:23+01:00", "type": "implementation", "status": "ok", "note": "Phase 5 synthetic audio-source proof added: Mimir lowers an SRP/PHAT-style localized source candidate with calibration/source identity and a world-space confidence envelope; Fensalir plans it as a FieldEvidence DebugOverlay packet. Live room geometry still needs an explicit coordinate frame before physical candidates become runtime truth."} +{"ts": "2026-05-28T12:13:46+01:00", "type": "implementation", "status": "ok", "note": "Phase 5 synthetic marker proof added: deterministic multi-camera marker candidates lower with calibration/marker identity and world-space support, and Fensalir plans them as FieldEvidence DebugOverlay packets. Ambiguous raw camera feature claims still defer."} diff --git a/state/map.yaml b/state/map.yaml index 0fa9b31..11d7e1c 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -503,7 +503,10 @@ accepted_design: localization result with calibration/source identity and a world-space confidence envelope, and Fensalir plans it as a FieldEvidence DebugOverlay packet. Live room geometry still needs an explicit measured coordinate - frame before those candidates become runtime truth. + frame before those candidates become runtime truth. Phase 5's synthetic + marker proof now lowers a deterministic multi-camera marker candidate with + calibration/marker identity and world-space support, and Fensalir plans it + as DebugOverlay evidence while ambiguous raw camera features still defer. - `config/perfect-machine.example.json` declares live owners, sensors, reservoir views, and runtime policy. - `src/Mimir.Runtime/Synchronization` now includes the Perfect Machine module