docs(cv): define milestone 4 recorded realtime plan

This commit is contained in:
DCCONSTRUCTIONS
2026-08-05 12:54:12 +03:00
parent 1b3e0b3406
commit 78a3dc2162
@@ -0,0 +1,646 @@
# Milestone 4 — object-centric recorded-realtime CV
Date: 2026-08-05
Status: planned; implementation not started
Audit base: `1b3e0b3` on `feat/simulation-polygon-s1`
## Purpose and isolation
This document is the standalone execution snapshot for Mission Core Milestone 4.
It deliberately does not rewrite the historical architecture audit, accepted ADRs
or immutable LAB conclusions. Where the historical audit and the verified code
state disagree, this document records the disagreement and the Milestone 4
decision explicitly.
The milestone has one product goal:
> turn the already-proven RAVNOVES00 detector, K1 range/occupied geometry,
> bounded temporal state and telemetry pieces into one reusable object-centric
> perception graph which runs at recorded source pace on Worker 006 and has the
> same execution semantics intended for a later live source.
The milestone does not require a physical live K1 session. It must leave physical
live acceptance, mounted vehicle geometry and command/navigation/safety authority
false.
## Audited inputs
The plan was reconciled against:
- `2026-08-05_Mission_Core_ARCH.docx`, treated as an architectural proposal made
before full code access;
- the repository at commit `1b3e0b3`;
- the accepted source, LAB and architecture documents in `docs/`,
`docs/adr/` and `experiments/perception/`;
- executable contracts and tests in `src/k1link/compute/`,
`src/k1link/laboratory/` and `tests/`;
- immutable local RAVNOVES00 results;
- the live read-only Worker 006 state on 2026-08-05;
- Mission Core Ops, including the intentional-debt boundary in MISSIONCOR-51.
## Product corrections applied to the historical audit
### Source identity
The admitted camera + LiDAR + pose reference is **RAVNOVES00**, source session
`20260720T065719Z_viewer_live`. It contains 4,489 right-camera frames over
448.723 seconds at approximately 10.004 FPS and the associated registered K1
point-map increments and poses.
`RAVNOVES01` is referenced only as a later LiDAR-only transfer source. It cannot
validate a camera-first graph and is not a replacement for RAVNOVES00. There is no
admitted source named `RAVNOVES001` in the current catalog. Unless a new source is
explicitly admitted, every Milestone 4 full replay uses RAVNOVES00.
### Classification is not the product contract
The product does not need stable semantic classes or long-lived object identity.
It needs to answer:
1. Is there task-relevant occupied or uncertain space?
2. Where is the current metric evidence and how old is it?
3. Is the observed component moving, stationary or unresolved?
4. Does it intersect a declared movement corridor and therefore represent a
threat, uncertainty or clear non-threat?
5. Did missing, stale or conflicting evidence fail visibly?
Detector labels remain optional diagnostic metadata. They must never be required
for occupancy, motion, threat or downstream identity. A changing detector ID is
acceptable. Short-lived internal association is allowed only to suppress flicker,
bridge a bounded occlusion and estimate motion.
### No mandatory DeepStream migration in this milestone
The historical audit proposed DeepStream as the immediate hot path. The verified
code and results do not justify making that migration a blocker:
- E46J already processes the complete raw KB4 frame with one YOLOX-S/Triton pass;
- measured core capacity is 47.840 FPS against a 10.004 FPS source;
- core-path p95 is 25.355 ms and inference-request p95 is 16.415 ms;
- the tested DeepStream/TAO detector branches were not accepted as the product
detector.
Milestone 4 therefore keeps Triton/YOLOX as the frozen baseline and introduces a
real provider boundary. DeepStream may later implement the same provider contract
if measured decode, copy, latency or deployment evidence warrants it. It is not a
precondition for the reference graph.
### ROS 2, Nav2, Gazebo and PX4 are consumers, not the current CV critical path
The milestone publishes a versioned `LocalObstacleMap` and threat/uncertainty
contract suitable for a later ROS 2/Nav2 adapter. It does not block object-centric
CV closure on a navigation stack, controller, physics engine or PX4 lifecycle.
Those systems remain separate platform workstreams.
## Verified current state
| Area | Verified evidence | Milestone 4 verdict |
| --- | --- | --- |
| Raw fisheye input | E46J binds the full 800×600 right KB4 source, valid-FOV fill and one top-left letterbox pass | Closed; freeze as `ImageFrame` source profile |
| Detector capacity | 4,489/4,489 frames, zero failed, 47.840 FPS capacity, 25.355 ms core p95 | Closed for capacity, open for object-presence quality |
| Detector semantics | E46J has no independent exhaustive truth and contains a known operator-shadow false positive | Class acceptance removed from the product gate; object presence remains open |
| Camera↔LiDAR range | E29/E32 implement source-bound current point ownership and explicit unavailable/conflict states; E53 reuses the association over the full source | Algorithm and contract exist; one canonical graph integration and object-centric quality gate remain open |
| Unknown obstacles | E32/E34 retain geometry-only occupied components and never infer free space from missing points | Closed as a reusable positive-evidence primitive |
| Temporal retention | E34 consumes 2,119,302 current point rows, uses 0.75 s TTL, publishes held as unknown and emits no free cells | Algorithm accepted; not integrated with E46J in one product graph |
| Degradation | E35 executes six deterministic full-source variants; maximum recovery is 0.102 s against a 0.25 s gate | Accepted reusable regression primitive |
| Recorded pacing | E33 processes all 4,489 frames at 10.006 FPS with depth-two queues, zero replacement/drop/deadline miss and 2.668 ms result-age p95 | Accepted stage runner evidence, not end-to-end perception evidence |
| Motion | E51 emits 22,885 bounded motion candidates with complete accounting and no map-frame jump candidate | Candidate implementation exists; moving/static correctness is not accepted |
| Threat/collision | E51/E53 intentionally publish collision unavailable because body and LiDAR-to-body geometry are unbound | Replay simulation is possible with an explicit virtual rig; physical threat acceptance remains deferred |
| Realtime worker | Worker 006 has healthy Triton and persistent perception containers plus live Telegraf | Infrastructure exists |
| Worker graph | The persistent process still executes `run_e15_shadow_inference.py serve` with E15/E19/E8/E3/E23 profiles | Open architectural blocker: runtime remains LAB-generation-specific |
| Model service | Canonical Triton currently exposes pinned `yolox_s` and `pointpillars`; PointPillars was rejected as a K1 product candidate | Reuse `yolox_s`; do not reopen PointPillars |
| CV provider interfaces | No executable `DetectorProvider`, `GeometryProvider`, `TemporalProvider` or object-map contract exists in the product package | Open architectural blocker |
| LAB execution | 31 evidence definitions are fully classified; E33, E35 and E46J are canonical canaries, the remaining 28 are bounded legacy | Closed for LAB governance; no bulk legacy migration |
| Production import graph | 93 compute modules (72,456 lines), 51 web modules and many direct `web → compute.e*/l*` imports remain | Do not clean wholesale; prohibit new dependencies and isolate the new graph |
| Telemetry | Generic pipeline telemetry, multi-contour enrollment, bounded journal and current Worker 006 Telegraf service exist | Reuse; add graph-stage and object-state metrics |
| Independent truth | E46/E47/L3.4 freeze paths exist, but the two real reviews/adjudication are incomplete | Existing class truth is not a Milestone 4 blocker; a smaller object-centric truth contract is required |
## What is reusable without another research cycle
The following pieces are admitted inputs, not subjects for reinvention:
- RAVNOVES00 source identity, camera archive, LiDAR pack and pose binding;
- raw KB4 full-frame preprocessing from E46J;
- Triton `yolox_s` provider identity and the E46J capacity result;
- `SensorRepresentationCapabilities` and fail-closed algorithm admission;
- `TrackGeometry v1`, `PointSlab v1` and exact one-point-owner policy;
- E29 association through the reusable functions in
`semantic_geometry_fusion.py`;
- E34 `TemporalOccupiedLayer` and its current/held/expired semantics;
- E35 degradation scenarios and recovery checks;
- E33 recorded-source pacing and bounded queue accounting;
- E51 motion calculation as a diagnostic starting point, not as accepted truth;
- pipeline telemetry, immutable evidence verification, content-addressed results
and the canonical LAB runner;
- the current two-container Worker 006 topology: Triton model service plus a
single-purpose graph worker.
The following lines of research remain frozen unless a later measured gate fails:
- new detector zoo or frame-specific threshold tuning;
- PointPillars/CenterPoint/BEVFusion on the current K1 representation;
- long-term ReID and persistent semantic identity;
- per-route filters;
- multi-view, dewarp or stitched-camera branches;
- a custom tracker, planner or physics engine;
- a DeepStream rewrite without a demonstrated runtime need;
- mass migration or deletion of historical LAB implementations.
## Target reference graph
```text
RecordedK1Source (M4) ─┐
├─> SourceSynchronizer
LiveK1Source (later) ──┘ │
├─> ImagePreprocessor
│ │
│ └─> DetectorProvider (YOLOX/Triton)
│ │
│ └─> ObjectProposal2D[]
│
├─> RegisteredPointMapIncrement + Pose
│ │
└─────────────────┴─> GeometryAssociationProvider
│
├─> camera proposals with metric support
└─> geometry-only occupied observations
│
v
ObstacleObservation[]
│
v
TemporalObstacleLayer
│
v
MotionEstimator
│
v
LocalObstacleMap
│
virtual rig/corridor (M4 only)
│
v
ThreatAssessment[]
Every stage -> bounded outcome accounting -> pipeline telemetry -> immutable evidence
```
Replay and live modes may differ only in their source adapter and authority. The
graph, providers, contracts, queue policies, telemetry and output validation must
be identical.
## Canonical contracts
Milestone 4 introduces contracts in a new product package, proposed as
`src/k1link/perception/`. Accepted historical contracts are adapted, not mutated.
### `SourceEnvelope v1`
Required fields:
- source/session/frame identities;
- UTC, monotonic and source timestamps with clock basis;
- image, registered point increment and pose availability;
- calibration and representation identities;
- exact source age and binding reason;
- terminal source outcome when a modality is unavailable.
### `ObjectProposal2D v1`
Required fields:
- proposal identity local to the frame;
- bounding region and objectness confidence;
- provider/model/preprocess identities;
- optional `semantic_hint` and optional provider tracklet;
- no metric range, occupancy or threat claim.
### `ObstacleObservation v1`
Required fields:
- observation identity, frame/source identity and evidence time;
- evidence basis: `camera`, `lidar`, `fused` or `conflict`;
- currentness: `current`, `held`, `stale`, `unavailable`;
- positive occupied support and exact source-point ownership when available;
- position/range/covariance only when metric support is qualified;
- optional semantic hint which may change without changing obstacle identity;
- explicit reason codes and false authority flags.
### `TemporalObstacle v1`
Required fields:
- ephemeral component ID which consumers must not persist as identity;
- current/held/expired state and TTL;
- last hit, age, short history and association basis;
- bounded cells/geometry;
- motion state `moving`, `stationary` or `unknown` plus confidence and reason;
- no long-term ReID.
### `LocalObstacleMap v1`
Required fields:
- source and graph identities;
- current occupied components;
- held/stale components represented as unknown, never as free;
- camera-only uncertainty separate from metric occupied geometry;
- output age and complete source/frame accounting;
- zero implicit free-space claims.
### `ThreatAssessment v1`
Required fields:
- assessed obstacle/component reference;
- rig/corridor profile identity and qualification state;
- relative motion, closest approach and TTC when measurable;
- corridor-intersection state;
- decision `threat`, `not-threat` or `unknown` with reason codes;
- authority state `replay-simulated` in Milestone 4;
- no physical collision or actuation acceptance.
## Dependency and code-shape rules
1. `perception/contracts` imports no provider, graph, LAB or web module.
2. Providers implement contracts and may use admitted reusable compute primitives.
3. The reference graph composes providers but imports no `eNN_*` or `lNN_*`
module.
4. Source adapters feed the graph; they do not contain detector, range, temporal
or threat logic.
5. LAB adapters may invoke the reference graph and evidence runner. The graph
never imports a LAB adapter.
6. Web/UI reads canonical result documents. It does not recompute acceptance or
import a new experiment executor.
7. Historical E/L modules remain immutable legacy unless an integrity defect is
found.
8. A new model or stack enters through an experimental provider adapter and does
not change core graph contracts.
Architecture tests must fail if a new canonical CV increment adds an experiment
name to the graph, app shell or generic LAB dispatcher.
## Implementation plan
### M4.0 — freeze the baseline and executable acceptance profile
Deliverables:
- create a versioned Milestone 4 acceptance profile which binds the exact
RAVNOVES00, E46J, E32, E34, E35, calibration, model and worker identities;
- record the object-centric non-goals and all authority flags;
- add a machine-readable inventory of reusable algorithms versus historical
wrappers;
- add an import-boundary test before new product code is introduced;
- retain the current E15 worker as rollback reference.
Exit:
- every input digest resolves;
- the profile cannot silently select RAVNOVES01, TEST007 or another detector;
- the baseline replay and current Worker 006 process identity are recorded;
- no source, model, threshold or historical result is rewritten.
### M4.1 — establish the product contracts
Deliverables:
- implement the six contracts above with strict serializers, exact-key
validation and versioned schemas;
- define provider protocols for source, detector, geometry association,
temporal state, motion and threat;
- define one graph configuration contract with provider pins, queue capacities,
deadlines and authority;
- implement adapters from `TrackGeometryFrame` and E34 temporal components to
the new obstacle contracts without changing v1 historical documents;
- add round-trip and adversarial contract tests.
Adversarial tests must reject:
- semantic class as required occupancy evidence;
- a changing semantic label changing obstacle occupancy identity;
- duplicate source-point ownership;
- range without current qualified points;
- held evidence published as current;
- missing LiDAR published as free;
- threat published without a rig/corridor identity;
- planner, command or safety authority in replay mode.
Exit:
- contracts are independent of E/L module names;
- a proposal with no class is valid;
- a geometry-only obstacle is valid;
- a camera-only proposal remains explicit non-metric uncertainty;
- downstream consumers cannot depend on persistent track ID.
### M4.2 — build one source-neutral graph runner
Deliverables:
- implement `ReferencePerceptionGraphV1` as one persistent stage graph;
- implement bounded queues and explicit outcome types for delivered,
superseded, stale, rejected, failed and unavailable inputs;
- reuse the existing pipeline telemetry emitter for stage and run documents;
- add deterministic graph startup, shutdown, cancellation and restart state;
- add a recorded source adapter which replays original RAVNOVES00 event timing at
`1.0×` and can also run uncapped for development;
- define, but do not physically accept, the later live source adapter contract.
Exit:
- replay and live source types share one graph entry point;
- no queue is unbounded;
- every admitted source item has one terminal outcome;
- repeated uncapped runs produce the same canonical object payload digest;
- shutdown leaves no orphan worker or replay process.
### M4.3 — integrate the frozen detector as object proposals
Deliverables:
- wrap E46J raw-fisheye preprocessing and Triton YOLOX inference behind
`DetectorProvider v1`;
- emit class-agnostic `ObjectProposal2D` rows while retaining YOLOX label as an
optional diagnostic hint;
- keep the E46J model, score, NMS, valid-FOV and full-frame profile frozen for the
first reference run;
- account for zero-proposal frames, pathological boxes and provider failures;
- publish provider latency and GPU metrics through the common telemetry path.
Exit:
- 4,489/4,489 frames execute through the provider;
- core capacity remains at or above the 10.004 FPS source rate;
- no class name participates in graph routing, occupancy or threat logic;
- no route-specific post-processing is introduced;
- a later detector can replace YOLOX without changing graph or output contracts.
### M4.4 — integrate metric support and unknown occupied geometry
Deliverables:
- wrap the accepted E29/E32 association primitives behind
`GeometryAssociationProvider v1`;
- publish fused observations only with exact current point support;
- publish camera-only proposals with unavailable metric range;
- publish independent geometry-only occupied observations without invented
semantic classes;
- retain conflict, sparse, stale, outside-overlap and unavailable reason codes;
- compute total and eligible range coverage separately.
Exit:
- exact point ownership closes on every frame;
- zero missing-point free-space claims occur;
- range is reproducible from source points and profile identity;
- eligible coverage excludes unavailable/out-of-overlap observations;
- the full-source output reconciles with accepted E32/E53 source accounting.
### M4.5 — integrate temporal retention and moving/static/unknown state
Deliverables:
- place the accepted E34 temporal layer after object observations;
- treat component IDs as ephemeral and hide them from downstream acceptance;
- extract a reusable motion estimator from the E51 diagnostic implementation;
- estimate motion only from bounded map-frame history with explicit minimum span,
displacement and confidence requirements;
- publish `unknown` on insufficient history, map-frame discontinuity or stale
support;
- add deterministic synthetic motion fixtures and labeled RAVNOVES00 clip checks.
Exit:
- a detector ID change does not erase a spatially retained occupied component;
- held evidence is unknown and expires by TTL;
- moving/static decisions never use semantic class;
- false persistence and ghost duration are measured;
- no long-term identity or unlimited history exists.
### M4.6 — implement replay-only threat assessment
Deliverables:
- define a versioned virtual rig and corridor profile for RAVNOVES00 replay;
- make its `replay-simulated` qualification unmistakable in every result;
- compute corridor intersection, relative motion, closest approach and TTC only
when the required geometry is available;
- map incomplete or camera-only evidence to `unknown`, never to safe;
- create deterministic static, crossing, approaching, receding, occluded and
stale-obstacle fixtures.
Exit:
- every obstacle receives `threat`, `not-threat` or `unknown` with reason codes;
- missing mount/body evidence cannot produce a physical collision claim;
- all critical synthetic threat fixtures avoid a false `not-threat` outcome;
- nuisance slow/stop output is measured separately from missed threats.
Physical mounted threat acceptance remains outside Milestone 4.
### M4.7 — cut Worker 006 over to the canonical graph
Deliverables:
- package one digest-bound worker artifact containing the graph runner, provider
pins, configs and validators but no credentials;
- keep `ndc-mission-core-triton` as the model service and
`ndc-mission-core-perception-worker` as the single graph process;
- replace the E15-specific process command only after a successful shadow canary;
- retain an exact rollback declaration for the current E15 container/process;
- correlate graph run, request, source, model, worker, container and telemetry
identities;
- verify Telegraf/agent telemetry continues across graph restart.
Exit:
- the durable process command contains no E/L experiment identity;
- worker health reports provider readiness and graph readiness separately;
- restart recovery is deterministic and does not reload models per run;
- rollback restores the previous worker without changing Triton or source data;
- no new container, model server or public worker port is introduced.
### M4.8 — execute the object-centric source-scoped quality gate
The existing E46 seven-class truth island remains historical work. Milestone 4
adds a separate object-centric review contract; it does not require class labels.
Dataset construction:
- freeze 20–30 connected clips of 5–10 seconds across route/time blocks;
- include occupied object, background false-positive, partial occlusion, fisheye
edge, small obstacle, moving crossing, static obstacle and no-object strata;
- label obstacle presence/extent, current geometry association, freshness,
moving/static/unknown and virtual-corridor threat/unknown;
- freeze graph predictions before review labels are joined;
- keep development and validation separated by connected clip/time component;
- use two independent reviewers plus adjudication for the release set.
Initial release thresholds:
| Dimension | Milestone 4 gate |
| --- | ---: |
| Evidence accounting | 100% terminal outcomes |
| False free-space claims | 0 |
| Obstacle presence precision | at least 90% |
| Obstacle presence recall | at least 90% |
| Critical corridor-obstacle recall | at least 95% |
| Geometry association correctness on eligible observations | at least 90% |
| Current/held/stale/unavailable correctness | at least 90% |
| Moving/static decision correctness where decision is supported | at least 90% |
| Critical threat labelled `not-threat` | 0 events |
`unknown` is an accepted conservative outcome when evidence is insufficient, but
its rate and causes must be reported. Aggregate quality cannot waive a critical
miss, hidden drop, false-free claim or stale/current substitution.
Exit:
- labels are object-centric and do not score semantic class;
- predictions predate validation-label reveal;
- every failed gate produces a bounded cause, not another model-zoo branch;
- a new detector challenger is authorized only if the frozen baseline fails for
a measured detector-caused obstacle-presence cluster.
### M4.9 — recorded-realtime release candidate and cutover
Execute on Worker 006 with the frozen RAVNOVES00 source:
1. one full `1.0×` 448.723-second replay;
2. one uncapped deterministic replay for payload comparison;
3. four consecutive source-paced loops for approximately 30 minutes of soak;
4. deterministic restart/recovery and each accepted E35 degradation variant;
5. immutable result verification after worker restart.
Runtime gates:
- all 4,489 camera frames and every admitted LiDAR/pose event are accounted for;
- sustained throughput is at least 10.004 FPS;
- source-to-`LocalObstacleMap` p95 is at most 100 ms and p99 at most 200 ms;
- all queue capacities and high-water marks are published;
- all drops/supersessions are explained by terminal outcomes;
- no monotonic unbounded RSS or VRAM growth appears across the four-loop soak;
- model load occurs at worker startup, not per run;
- graph restart returns to ready without source/result identity collision;
- accepted degradation recovery remains within 0.25 s;
- command, navigation and safety authority remain false.
Release evidence:
- immutable source, graph, provider, model, config, worker and container identities;
- per-stage latency, queue, CPU/RAM, GPU/VRAM, temperature, power and disk metrics;
- canonical object, temporal, motion, threat and map documents;
- full visual replay plus automatically selected failure/unknown clips;
- object-centric quality report and regression comparison;
- reproducibility receipt and verified artifact hashes;
- automatically rendered Mission Core LAB report using the existing report canon;
- factual Ops update with implementation files, test commands and immutable result
IDs.
Exit:
- one source-neutral product graph, not a new E54-specific runtime, owns the
recorded-realtime result;
- the GUI consumes its standard report/evidence shape without a custom page;
- the E15 runtime is retained only as rollback/legacy;
- the milestone may be closed as recorded-realtime CV accepted for RAVNOVES00.
## Verification layers
### Unit and contract
- strict schema round trips;
- class-free object proposal and observation cases;
- geometry-only obstacle cases;
- ID-switch invariance;
- point-ownership and no-false-free adversarial cases;
- TTL, stale, recovery and restart state machines;
- motion and threat deterministic fixtures;
- dependency/import boundary tests.
### Integration
- recorded source → detector → geometry → temporal → motion → map → threat;
- Triton provider failure and recovery;
- missing camera, LiDAR and pose;
- delayed and out-of-order events;
- queue overload with explicit supersession;
- immutable artifact reopen after process restart;
- telemetry run correlation.
### Full-source
- exact RAVNOVES00 accounting;
- source-paced and uncapped replay parity;
- quality-set evaluation after frozen prediction;
- four-loop resource soak;
- visual evidence for successes, failures and unknowns.
## Explicit non-blocking debt after Milestone 4
The following debts remain known and do not block recorded-realtime CV closure:
- physical live K1 run in the shared LAN;
- measured vehicle body, footprint and LiDAR-to-body mount/extrinsic;
- physical threat/collision acceptance;
- second route/source generalization and RAVNOVES01 LiDAR-only transfer;
- second physical worker acceptance and universal GPU-host bootstrap;
- fleet TLS, HA and production storage/deployment;
- Nav2/PX4/Gazebo closed-loop integration;
- ROS 2 transport adapter;
- traversability, ditch, curb and negative-obstacle acceptance;
- open-world/adversarial perception;
- optional semantic-class quality;
- a DeepStream provider, unless a measured runtime/deployment gate later demands it.
Each debt returns to the critical path only when the corresponding product claim
is requested. None may be silently claimed from a successful recorded replay.
## Milestone 4 definition of done
Milestone 4 is complete only when all of the following are true:
- [ ] One versioned reference graph exists outside experiment-named modules.
- [ ] RAVNOVES00 runs through the same graph contract intended for a live source.
- [ ] Semantic class and persistent detector ID are optional diagnostics only.
- [ ] Camera, fused and geometry-only obstacles share one canonical observation
contract.
- [ ] Current, held, stale, unavailable and conflict states are explicit.
- [ ] Moving/static/unknown state is measured without semantic-class dependence.
- [ ] Replay-only threat assessment is explicit and cannot claim physical authority.
- [ ] Worker 006 runs the canonical graph instead of the E15-specific server.
- [ ] Full source-paced replay, deterministic replay, degradation, recovery and
soak gates pass.
- [ ] Object-centric independent review thresholds pass or every exception is
explicitly accepted by the product owner.
- [ ] Evidence accounting is 100% and false-free claims are zero.
- [ ] The standard LAB report and visual evidence are reproducible after restart.
- [ ] No new canonical CV work adds an E/L-specific branch to product core or UI.
- [ ] The Ops card contains exact files, commits, result IDs and validation.
- [ ] Physical live, mounted geometry, navigation and safety remain visibly deferred.
## Implementation order
The implementation sequence is intentionally strict:
```text
M4.0 baseline freeze
→ M4.1 contracts
→ M4.2 graph runner
→ M4.3 detector provider
→ M4.4 geometry/range provider
→ M4.5 temporal + motion
→ M4.6 replay threat
→ M4.7 Worker 006 shadow/cutover
→ M4.8 object-centric independent gate
→ M4.9 recorded-realtime release candidate
```
Contract and truth-set preparation may overlap implementation, but validation
labels must remain unavailable until the corresponding predictions are frozen.
No later phase may be used to conceal a failed earlier contract or accounting
gate.