Preserve the completed teach-and-repeat laboratory stage: reference preparation, cascaded acquisition, local tracking and recovery, recording lifecycle, replay qualification, and persistent Rerun scene controls. Document the open grid-picking regression and Rerun upgrade contract. No autonomous driving or loop-closure optimization is claimed.
183 lines
11 KiB
Markdown
183 lines
11 KiB
Markdown
# Route cascade v6 — archive qualification
|
||
|
||
## Scope and architectural boundary
|
||
|
||
Owner-authorized implementation following the run 016 retrieval diagnosis.
|
||
This change stays inside Teach & Repeat for the selected, previously recorded
|
||
location. It neither changes K1 SLAM nor admits autonomous vehicle control.
|
||
Original captures, physical experiment reports and planner comparisons remain
|
||
immutable; qualification results live in separate private `.runtime` folders.
|
||
No scanner, recording, MQTT publisher or second application server is started.
|
||
|
||
The defect was not insufficient overlap or scanner range. In run 016 the coarse
|
||
descriptor ranked the actual location below a permanent top-six shortlist.
|
||
The precise fitter never received that location. See
|
||
[the preceding diagnosis](2026-09-20-mid-route-016-retrieval-diagnosis.md).
|
||
|
||
## Implemented sequence
|
||
|
||
1. Collect a continuous stationary prefix using the existing ten-second and
|
||
motion gates. Start with the established dense local-start search; after a
|
||
tracking loss its target is the last confirmed place, not the route origin.
|
||
2. If dense search honestly rejects the location, rank eligible local regions
|
||
across the selected route. Ranking controls work order, not eligibility:
|
||
the six-item permanent shortlist is gone. Six is now only a batch size.
|
||
3. Precisely fit the ranked regions sequentially, constructing one region at
|
||
a time. Preserve the existing numerical and freshness budgets. Account for
|
||
evaluated and remaining regions; an unfinished search is *incomplete*, not
|
||
a completed negative result or a valid provisional position.
|
||
4. Group converged fits by their resulting position **and orientation**.
|
||
Compare competing distinct locations, including fits originating from the
|
||
same coarse anchor. An ambiguous best result cannot become a prior.
|
||
5. A qualified best hypothesis remains provisional. Only three consistent,
|
||
disjoint fresh-data windows can establish tracking. If fresh geometry
|
||
rejects this hypothesis before tracking, try the next non-ambiguous
|
||
alternative, reset the streak, and collect new receipts after a new fence.
|
||
The original prefix's age limit is not extended by a retry.
|
||
6. If a dense-start provisional hypothesis fails fresh confirmation, collect
|
||
a new stationary prefix within the same recording and search the route
|
||
without repeatedly retrying that disproved start. Transport, identity,
|
||
stale-data and ambiguous-location failures cannot use this fallback to
|
||
manufacture a successful geometric result.
|
||
7. Established tracking keeps the existing local update and recovery flow.
|
||
Loss clears accepted localisation immediately and begins stationary
|
||
recovery around the last confirmed position. Capture ownership remains
|
||
separate. End of input clears live authority and releases the compute lease.
|
||
|
||
The three fresh checks, overlap/RMSE, convergence, shape/information, identity,
|
||
continuity and age gates were not weakened. Descriptor similarity and fitted
|
||
overlap are not probabilities that the scanner is in the correct place.
|
||
|
||
## Implementation map
|
||
|
||
- `route_relocalization.py`: v6 full ranked queue, lazy local targets,
|
||
completeness accounting, distinct fitted-pose ambiguity and candidate queue.
|
||
- `stationary_bootstrap.py`: v3 per-hypothesis fresh confirmation, queue
|
||
continuation and dense-start rejection signal.
|
||
- `stationary_live.py` and `route_relocalization_worker.py`: same-capture
|
||
route-only retry after collecting a new stationary prefix; auditable candidate
|
||
index and trial number. Existing manual reinitialisation and post-loss recovery
|
||
retain their separate lifecycle.
|
||
- `planning_archive_source.py` / `check_planning_live_bootstrap.py`: optional
|
||
lower receipt boundary for testing the second recorded position independently;
|
||
preserves original clock intervals, epoch and source sequence. The boundary
|
||
does not provide the fitter with a reference position.
|
||
|
||
## Acceptance evidence
|
||
|
||
Qualification runs use the real derived ingress queue, K1 decoder, live service,
|
||
CPU worker and temporal gate at original 1x receipt timing. Each run seals input
|
||
digests, executed source copies, decisions, deliveries and terminal cleanup.
|
||
These are counterfactual software replays, not new physical field passes.
|
||
|
||
Initial focused suite: 68 tests passed, including real numerical synthetic
|
||
registration and live-service fallback tests. Explicit cases cover a valid
|
||
location beyond the first batch, partial search, ambiguity from the same
|
||
anchor, distinct fresh receipts, stale priors/results, queue exhaustion,
|
||
transport discontinuity, source end and resource cleanup. The fake-result
|
||
service tests prove orchestration, not geometric recognition accuracy.
|
||
|
||
Pre-final-metadata full 016 replay in
|
||
`.runtime/qualification-20260920-v6-016-full`: all 11 checks passed. Complete
|
||
108-seed start rejection followed by 54 fits across 18 route regions selected
|
||
the 30 m anchor. Three fresh checks established tracking at about 48.9 s;
|
||
the recorded approximately 6 m relocation remained tracked until input ended.
|
||
No operator reinitialisation command was injected in this full replay: unlike
|
||
the physical failed run, the first hypothesis now succeeded.
|
||
|
||
Independent second-position replay in
|
||
`.runtime/qualification-20260920-v6-016-second`: search completed, selected a
|
||
20 m coarse anchor and passed two fresh checks. The original retained suffix
|
||
contains only 46.746 s of receipts after reinitialisation. Input ended before
|
||
the third check, so tracking was **not** established and the expected-tracking
|
||
assertion failed. The other ten checks passed. This is explicitly partial
|
||
evidence, not a passed cold-start qualification, and no frames were duplicated
|
||
to prolong it.
|
||
|
||
Final-code full 015 regression in
|
||
`.runtime/qualification-20260920-v6-015-full`: all 11 checks passed. The unchanged
|
||
dense-start path succeeded without route retrieval (108 fits, 14.978 s numerical
|
||
search). Tracking was established at 39.1 s; all 27 fresh checks were accepted.
|
||
The recorded 99.282 m traversal completed without tracking loss or recovery;
|
||
end-of-input correctly cleared live localisation. Maximum ingress delivery lag
|
||
was 0.055 s. The physical original had 28 checks; replay scheduling changes
|
||
window boundaries and is not a claim of bit-for-bit fit identity.
|
||
|
||
Wrong-region control in `.runtime/qualification-20260920-v6-016-wrong-region`:
|
||
the same 016 raw input was compared with the actual reference recording's
|
||
130–160 m interval, not a translated copy of the correct map. All 11 checks
|
||
passed, with **no** provisional position and **no** tracking. Start search
|
||
rejected as ambiguous; subsequent route search completed all 24 generated fits
|
||
and returned no admissible location. Together with 108 start fits, all 132
|
||
attempts were accounted for. Maximum ingress lag was 0.029 s. This is one
|
||
negative example, not proof that all perceptually aliased places are rejected.
|
||
|
||
Final-code repeat in `.runtime/qualification-20260920-v6-016-final`: all 11
|
||
checks passed. Complete 108 + 54 fits selected the 30 m anchor (overlap
|
||
0.96945, inlier RMSE 0.14720 m); dense stage took 15.449 s, route stage 10.088 s.
|
||
Tracking started at 48.9 s. All 12 fresh checks passed, including the recorded
|
||
5.976 m movement; no tracking loss preceded the end of receipts at 96.9 s.
|
||
Maximum ingress lag was 0.043 s. RMSE measures fit residual, **not** absolute
|
||
localisation error. All original inputs and executed files remained unchanged
|
||
during each qualification; the final source/script hashes were rechecked.
|
||
|
||
Expanded backend regression: **155/155 passed** in 20.29 s; retained JUnit is
|
||
`.runtime/qualification-20260920-v6-final-tests-r2.xml`. One existing
|
||
Starlette/httpx deprecation warning remains. Ruff on changed implementation and
|
||
focused tests, plus `git diff --check`, passed. The first expanded run is
|
||
retained separately: five distance tests had a v2-only bootstrap fake missing
|
||
the newly recorded candidate fields, and one existing threaded test asserted
|
||
publication immediately after worker submission. Updated the fake's contract
|
||
and waited for the actual published phase; no production acceptance rule was
|
||
changed to fix these tests. Only test files changed after archive qualification;
|
||
the executed product and probe script hashes still match the final replay.
|
||
|
||
Source base: `be58d589e2fba520b1a3800574073f01a7fc9591`, with the existing dirty
|
||
worktree preserved. Qualified v6 source SHA-256:
|
||
|
||
- `route_relocalization.py`:
|
||
`b49e802015b062d531cf4f6a0570f8ae45a4db34886ba7a4f13d465983fd9fe7`
|
||
- `stationary_bootstrap.py`:
|
||
`40720eab565b7860d7829fefe0e2583d0a6c6859f493be05da1b4eefdbbd265e`
|
||
- `stationary_live.py`:
|
||
`95f5bfc7d3a22851745b6f9e52d6e51da38eb9d7251c1c4ce9c1be90d1dda791`
|
||
|
||
## Local service acceptance
|
||
|
||
With K1 source idle and capture completed, reviewed a fresh plan and applied
|
||
the repository's `manage_mission_core_launch_agent.py` workflow. Installed and
|
||
desired plist SHA were identical:
|
||
`5d7a58122c016f8021fede27d1b21891ee20238fc38c281e5b33574ff8204e3f`.
|
||
This was a code reload in the same repository, not a configuration or data-root
|
||
migration. The workflow retained its backup and accepted health.
|
||
|
||
Final runtime started at `2026-09-20T17:33:53.567849Z`: PID 95001 listens only
|
||
on canonical `127.0.0.1:8000`; `/api/health` is operational, device/source state
|
||
is idle, live ingress inactive. All 16 saved planner comparisons remain.
|
||
Nothing listens on 8765. Qualification publishers, numerical workers and pytest
|
||
processes have exited; no Docker workload or duplicate server was introduced.
|
||
Memory pressure remained normal (48% free by `memory_pressure`); swap stayed
|
||
at approximately 6339.5 MiB during the final sequential qualification stages.
|
||
|
||
## Limits and next field gate
|
||
|
||
This repairs exclusion by the shortlist and premature abandonment of a viable
|
||
queue. It is not a validated kilometre-scale place-recognition solution: visiting
|
||
all local regions still grows with route length. A runtime deadline remains a
|
||
freshness/resource guard, not a distance cap; exceeding it must remain visible
|
||
as incomplete. Descriptor comparability, spatially diverse retrieval and a
|
||
more scalable atlas remain follow-up work, not claims of this patch.
|
||
|
||
The 80 m visual clipping domain and outline correction are unchanged. Visual
|
||
range is not a promise of usable geometric overlap at an arbitrary position.
|
||
Archive success does not establish absolute ground-truth accuracy, arbitrary
|
||
10 m off-route cold starts, lighting robustness, different mounting/terrain,
|
||
kilometre routes or vehicle safety.
|
||
|
||
Only after archive regressions and negative controls pass is a short physical
|
||
test appropriate: initialise at the previously failed 20–30 m route location,
|
||
remain stationary through confirmation, then make a short monitored walk.
|
||
The independent second-position cold start still needs enough fresh real
|
||
receipts to complete its third check. There is no reason to request another
|
||
long walk just to debug the same saved input.
|