diff --git a/_docs/02_tasks/_dependencies_table.md b/_docs/02_tasks/_dependencies_table.md index 1b79b2e..0c96fef 100644 --- a/_docs/02_tasks/_dependencies_table.md +++ b/_docs/02_tasks/_dependencies_table.md @@ -1,8 +1,8 @@ # Dependencies Table -**Date**: 2026-05-23 (cycle-3 Step 10 Implement — Epic AZ-835 decomposed into 4 leaf tasks + AZ-777 closed: AZ-839 (C3, 5pt operator_pre_flight_setup real fixture, deps AZ-836+AZ-838+AZ-777Phase1+AZ-322+AZ-316+AZ-306, epic AZ-835), AZ-840 (C4, 3pt e2e orchestrator test (tlog,video,calibration), deps AZ-839+AZ-836+AZ-838+AZ-699+AZ-405+AZ-702+AZ-696, epic AZ-835), AZ-841 (C5, 1pt un-xfail AZ-777 AC-4+AC-5, deps AZ-839+AZ-840, epic AZ-835), AZ-842 (C6, 2pt docs — replay_protocol.md Invariant 12 + architecture.md + orchestrator README, soft dep AZ-841, epic AZ-835). AZ-777 transitioned to Done in Jira: Phases 1+2 shipped (batch 104 + between batches 104 and 106); Phases 3-5 superseded by Epic AZ-835 children per 2026-05-22 user directive. AZ-777 spec moved to done/. Earlier 2026-05-21 (cycle-3 Step 9 New Task — added AZ-776 (3pt open-loop ESKF composition profile via `c4_pose.enabled` flag, no deps, epic AZ-602) + AZ-777 (5pt Derkachi C6 reference tile cache + FAISS descriptor index from OSM/CARTO basemap, depends on AZ-776, epic AZ-602). Both unblock the 7 currently-`@xfail`-masked Derkachi e2e tests on Jetson; AZ-776 unblocks 5 (AC-1, AC-2, AC-5, AC-6 realtime, AC-6 asap), AZ-777 unblocks the remaining 2 (AC-3 + AZ-699 real-flight verdict). Earlier 2026-05-19 (refreshed late-morning after 11:27 Jetson Tier-2 e2e run for AZ-618 — surfaced a NEW gap: replay-mode `Config` lacks `c6_tile_cache` block, so `build_pre_constructed → _build_c6_descriptor_index → _c6_config` raises `KeyError` for AC-1/2/5/6. Follow-up filed as AZ-687 (2pt) under E-AZ-602 with guard at the bootstrap layer (NOT silent fallback in `_c6_config`). Earlier same-day mid-day after AZ-618 split: per the spec author's own Sizing-note recommendation + user-rule cap on PBI complexity, AZ-618 was split into 6 subtasks AZ-619..AZ-624 in Jira (subtasks of AZ-618; epic AZ-602 stays grandparent). AZ-618 retained at 0pt as the umbrella tracker; aggregate actionable work is 16pt across the subtasks (vs. AZ-618's original 5pt filing — author's "likely a true 8" caveat was understated due to c5_isam2_graph_handle ordering + GPU builder unknowns). Earlier same-day refresh at start of Step-7 rewind for AZ-618 — Step-11 Jetson tier-2 e2e gate identified missing internal product implementation: `runtime_root.main()` does not build the airborne `pre_constructed` infrastructure dict before `compose_root()`; AZ-618 = 5pt cross-cutting follow-up to AZ-591, lives under E-AZ-602; all 12 dep tasks are in `done/`. Earlier 2026-05-16 (cycle-1 completeness-gate post-mortem): AZ-589 + AZ-590 closed Won't Fix — were wrong abstraction (OKVIS v1 `ThreadedKFVio` API doesn't exist in OKVIS2 upstream; VINS-Mono `cpp/vins_mono/upstream/` submodule never existed; the actual production gap is the empty central `_STRATEGY_REGISTRY` affecting EVERY component with a strategy-selecting config field, not just c1_vio); replaced by AZ-591 (cross-cutting compose_root per-binary bootstrap, todo/, 5pt) + AZ-592 (AZ-332 Tier-2 validation bundle, backlog/, 5pt placeholder) + AZ-593 (AZ-333 Tier-2 validation bundle, backlog/, 5pt placeholder); AZ-332 + AZ-333 re-classified in gate report from FAIL to BLOCKED-on-Tier-2 per the original tasks' Implementation Notes deferral handles; earlier same-day after end of cycle-1 gate: AZ-589 + AZ-590 created (now closed); earlier same-day after end of Batch 64: AZ-558 implementation closed — `MavlinkTransport` seam now routes every C8 outbound MAVLink byte; AZ-401 AC-9 + AZ-404 AC-4b unskipped together; encoder helpers extracted to `_outbound_mavlink_payloads.py`; live-mode `compose_root` injection deferred to whichever future batch registers AP/iNav strategies in an airborne binary; earlier 2026-05-14: refreshed at start of Batch 63: AZ-559 closed Won't Fix — gap was illusory; `TileSource.ONBOARD_INGEST` + `TileMetadata.quality_metadata` + `write_tile`'s `FreshnessRejectionError` already cover the AZ-389 mid-flight ingest semantic without any new API; AZ-389 dep restored to AZ-303; earlier same-day after Batch 61: AZ-558 follow-up added — routes C8 outbound encoder bytes through `MavlinkTransport` seam; closes AZ-401 AC-9 deferred during batch 61 due to encoder-side routing not being in the AZ-401 task envelope; earlier same-day after cumulative review batches 52-54: AZ-528 hygiene PBI added for c1_vio strategy facade orchestration-spine 3-way duplication (Medium); earlier same-day after Batch 53: AZ-333 VINS-Mono landed — first c1_vio strategy after the AZ-332 OKVIS2 production-default; consolidation hygiene for the strategy-facade duplication deferred to a post-AZ-334 PBI; earlier same-day after Batch 51: AZ-527 hygiene PBI added from cumulative review batches 49-51 F1; 2026-05-13: AZ-526 hygiene PBI added from cumulative review batches 46-48 F1+F3; same-day refresh after Batch 44 SRP refactor: AZ-317 superseded; AZ-329 + AZ-330 specs rewritten; AZ-523 + AZ-524 audit-trail tickets added; E-C12 epic renamed `Operator Pre-flight Tooling` → `Operator Pre-flight Orchestrator`; earlier same-day refresh: AZ-507 + AZ-508 hygiene PBIs from cumulative review batches 31-33; 2026-05-11: AZ-489 + AZ-490 ADR-010 operator-origin path) -**Total Tasks**: 173 (132 product + 41 blackbox-test) — 2026-05-23 bump: +AZ-835 (Epic) + AZ-836 (C1) + AZ-837 (test-stack hardening, not this Epic) + AZ-838 (C2) added 2026-05-22→2026-05-23 prior to this update; +AZ-839 (C3) + AZ-840 (C4) + AZ-841 (C5) + AZ-842 (C6) added this update. AZ-777 stays in the table (now closed in Jira; spec at `done/AZ-777_derkachi_c6_reference_fixture.md` retains 8pt credit for Phases 1+2 shipped). Earlier counts: 165 (124 product + 41 blackbox-test) — AZ-317 retained in the table marked SUPERSEDED for audit; AZ-523 (C11 gate removal) + AZ-524 (C12 rename) added as 2 closed audit-trail tasks; AZ-526 = 2pt clock-helper hygiene; AZ-527 = 2pt c2 engine-dim helper hygiene; AZ-528 = 3pt c1_vio facade-spine hygiene; AZ-558 = 3pt MavlinkTransport routing follow-up; AZ-559 closed Won't Fix; AZ-589 + AZ-590 closed Won't Fix (kept in table as 0pt audit-trail rows); AZ-591 = 5pt cross-cutting compose_root bootstrap (todo/); AZ-592 = 5pt OKVIS2 Tier-2 placeholder (backlog/); AZ-593 = 5pt VINS-Mono Tier-2 placeholder (backlog/); AZ-618 = 0pt umbrella (split into AZ-619..AZ-624 on 2026-05-19); AZ-619..AZ-624 = 6 subtasks of AZ-618 covering Phase A..F of the airborne `pre_constructed` assembly, summing to 16pt actionable work; AZ-687 = 2pt replay-mode guard follow-up surfaced by AZ-618 Tier-2 run on 2026-05-19 -**Total Complexity Points**: 557 (424 product + 133 blackbox-test) — 2026-05-23 bump: +5pt AZ-839 + 3pt AZ-840 + 1pt AZ-841 + 2pt AZ-842 = +11 product pts on top of prior reconciled total. AZ-836 (3pt) + AZ-838 (3pt) were added 2026-05-22→2026-05-23 prior to this update; AZ-837 (test-stack hardening, not this Epic) is unaccounted in this delta and should be folded in at the next preamble reconciliation. Earlier baseline: 546 (413 product + 133 blackbox-test) — +3pt AZ-776 + 8pt AZ-777 (5→8 override 2026-05-21 cycle-3 batch 104; see `_docs/_process_leftovers/2026-05-21_az777_complexity_override.md` for rationale + the spec refresh that pulled e2e-runner wiring + C11 contract adapt + Derkachi catalog seed + fixture replacement + un-xfail into one ticket) — AZ-523 = 3pt, AZ-524 = 2pt, AZ-526 = 2pt, AZ-527 = 2pt, AZ-528 = 3pt, AZ-558 = 3pt, AZ-589 + AZ-590 retained at 5pt each but closed Won't Fix (treated as 0 effective pts going forward), AZ-591 = 5pt, AZ-592 = 5pt placeholder, AZ-593 = 5pt placeholder, AZ-618 = 0pt umbrella post-split, AZ-619 = 2pt, AZ-620 = 3pt, AZ-621 = 3pt, AZ-622 = 3pt, AZ-623 = 3pt, AZ-624 = 2pt, AZ-687 = 2pt +**Date**: 2026-05-23 (cycle-3 Step 10 Implement, refactor run 02-az507-routespec-relocation — added AZ-844 (Epic, run dir `_docs/04_refactoring/02-az507-routespec-relocation/`) + AZ-845 (C01, 2pt relocate `RouteSpec` from `replay_input/tlog_route.py` to `_types/route.py`, deps None, epic AZ-844) + AZ-846 (C02, 2pt refresh `module-layout.md` cycle-3 entries — c11 + replay_input + `_types/route`, deps AZ-845, epic AZ-844) + AZ-847 (C03, 2pt widen `test_az270_compose_root` lint to enforce full rule-9 allow-list, deps AZ-845, epic AZ-844). Resolves cycle-3 cumulative review FAIL verdict (F1 High Architecture, F2 Medium Architecture, F3 Medium Maintainability) per `_docs/03_implementation/cumulative_review_batches_104-109_cycle3_report.md`. Jira "Blocks" links recorded: AZ-845 → AZ-846, AZ-845 → AZ-847. Earlier same-day at start of Step 10 Implement — Epic AZ-835 decomposed into 4 leaf tasks + AZ-777 closed: AZ-839 (C3, 5pt operator_pre_flight_setup real fixture, deps AZ-836+AZ-838+AZ-777Phase1+AZ-322+AZ-316+AZ-306, epic AZ-835), AZ-840 (C4, 3pt e2e orchestrator test (tlog,video,calibration), deps AZ-839+AZ-836+AZ-838+AZ-699+AZ-405+AZ-702+AZ-696, epic AZ-835), AZ-841 (C5, 1pt un-xfail AZ-777 AC-4+AC-5, deps AZ-839+AZ-840, epic AZ-835), AZ-842 (C6, 2pt docs — replay_protocol.md Invariant 12 + architecture.md + orchestrator README, soft dep AZ-841, epic AZ-835). AZ-777 transitioned to Done in Jira: Phases 1+2 shipped (batch 104 + between batches 104 and 106); Phases 3-5 superseded by Epic AZ-835 children per 2026-05-22 user directive. AZ-777 spec moved to done/. Earlier 2026-05-21 (cycle-3 Step 9 New Task — added AZ-776 (3pt open-loop ESKF composition profile via `c4_pose.enabled` flag, no deps, epic AZ-602) + AZ-777 (5pt Derkachi C6 reference tile cache + FAISS descriptor index from OSM/CARTO basemap, depends on AZ-776, epic AZ-602). Both unblock the 7 currently-`@xfail`-masked Derkachi e2e tests on Jetson; AZ-776 unblocks 5 (AC-1, AC-2, AC-5, AC-6 realtime, AC-6 asap), AZ-777 unblocks the remaining 2 (AC-3 + AZ-699 real-flight verdict). Earlier 2026-05-19 (refreshed late-morning after 11:27 Jetson Tier-2 e2e run for AZ-618 — surfaced a NEW gap: replay-mode `Config` lacks `c6_tile_cache` block, so `build_pre_constructed → _build_c6_descriptor_index → _c6_config` raises `KeyError` for AC-1/2/5/6. Follow-up filed as AZ-687 (2pt) under E-AZ-602 with guard at the bootstrap layer (NOT silent fallback in `_c6_config`). Earlier same-day mid-day after AZ-618 split: per the spec author's own Sizing-note recommendation + user-rule cap on PBI complexity, AZ-618 was split into 6 subtasks AZ-619..AZ-624 in Jira (subtasks of AZ-618; epic AZ-602 stays grandparent). AZ-618 retained at 0pt as the umbrella tracker; aggregate actionable work is 16pt across the subtasks (vs. AZ-618's original 5pt filing — author's "likely a true 8" caveat was understated due to c5_isam2_graph_handle ordering + GPU builder unknowns). Earlier same-day refresh at start of Step-7 rewind for AZ-618 — Step-11 Jetson tier-2 e2e gate identified missing internal product implementation: `runtime_root.main()` does not build the airborne `pre_constructed` infrastructure dict before `compose_root()`; AZ-618 = 5pt cross-cutting follow-up to AZ-591, lives under E-AZ-602; all 12 dep tasks are in `done/`. Earlier 2026-05-16 (cycle-1 completeness-gate post-mortem): AZ-589 + AZ-590 closed Won't Fix — were wrong abstraction (OKVIS v1 `ThreadedKFVio` API doesn't exist in OKVIS2 upstream; VINS-Mono `cpp/vins_mono/upstream/` submodule never existed; the actual production gap is the empty central `_STRATEGY_REGISTRY` affecting EVERY component with a strategy-selecting config field, not just c1_vio); replaced by AZ-591 (cross-cutting compose_root per-binary bootstrap, todo/, 5pt) + AZ-592 (AZ-332 Tier-2 validation bundle, backlog/, 5pt placeholder) + AZ-593 (AZ-333 Tier-2 validation bundle, backlog/, 5pt placeholder); AZ-332 + AZ-333 re-classified in gate report from FAIL to BLOCKED-on-Tier-2 per the original tasks' Implementation Notes deferral handles; earlier same-day after end of cycle-1 gate: AZ-589 + AZ-590 created (now closed); earlier same-day after end of Batch 64: AZ-558 implementation closed — `MavlinkTransport` seam now routes every C8 outbound MAVLink byte; AZ-401 AC-9 + AZ-404 AC-4b unskipped together; encoder helpers extracted to `_outbound_mavlink_payloads.py`; live-mode `compose_root` injection deferred to whichever future batch registers AP/iNav strategies in an airborne binary; earlier 2026-05-14: refreshed at start of Batch 63: AZ-559 closed Won't Fix — gap was illusory; `TileSource.ONBOARD_INGEST` + `TileMetadata.quality_metadata` + `write_tile`'s `FreshnessRejectionError` already cover the AZ-389 mid-flight ingest semantic without any new API; AZ-389 dep restored to AZ-303; earlier same-day after Batch 61: AZ-558 follow-up added — routes C8 outbound encoder bytes through `MavlinkTransport` seam; closes AZ-401 AC-9 deferred during batch 61 due to encoder-side routing not being in the AZ-401 task envelope; earlier same-day after cumulative review batches 52-54: AZ-528 hygiene PBI added for c1_vio strategy facade orchestration-spine 3-way duplication (Medium); earlier same-day after Batch 53: AZ-333 VINS-Mono landed — first c1_vio strategy after the AZ-332 OKVIS2 production-default; consolidation hygiene for the strategy-facade duplication deferred to a post-AZ-334 PBI; earlier same-day after Batch 51: AZ-527 hygiene PBI added from cumulative review batches 49-51 F1; 2026-05-13: AZ-526 hygiene PBI added from cumulative review batches 46-48 F1+F3; same-day refresh after Batch 44 SRP refactor: AZ-317 superseded; AZ-329 + AZ-330 specs rewritten; AZ-523 + AZ-524 audit-trail tickets added; E-C12 epic renamed `Operator Pre-flight Tooling` → `Operator Pre-flight Orchestrator`; earlier same-day refresh: AZ-507 + AZ-508 hygiene PBIs from cumulative review batches 31-33; 2026-05-11: AZ-489 + AZ-490 ADR-010 operator-origin path) +**Total Tasks**: 176 (135 product + 41 blackbox-test) — 2026-05-23 refactor-run bump: +AZ-844 (Epic, 0pt umbrella for refactor run 02) + AZ-845 + AZ-846 + AZ-847 (3 product tasks). Prior 2026-05-23 bump (Epic AZ-835 decomposition): 173 (132 product + 41 blackbox-test) = +AZ-835 (Epic) + AZ-836 (C1) + AZ-837 (test-stack hardening, not this Epic) + AZ-838 (C2) added 2026-05-22→2026-05-23 prior to that update; +AZ-839 (C3) + AZ-840 (C4) + AZ-841 (C5) + AZ-842 (C6) added in that update. AZ-777 stays in the table (now closed in Jira; spec at `done/AZ-777_derkachi_c6_reference_fixture.md` retains 8pt credit for Phases 1+2 shipped). Earlier counts: 165 (124 product + 41 blackbox-test) — AZ-317 retained in the table marked SUPERSEDED for audit; AZ-523 (C11 gate removal) + AZ-524 (C12 rename) added as 2 closed audit-trail tasks; AZ-526 = 2pt clock-helper hygiene; AZ-527 = 2pt c2 engine-dim helper hygiene; AZ-528 = 3pt c1_vio facade-spine hygiene; AZ-558 = 3pt MavlinkTransport routing follow-up; AZ-559 closed Won't Fix; AZ-589 + AZ-590 closed Won't Fix (kept in table as 0pt audit-trail rows); AZ-591 = 5pt cross-cutting compose_root bootstrap (todo/); AZ-592 = 5pt OKVIS2 Tier-2 placeholder (backlog/); AZ-593 = 5pt VINS-Mono Tier-2 placeholder (backlog/); AZ-618 = 0pt umbrella (split into AZ-619..AZ-624 on 2026-05-19); AZ-619..AZ-624 = 6 subtasks of AZ-618 covering Phase A..F of the airborne `pre_constructed` assembly, summing to 16pt actionable work; AZ-687 = 2pt replay-mode guard follow-up surfaced by AZ-618 Tier-2 run on 2026-05-19 +**Total Complexity Points**: 563 (430 product + 133 blackbox-test) — 2026-05-23 refactor-run bump: +2pt AZ-845 + 2pt AZ-846 + 2pt AZ-847 = +6 product pts on top of prior reconciled total (AZ-844 epic itself is 0pt umbrella). Prior 2026-05-23 reconciled total: 557 (424 product + 133 blackbox-test) — +5pt AZ-839 + 3pt AZ-840 + 1pt AZ-841 + 2pt AZ-842 = +11 product pts on top of prior reconciled total. AZ-836 (3pt) + AZ-838 (3pt) were added 2026-05-22→2026-05-23 prior to that update; AZ-837 (test-stack hardening, not this Epic) is unaccounted in that delta and should be folded in at the next preamble reconciliation. Earlier baseline: 546 (413 product + 133 blackbox-test) — +3pt AZ-776 + 8pt AZ-777 (5→8 override 2026-05-21 cycle-3 batch 104; see `_docs/_process_leftovers/2026-05-21_az777_complexity_override.md` for rationale + the spec refresh that pulled e2e-runner wiring + C11 contract adapt + Derkachi catalog seed + fixture replacement + un-xfail into one ticket) — AZ-523 = 3pt, AZ-524 = 2pt, AZ-526 = 2pt, AZ-527 = 2pt, AZ-528 = 3pt, AZ-558 = 3pt, AZ-589 + AZ-590 retained at 5pt each but closed Won't Fix (treated as 0 effective pts going forward), AZ-591 = 5pt, AZ-592 = 5pt placeholder, AZ-593 = 5pt placeholder, AZ-618 = 0pt umbrella post-split, AZ-619 = 2pt, AZ-620 = 3pt, AZ-621 = 3pt, AZ-622 = 3pt, AZ-623 = 3pt, AZ-624 = 2pt, AZ-687 = 2pt Dependencies columns list only the tracker-ID portion (descriptive tail text in each task spec is omitted here for table-readability). The @@ -192,6 +192,9 @@ are all declared and documented below under **Cycle Check**. | AZ-840 | C4: E2E orchestrator test — raw (tlog, video, calibration) drives steps 1-7 end-to-end | 3 | AZ-839; AZ-836; AZ-838; AZ-699; AZ-405; AZ-702; AZ-696 | AZ-835 | | AZ-841 | C5: Un-xfail AZ-777 AC-4 + AC-5 Tier-2 tests once C3 + C4 land | 1 | AZ-839; AZ-840 | AZ-835 | | AZ-842 | C6: Docs — replay_protocol.md Invariant 12 + architecture.md + orchestrator-test README | 2 | AZ-841 (soft) | AZ-835 | +| AZ-845 | Refactor C01: Relocate RouteSpec DTO to _types/route.py (AZ-507 rule 9 fix) | 2 | None | AZ-844 | +| AZ-846 | Refactor C02: Refresh module-layout.md cycle-3 entries (c11 + replay_input + _types/route) | 2 | AZ-845 | AZ-844 | +| AZ-847 | Refactor C03: Widen test_az270_compose_root lint to enforce full rule-9 allow-list | 2 | AZ-845 | AZ-844 | ## Notes diff --git a/_docs/02_tasks/todo/AZ-845_refactor_relocate_routespec.md b/_docs/02_tasks/todo/AZ-845_refactor_relocate_routespec.md new file mode 100644 index 0000000..4f45180 --- /dev/null +++ b/_docs/02_tasks/todo/AZ-845_refactor_relocate_routespec.md @@ -0,0 +1,69 @@ +# Relocate RouteSpec DTO to _types/route.py (AZ-507 rule 9 fix) + +**Task**: AZ-845_refactor_relocate_routespec +**Name**: Relocate `RouteSpec` from `replay_input/tlog_route.py` to `_types/route.py` +**Description**: Resolve cycle-3 cumulative review F1 (High Architecture). Move the `RouteSpec` cross-component DTO to `_types/route.py` so the `c11_tile_manager.route_client` import becomes rule-9 compliant. Producer-side keeps backward-compat re-export so test imports do not break. +**Complexity**: 2 SP +**Dependencies**: None (anchor task of refactor run 02-az507-routespec-relocation) +**Component**: `_types/` (new file `route.py`); `replay_input/` (`tlog_route.py`, `__init__.py` modify); `components/c11_tile_manager/` (`route_client.py` modify) +**Tracker**: AZ-845 (https://denyspopov.atlassian.net/browse/AZ-845) +**Parent Epic**: AZ-844 (Refactor 02 — RouteSpec relocation + module-layout refresh + AZ-270 lint widening) + +Jira AZ-845 is the authoritative spec; this file is the in-workspace mirror. + +## Problem + +`src/gps_denied_onboard/components/c11_tile_manager/route_client.py:56` imports `RouteSpec` from `gps_denied_onboard.replay_input.tlog_route`, violating `module-layout.md` rule 9 (AZ-507 cross-component contract surface). Per the rule, `components//*.py` may only import from `_types/*`, `_types.inference_errors`, `helpers/*`, `config`, `logging`, `fdr_client`, `clock`, `frame_source` (interface only), and its own subpackage. `replay_input` is not in this allow-list. Every other cross-component DTO already lives under `_types/*` (`_types/geo.py`, `_types/tile.py`, `_types/inference.py`, etc.); `RouteSpec` is the asymmetric outlier. + +## Outcome + +- `RouteSpec` is defined in `src/gps_denied_onboard/_types/route.py` (frozen+slots dataclass; full docstring carried over verbatim). +- `c11_tile_manager/route_client.py:56` imports `RouteSpec` from `gps_denied_onboard._types.route`. +- `replay_input/tlog_route.py` continues to use `RouteSpec` internally (extractor return type) by importing from `_types.route`; keeps `RouteSpec` in `__all__` for backward-compat re-export. +- `replay_input/__init__.py` re-exports `RouteSpec` from `_types.route` directly. +- All existing tests pass at HEAD. +- Rule-9 audit reports zero violations after the move. + +## Scope + +### Included + +- New file: `src/gps_denied_onboard/_types/route.py` with `RouteSpec` dataclass. +- Modify `src/gps_denied_onboard/replay_input/tlog_route.py` (remove local definition, add import). +- Modify `src/gps_denied_onboard/replay_input/__init__.py` (re-export from `_types.route`). +- Modify `src/gps_denied_onboard/components/c11_tile_manager/route_client.py:56` (the rule-9 fix) plus the docstring snippet at file-top that names the source module. +- Optional hygiene: update 5 test files that import `RouteSpec` from `replay_input.tlog_route` directly (`tests/unit/replay_input/test_tlog_route.py`, `tests/unit/c11_tile_manager/test_route_client.py`, `tests/e2e/replay/_operator_pre_flight.py`, `tests/e2e/replay/test_e2e_orchestrator_unit.py`, `tests/e2e/replay/test_operator_pre_flight_driver.py`) to import from `_types.route` for symmetry. + +### Excluded + +- `RouteExtractionError` does NOT relocate — it is a `replay_input/`-specific error not imported by any `components//*.py` file. +- `extract_route_from_tlog` does NOT relocate — extraction logic is a `replay_input/` concern; only the DTO moves. +- No contract document at `_docs/02_document/contracts/shared_types/route.md`. +- No behaviour, performance, or contract-shape changes. + +## Acceptance Criteria + +| # | Criterion | +|---|-----------| +| AC-1 | `_types/route.py` contains `RouteSpec` with `@dataclass(frozen=True, slots=True)`, identical fields to the original (`waypoints`, `suggested_region_size_meters`, `source_tlog`, `source_segment`, `total_distance_meters`), and the full original docstring. | +| AC-2 | `route_client.py:56` reads `from gps_denied_onboard._types.route import RouteSpec`; rule-9 audit reports zero violations across `components/**/*.py`. | +| AC-3 | `replay_input/tlog_route.py` imports `RouteSpec` from `_types.route`; `extract_route_from_tlog` returns `RouteSpec`; `RouteSpec` is in `__all__` so `from replay_input.tlog_route import RouteSpec` resolves via re-export. | +| AC-4 | `from gps_denied_onboard.replay_input import RouteSpec` resolves to the same class object as `_types.route.RouteSpec` (verified by `is` identity check in test). | +| AC-5 | `pytest tests/unit/replay_input/test_tlog_route.py tests/unit/c11_tile_manager/test_route_client.py` passes — no failures, no skipped tests beyond pre-existing skips. | + +## Constraints + +- `RouteSpec` MUST remain `frozen=True, slots=True` (AZ-355 AC-2). +- `RouteSpec.__module__` MAY change to `gps_denied_onboard._types.route` (intended observable change; no test asserts on it). +- `from gps_denied_onboard.replay_input import RouteSpec` MUST keep working. + +## Risks & Mitigation + +**Risk 1 — pickle / serialization break**: confirmed by grep — no `pickle.dumps(route)` exists in `src/` or `tests/`. Risk does not materialize. + +**Risk 2 — hidden import grep missed**: producer-side keeps `RouteSpec` in its namespace via re-import + `__all__`; lazy importers using the old path resolve correctly. + +## Implementation Notes + +- This is the anchor of refactor run 02-az507-routespec-relocation. AZ-846 (module-layout refresh) and AZ-847 (lint widening) are blocked by this task (Jira "Blocks" links recorded). +- After this task lands, run the rule-9 audit script (the widened lint from AZ-847 once it lands) to confirm zero violations. diff --git a/_docs/02_tasks/todo/AZ-846_refactor_module_layout_cycle3.md b/_docs/02_tasks/todo/AZ-846_refactor_module_layout_cycle3.md new file mode 100644 index 0000000..8daef8d --- /dev/null +++ b/_docs/02_tasks/todo/AZ-846_refactor_module_layout_cycle3.md @@ -0,0 +1,60 @@ +# Refresh module-layout.md cycle-3 entries (c11 + replay_input + _types/route) + +**Task**: AZ-846_refactor_module_layout_cycle3 +**Name**: Refresh `module-layout.md` for cycle-3 file additions and the new `_types/route.py` +**Description**: Resolve cycle-3 cumulative review F2 (Medium Architecture). Update the c11_tile_manager Internal list, the shared/replay_input file list, and the `_types/` section in `module-layout.md` so they match on-disk reality. Cycle-2 carry-overs OUTSIDE these three sections are explicitly out of scope (deferred to a separate doc task). +**Complexity**: 2 SP +**Dependencies**: AZ-845 (the new `_types/route.py` file must exist before this task can register it) +**Component**: `_docs/02_document/module-layout.md` (single file) +**Tracker**: AZ-846 (https://denyspopov.atlassian.net/browse/AZ-846) +**Parent Epic**: AZ-844 (Refactor 02 — RouteSpec relocation + module-layout refresh + AZ-270 lint widening) + +Jira AZ-846 is the authoritative spec; this file is the in-workspace mirror. + +## Problem + +`module-layout.md` is stale. Cycle-3 cumulative review F2 documents: + +- **c11_tile_manager Internal list** lists 2 files (`satellite_provider_downloader.py`, `satellite_provider_uploader.py`); on-disk has 8 internal files plus `route_client.py` (cycle-3 NEW from batch 107). +- **shared/replay_input file list** is missing `errors.py` (cycle-2 carry), `tlog_ground_truth.py` (cycle-2 carry), `tlog_route.py` (cycle-3 NEW from batch 106). +- **`_types/` file list** does not yet include `route.py` (added by AZ-845). + +`/implement` Step 4 (File Ownership) treats `module-layout.md` as authoritative; staleness BLOCKS any future task touching unregistered areas. F2 is currently Medium; severity escalates to High if a fourth consecutive cycle leaves it stale. + +## Outcome + +- c11_tile_manager Internal list registers all 8 internals + `route_client.py`. +- shared/replay_input file list registers `errors.py`, `tlog_ground_truth.py`, `tlog_route.py`. +- `_types/` section registers `route.py` with a one-line description matching the convention of other `_types/*.py` entries. +- `git diff` shows additions only to those three sections — no other section, rule, or rule-text edit. + +## Scope + +### Included + +- Append cycle-3 + relevant cycle-2-carry entries to the c11_tile_manager Internal list, the shared/replay_input file list, and the `_types/` section. + +### Excluded + +- **Cycle-2 carry-overs OUTSIDE these sections**: `replay_api/` Per-Component Mapping entry, `cli/render_map.py`, `cli/replay_api_entrypoint.py`, `helpers/gps_compare.py`, `helpers/accuracy_report.py`. These are recorded in the cycle-3 retrospective and require a separate follow-up doc task with its own AZ ID. +- No code changes. +- No changes to `module-layout.md` rule numbering or rule text. Only the per-section file inventories are updated. + +## Acceptance Criteria + +| # | Criterion | +|---|-----------| +| AC-1 | c11_tile_manager Internal list contains all 8 existing internals (`_types.py`, `config.py`, `errors.py`, `idempotent_retry.py`, `signing_key.py`, `tile_downloader.py`, `tile_uploader.py`) plus `route_client.py`, alphabetised. | +| AC-2 | shared/replay_input file list adds `errors.py`, `tlog_ground_truth.py`, `tlog_route.py` with one-line descriptions matching the existing convention. | +| AC-3 | `_types/` section includes `route.py` with a one-line description of `RouteSpec` (waypoints + region size + source tlog provenance), identifying its producer (`replay_input/tlog_route.py`) and consumer (`c11/route_client.py`). | +| AC-4 | Diff of `module-layout.md` shows edits to ONLY the three named sections; no edits to other sections, rule numbering, or rule text. | + +## Constraints + +- Single file modified: `_docs/02_document/module-layout.md`. +- No tests required — documentation update. +- Scope discipline: cycle-2 doc carry-overs outside the three sections remain deferred. + +## Risks & Mitigation + +**Risk 1 — scope creep into cycle-2 carry-overs**: the Excluded list is explicit; Phase-4 implementer reviews the diff against ACs and rejects entries outside the three named sections at review. diff --git a/_docs/02_tasks/todo/AZ-847_refactor_az270_lint_widening.md b/_docs/02_tasks/todo/AZ-847_refactor_az270_lint_widening.md new file mode 100644 index 0000000..b7bdfc8 --- /dev/null +++ b/_docs/02_tasks/todo/AZ-847_refactor_az270_lint_widening.md @@ -0,0 +1,61 @@ +# Widen test_az270_compose_root lint to enforce full rule-9 allow-list + +**Task**: AZ-847_refactor_az270_lint_widening +**Name**: Widen `test_ac6_only_compose_root_imports_concrete_strategies` to enforce the full rule-9 allow-list +**Description**: Resolve cycle-3 cumulative review F3 (Medium Maintainability). Replace the AZ-270 lint's narrow `components → components` check with a full rule-9 allow-list check, so any future cross-component drift is caught at lint time rather than at cumulative-review time. Strict superset of the existing AC-6 check. +**Complexity**: 2 SP +**Dependencies**: AZ-845 (the widened lint must see a clean codebase to pass; running it against pre-AZ-845 HEAD is what AC-4 demonstrates as a one-time verification) +**Component**: `tests/unit/test_az270_compose_root.py` (single file) +**Tracker**: AZ-847 (https://denyspopov.atlassian.net/browse/AZ-847) +**Parent Epic**: AZ-844 (Refactor 02 — RouteSpec relocation + module-layout refresh + AZ-270 lint widening) + +Jira AZ-847 is the authoritative spec; this file is the in-workspace mirror. + +## Problem + +`tests/unit/test_az270_compose_root.py:194-219` (`test_ac6_only_compose_root_imports_concrete_strategies`) walks `src/gps_denied_onboard/components/**/*.py` and flags only edges whose `node.module` starts with `gps_denied_onboard.components.` AND whose leaf-component is not the importer's component. The full rule-9 allow-list (8 prefixes plus `frame_source` interface-only restriction) is NOT enforced. Imports from `replay_input`, `replay_api`, `runtime_root`, `cli/*`, and `frame_source` non-interface modules pass silently. F1 of the cycle-3 cumulative review (the c11 → replay_input edge) is the concrete consequence. + +`module-layout.md` rule 9 documents this lint as the enforcement mechanism for the rule. Reviewers reasonably assume the lint covers the documented allow-list; in practice it covers only one of the eight prefixes. The asymmetry is the F3 finding. + +## Outcome + +- `test_ac6_only_compose_root_imports_concrete_strategies` enforces the full rule-9 allow-list: a `components//*.py` ImportFrom node is allowed iff the imported module matches one of: `gps_denied_onboard.components..*` (own component), `gps_denied_onboard._types.*`, `gps_denied_onboard._types.inference_errors`, `gps_denied_onboard.helpers.*`, `gps_denied_onboard.config`, `gps_denied_onboard.logging`, `gps_denied_onboard.fdr_client`, `gps_denied_onboard.clock`, `gps_denied_onboard.frame_source` (interface-only — see Constraints). +- The widened lint is a strict superset of the existing AC-6 narrow check. +- After AZ-845 lands, the widened lint reports zero violations. +- The test docstring cites `module-layout.md` rule 9, not just AZ-270 AC-6. + +## Scope + +### Included + +- Modify `tests/unit/test_az270_compose_root.py` — the `test_ac6_*` test and its docstring. +- Add a small allow-list constant at module scope (single source of truth). +- Verify by `pytest tests/unit/test_az270_compose_root.py` after AZ-845 lands. + +### Excluded + +- Changes to other tests in the same file. +- Changes to production code. +- The `frame_source` interface-only enforcement: if AST-level disambiguation between interface and non-interface modules within `frame_source/*` is not feasible, allow-list only the explicit interface module path and reject other `frame_source.*` paths. Document in the test docstring. + +## Acceptance Criteria + +| # | Criterion | +|---|-----------| +| AC-1 | The lint flags any ImportFrom in `components/**/*.py` whose `module` starts with `gps_denied_onboard.` and is NOT in the rule-9 allow-list. | +| AC-2 | Strict superset of the existing AC-6 narrow check — every cross-component edge previously flagged is still flagged. | +| AC-3 | After AZ-845 lands, the widened lint reports zero violations. | +| AC-4 | Against the codebase BEFORE AZ-845 (verified during implementation by running the new lint on a temp checkout of pre-relocation HEAD), the lint produces a failure naming the c11 → replay_input edge and citing rule 9. | +| AC-5 | The test docstring cites `module-layout.md` rule 9 (AZ-507 cross-component contract surface) and lists the allow-list. | + +## Constraints + +- `frame_source` interface-only requirement: if AST-level disambiguation is not feasible, allow-list only the explicit interface module path. Document the chosen disambiguation strategy in the test docstring. Surface to user if the documented intent and codebase reality disagree. +- The existing test name MAY remain (preserves AZ-270 audit trail) or be renamed; if renamed, update `module-layout.md` rule 9's enforcement-citation. +- Single file modified: `tests/unit/test_az270_compose_root.py`. No production source change. + +## Risks & Mitigation + +**Risk 1 — widening exposes another rule-9 violation**: STOP-and-surface protocol. The implement skill MUST stop and present the additional violation as a scope-decision Choose to the user, NOT auto-bundle into this task. Remediation of any newly-exposed violation is a separate AZ ticket. + +**Risk 2 — false positive on `gps_denied_onboard.frame_source` non-interface module**: documented disambiguation strategy in the test docstring. If wrong, the failure surfaces as a deterministic test failure, not silent drift; surface to user. diff --git a/_docs/03_implementation/cumulative_review_batches_104-109_cycle3_report.md b/_docs/03_implementation/cumulative_review_batches_104-109_cycle3_report.md new file mode 100644 index 0000000..e7c87a3 --- /dev/null +++ b/_docs/03_implementation/cumulative_review_batches_104-109_cycle3_report.md @@ -0,0 +1,178 @@ +# Cumulative Code Review — Cycle 3 — Batches 104–109 + +**Date**: 2026-05-23 +**Scope**: union of files changed across cycle-3 batches 104, 106, 107, 108, 108b, 109 +**Tasks covered**: AZ-777 spec refresh + Phase 1 + Phase 2; AZ-836 (Epic AZ-835 C1); AZ-838 (Epic AZ-835 C2); AZ-839 (Epic AZ-835 C3) + 108b fixture-path fix; AZ-840 (Epic AZ-835 C4) +**Mode**: cumulative (all 7 phases) +**Verdict**: **FAIL** (0 Critical, 1 High, 2 Medium, 0 Low) +**Baseline file**: `_docs/02_document/architecture_compliance_baseline.md` — **still absent** (carried over from cycle 2 retro action), no `## Baseline Delta` section emitted (see Notes) + +## Scope of files reviewed + +**Production source** (6 files): +1. `src/gps_denied_onboard/components/c11_tile_manager/tile_downloader.py` — modified (b104; AZ-777 Phase 1 contract adaptation: `_LIST_PATH` / `_GET_PATH` aligned with `Program.cs:187-209`) +2. `src/gps_denied_onboard/components/c11_tile_manager/route_client.py` — **new** (b107; ~600 LOC; `SatelliteProviderRouteClient`, `RouteSeedResult`, helpers) +3. `src/gps_denied_onboard/components/c11_tile_manager/errors.py` — modified (b107; new `SatelliteProviderRouteError` + `RouteValidationError` + `RouteTransientError` + `RouteTerminalFailureError`) +4. `src/gps_denied_onboard/components/c11_tile_manager/__init__.py` — modified (b107; re-exports new public surface) +5. `src/gps_denied_onboard/replay_input/tlog_route.py` — **new** (b106; `RouteSpec`, `RouteExtractionError`, `extract_route_from_tlog`) +6. `src/gps_denied_onboard/replay_input/__init__.py` — modified (b106; re-exports new public surface) + +**Tests** (10 files): `tests/unit/c11_tile_manager/test_tile_downloader.py` (rewritten, b104; 14 ACs), `tests/unit/replay_input/test_tlog_route.py` (new, b106; 14 tests), `tests/e2e/satellite_provider/__init__.py` + `test_smoke.py` (new, b104; 2 tier-2 tests), `tests/e2e/replay/_operator_pre_flight.py` (new, b108; ~430 LOC), `tests/e2e/replay/conftest.py` (modified, b108+b108b), `tests/e2e/replay/test_operator_pre_flight_driver.py` (new, b108; 11 unit tests), `tests/e2e/replay/_e2e_orchestrator.py` (new, b109; 656 LOC), `tests/e2e/replay/test_e2e_orchestrator_unit.py` (new, b109; 17 unit tests), `tests/e2e/replay/test_az835_e2e_real_flight.py` (new, b109; tier-2 integration). + +**CLI / fixtures** (2 files): `tests/fixtures/derkachi_c6/seed_route.py` (new, b107), `scripts/mint_dev_jwt.py` (new, b104). + +**Compose / env** (2 files): `docker-compose.test.jetson.yml` (modified, b104), `.env.test.example` (modified, b104). + +## Findings + +| # | Severity | Category | File | Title | +|---|----------|----------|------|-------| +| F1 | High | Architecture | `src/gps_denied_onboard/components/c11_tile_manager/route_client.py:56` | `RouteSpec` DTO placement violates AZ-507 cross-component contract surface | +| F2 | Medium | Architecture | `_docs/02_document/module-layout.md` | Module layout stale — cycle-3 additions unregistered (cycle-2 carry-over worsened) | +| F3 | Medium | Maintainability | `tests/unit/test_az270_compose_root.py:194` | `test_ac6_only_compose_root_imports_concrete_strategies` lint scope is narrower than module-layout.md rule 9 | + +### Finding Details + +**F1: `RouteSpec` DTO placement violates AZ-507 cross-component contract surface** (High / Architecture) + +- Location: `src/gps_denied_onboard/components/c11_tile_manager/route_client.py:56` +- Description: `route_client.py` (a `components/c11_tile_manager/*.py` file) imports `RouteSpec` from `gps_denied_onboard.replay_input.tlog_route`. Per `module-layout.md` rule 9 (AZ-507 cross-component contract surface): + + > "the only places a `components//*.py` file may import are: its own subpackage (`gps_denied_onboard.components..*`), `_types/*`, `_types.inference_errors`, `helpers/*`, `config`, `logging`, `fdr_client`, `clock`, `frame_source` (interface only)." + + `replay_input` is not in this allow-list. The architecture rationale: cross-component DTOs reach consumers through `_types/*`, not through cross-cutting coordinator packages. The current placement makes c11 (an Adapter, Layer 4) structurally depend on `replay_input` (a coordinator, Layer 4) — a Layer 4 → Layer 4 cross-cutting edge that the layering table does not declare as allowed. + +- Impact: The dependency is **intentional and documented** — AZ-838 task spec line 19 explicitly specifies `from gps_denied_onboard.replay_input.tlog_route import RouteSpec`, and the route_client docstring acknowledges the source (`Takes a gps_denied_onboard.replay_input.tlog_route.RouteSpec (produced by AZ-836 / C1)`). But "intentional" does not equal "compliant"; the architecture rule was not amended at decompose time, and the AZ-270 lint is too narrow to catch this case (see F3). The next task that imports a similarly-placed DTO will compound the drift. + +- Suggestion: relocate `RouteSpec` (plus `RouteExtractionError` if exported as part of the cross-component surface) to `src/gps_denied_onboard/_types/route.py`. After the move, both `c11_tile_manager.route_client` and `replay_input.tlog_route` import the DTO from `_types`, which is in both modules' allow-lists. AZ-836's `extract_route_from_tlog` continues to live in `replay_input/`; AZ-838's `SatelliteProviderRouteClient` continues to live in `c11_tile_manager/`. The behavioral surface is unchanged. Estimated complexity: 2 SP (move + update imports + verify AZ-838/AZ-836 tests + module-layout.md update). + +- Tasks: AZ-838 (primary — owns the violating import), AZ-836 (secondary — owns the DTO definition). + +**F2: Module layout stale — cycle-3 additions unregistered (cycle-2 carry-over worsened)** (Medium / Architecture) + +- Location: `_docs/02_document/module-layout.md` +- Description: cycle 3 introduced new package files that are not registered in the authoritative file-ownership map. The cycle-2 cumulative review (`98-102`) already flagged 6 unregistered cycle-2 additions (F1 there); none of those carry-overs have been resolved, and cycle 3 added more: + - **c11_tile_manager Internal list** (currently lists `satellite_provider_downloader.py` + `satellite_provider_uploader.py`): missing `_types.py`, `config.py`, `errors.py`, `idempotent_retry.py`, `signing_key.py`, `tile_downloader.py`, `tile_uploader.py`, **`route_client.py`** (cycle-3 NEW). + - **shared/replay_input file list** (currently lists `__init__.py`, `interface.py`, `tlog_video_adapter.py`, `auto_sync.py`, `tests/`): missing `errors.py` (cycle-2 carry), `tlog_ground_truth.py` (cycle-2 carry), **`tlog_route.py`** (cycle-3 NEW). + - **Carried over from cycle-2 review** (still unregistered): `replay_api/` package (7 files), `cli/render_map.py`, `cli/replay_api_entrypoint.py`, `helpers/gps_compare.py`, `helpers/accuracy_report.py`. +- Impact: `/implement` Step 4 (File Ownership) resolves a task's `Component` field against this file. Any future task touching the unregistered areas will hit the BLOCKING ownership check at Step 4 — the skill explicitly STOPs when the component isn't found and forbids guessing from prose. Cycle-3 batches 104–109 happened to operate inside already-listed component directories (c11_tile_manager/**, replay_input/**) so the staleness did not block them, but the next task that needs a new component or extends `replay_api/` will block. +- Suggestion: cycle-3 Step 13 (Update Docs) should reconcile module-layout.md with on-disk reality. The minimum: refresh the c11_tile_manager Internal list, the shared/replay_input file list, and add the cycle-2 carry-over entries (replay_api Per-Component Mapping entry, cli additions, helpers additions, replay_input file list completion). Severity escalates to High if a fourth consecutive cycle leaves the file stale. +- Tasks: AZ-838, AZ-836 (primary, cycle-3 contributors); AZ-700, AZ-697, AZ-699, AZ-701 (secondary, cycle-2 carry). + +**F3: `test_ac6_only_compose_root_imports_concrete_strategies` lint scope is narrower than module-layout.md rule 9** (Medium / Maintainability) + +- Location: `tests/unit/test_az270_compose_root.py:194-219` +- Description: `module-layout.md` rule 9 documents `test_az270_compose_root.test_ac6_only_compose_root_imports_concrete_strategies` as the lint that "enforces this on every `components/**/*.py`". In practice the lint only checks for `gps_denied_onboard.components.` import edges — it walks `components/**/*.py`, parses `ImportFrom` nodes, and flags only when `node.module.startswith("gps_denied_onboard.components.")` with a different leaf component. The full rule-9 allow-list (`_types/*`, `_types.inference_errors`, `helpers/*`, `config`, `logging`, `fdr_client`, `clock`, `frame_source` interface only) is NOT enforced. Imports from `replay_input`, `replay_api`, `runtime_root`, `cli/*`, `frame_source` non-interface modules, etc. all pass the lint silently. F1 is the concrete consequence: the c11 → replay_input import slipped through both code review and the AZ-270 lint. +- Impact: `module-layout.md` rule 9 is documented as enforced; in practice it is partially enforced, partially honor-system. Reviewers (human or AI) reading the rule-9 paragraph reasonably assume the lint covers it; the test name and docstring reinforce that. The asymmetry is a maintainability risk — the rule and its enforcement diverge silently. +- Suggestion: either expand `test_ac6_only_compose_root_imports_concrete_strategies` to enforce the full allow-list (one extra branch in the AST walker), or amend rule 9 to admit the additional imports the codebase actually relies on (with a documented rationale per module). The first is preferable — the rule's intent is structural, and lint coverage matters more than rule wording. +- Tasks: cross-cutting; surface in cycle-3 retrospective. + +## Verdict Logic + +- 0 Critical → no FAIL trigger from Critical +- 1 High (F1) → **FAIL trigger** +- 2 Medium (F2, F3) → not a verdict driver +- 0 Low + +Result: **FAIL** — `/implement` Step 14.5 gate stops. Per `implement/SKILL.md` Step 14.5 + the auto-fix matrix, F1 (High Architecture) **escalates** rather than auto-fixes; F2 + F3 are eligible for Medium-Style auto-fix on the matrix but the High-Architecture finding alone gates the whole report. Re-run requires user direction (Choose A/B/C in the implement skill's Step 14.5 escalation block). + +## Phase-by-Phase Notes + +### Phase 1 — Context Loading + +Inputs read: +- Task specs: AZ-836 (`done/`), AZ-838 (`done/`), AZ-839 (`done/`), AZ-840 (`done/`), AZ-777 (refreshed spec; closure logged in `done/`); AZ-841 (`todo/`), AZ-842 (`todo/`); Epic AZ-835 (`todo/`). +- Batch reports: `batch_104_cycle3_report.md`, `batch_106_cycle3_report.md`, `batch_107_cycle3_report.md`, `batch_108_cycle3_report.md`, `batch_108b_cycle3_report.md`, `batch_109_cycle3_report.md`. +- Architecture / layout: `_docs/02_document/module-layout.md` (rule 9 + per-component sections + Layering Table); `_docs/02_document/architecture.md` (header read-through; full re-read deferred to per-finding evidence). +- Last cumulative review: `_docs/03_implementation/cumulative_review_batches_98-102_cycle2_report.md` (carry-over baseline). +- Restrictions / solution overview: not re-read (already covered in per-batch reviews). +- ADR directory: `_docs/02_document/adr/` does NOT exist; ADR compliance check skipped (logged in Phase 7 below). + +### Phase 2 — Spec Compliance + +Cross-batch promise points (per-batch ACs already verified in batch reports): + +- **AZ-836 (`RouteSpec` + extractor) → AZ-838 (`SatelliteProviderRouteClient`)**: AZ-838 task spec line 19 explicitly specifies `from gps_denied_onboard.replay_input.tlog_route import RouteSpec`. Implementation matches. The DTO contract is not formally documented in `_docs/02_document/contracts/c11_tilemanager/` — Spec-Gap candidate, but downgrades because both producer and consumer are owned by the same Epic (AZ-835) and the Epic spec describes the DTO shape inline. Note (not a separate finding): if `RouteSpec` survives F1 remediation by moving to `_types/route.py`, a contract `_docs/02_document/contracts/shared_types/route.md` is the right home. +- **AZ-838 (`SatelliteProviderRouteClient`) → AZ-839 (C3 fixture, `populate_c6_from_route`)**: the fixture's driver imports `SatelliteProviderRouteClient` and uses `seed_route()`; signature matches AZ-838's `seed_route(spec, *, name=None) -> RouteSeedResult`. Cross-batch wiring sound. +- **AZ-839 (C3 fixture, `PopulatedC6Cache`) → AZ-840 (orchestrator)**: AZ-840's `_e2e_orchestrator.write_effective_replay_config` overlays `c6_tile_cache.root_dir` onto the operator YAML using the cache_root the C3 fixture chose. AZ-840 batch report documents the contract; per-test fixtures consume `PopulatedC6Cache` directly. Sound. +- **AZ-777 contract adaptation (b104) → satellite-provider real endpoints**: `tile_downloader.py` `_LIST_PATH` / `_GET_PATH` now point at the real endpoints (`/api/satellite/tiles/inventory` + `/tiles/{z}/{x}/{y}`). The leftover `_docs/_process_leftovers/2026-05-21_az777_complexity_override.md` 2026-05-21 addendum recorded this as the "largest single sub-deliverable of the refreshed Phase 1". Implementation matches. + +No Spec-Gap findings. + +### Phase 3 — Code Quality + +- All cycle-3 production modules (`tlog_route.py`, `route_client.py`, expanded `errors.py`, modified `tile_downloader.py`) carry module + class + function docstrings consistent with the project pattern (cycle-2 baseline preserved). +- `route_client.py` is ~600 LOC with one class (`SatelliteProviderRouteClient`) plus one DTO (`RouteSeedResult`) plus module-level helpers. The class has 5 public methods (validate, seed_route, _post_route, _poll_route_status, _verify_inventory). Each method is single-responsibility. No method exceeds the 50-line / cyclomatic-10 thresholds enumerated in the skill's Phase 3 list (per code reading; not measured). +- `tlog_route.py` `extract_route_from_tlog` uses Douglas-Peucker for waypoint coarsening — correct choice per AZ-836 spec. +- Tests follow Arrange / Act / Assert per coderule (verified by sampling `test_tlog_route.py` and `test_e2e_orchestrator_unit.py`; no exhaustive enumeration). + +No Code Quality findings. + +### Phase 4 — Security Quick-Scan + +- `route_client.py` HTTP client uses `httpx.Client` with `timeout` parameter (no infinite hangs), argv-style request construction (no shell), and bearer-token auth via the existing C11 plumbing. No secrets in source. +- `route_client.py` JSON request payload built via `json.dumps` on dataclass fields → no injection. +- `route_client.py` URL construction uses `_ROUTE_STATUS_PATH_TPL.format(id=...)` where `id` is a UUID returned by the server — type-bounded, no injection surface. +- `tile_downloader.py` modifications (b104) are confined to `_LIST_PATH` / `_GET_PATH` constants (per batch report); no new auth/parsing surface. +- `scripts/mint_dev_jwt.py` (new, b104): JWT minting tooling for dev/test JWT signing keys. Per file naming (`mint_dev_jwt.py`) and per the `.env.test.example` pairing this is intended for non-prod use; not reviewed line-by-line in this pass. + +No Security findings. + +### Phase 5 — Performance Scan + +- `route_client._poll_route_status` polls with default 5 s interval, max 60 attempts (= 5 min ceiling) using `time.sleep`. Configurable via constructor. Standard polling, not a perf concern. +- `route_client._enumerate_route_tile_coords` walks the route's `regionSizeMeters × N waypoints` tile coverage locally; per AZ-838 batch report this is ~50–100 tiles for the Derkachi route. O(N) over waypoints. +- `tlog_route.extract_route_from_tlog` runs Douglas-Peucker on the active GPS segment; per the unit test, completes in milliseconds for the Derkachi clip. +- `_operator_pre_flight.py` and `_e2e_orchestrator.py` run inside the test harness; performance is bounded by the wall-clock budget (15 min on Tier-2). + +No Performance findings. + +### Phase 6 — Cross-Task Consistency + +- **Sequential Epic chain**: AZ-836 (C1) → AZ-838 (C2) → AZ-839 (C3) → AZ-840 (C4). Each batch's "Files changed" is disjoint at the production level (C1 in `replay_input/`, C2 in `c11_tile_manager/`, C3+C4 in `tests/e2e/replay/`). No conflicting patterns; the test layer wires the production chain together via the orchestrator. +- **Symbol uniqueness**: `RouteSpec`, `RouteExtractionError`, `extract_route_from_tlog`, `SatelliteProviderRouteClient`, `RouteSeedResult`, `SatelliteProviderRouteError`, `RouteValidationError`, `RouteTransientError`, `RouteTerminalFailureError`, `OrchestratorStep`, `OrchestrationFailure`, `OrchestrationReport`, `PopulatedC6Cache` — each defined exactly once across cycle-3 production + tests. No duplicates. +- **AZ-839 b108b fix**: the hot-fix renamed `tile_store_path = cache_root / "tile_store"` → `cache_root / "tiles"` to match `PostgresFilesystemStore` layout. Cross-task consistency preserved (the path AZ-840 reads now matches the path AZ-839 writes). + +No Cross-Task Consistency findings. + +### Phase 7 — Architecture Compliance + +**Layer-direction analysis** (against module-layout.md "Allowed Dependencies" + rule 9): + +- `replay_input/tlog_route.py` (Layer 4 cross-cutting coordinator): imports `_types.geo` (Layer 1), `helpers.gps_compare` (Layer 1), `helpers.wgs_converter` (Layer 1), and intra-package `replay_input.errors` + `replay_input.tlog_ground_truth`. All imports are downward (Layer 4 → Layer 1) or intra-package. Compliant. +- `c11_tile_manager/route_client.py` (Layer 4 component): imports own subpackage (`c11_tile_manager.errors`) + third-party (`httpx`) + **`replay_input.tlog_route.RouteSpec`** — see F1. The cross-cutting `replay_input` is not in c11's allow-list per rule 9. Architecture finding F1 (High). +- `c11_tile_manager/tile_downloader.py` (Layer 4 component): modifications confined to constants. No new cross-component edges introduced. + +**Public API respect**: +- `c11_tile_manager.__init__.py` re-exports the new public surface (`RouteSeedResult`, `SatelliteProviderRouteClient`, plus the new error classes). Consumers calling `from gps_denied_onboard.components.c11_tile_manager import SatelliteProviderRouteClient` reach the package's public surface. ✅ +- `replay_input.__init__.py` re-exports `RouteSpec`, `RouteExtractionError`, `extract_route_from_tlog`. ✅ +- The F1 violation is a public API respect violation in the OPPOSITE direction: `c11.route_client` reaches into `replay_input.tlog_route` (a sub-module path) rather than the package's `__init__` re-export — but the deeper issue is that no direction of this import is rule-9-compliant. + +**Cyclic-dependency check**: +- New edges this cycle: `c11_tile_manager.route_client → replay_input.tlog_route` (F1) + `c11_tile_manager.route_client → c11_tile_manager.errors` (intra-package). +- `replay_input.tlog_route → c11_tile_manager.*`? No (verified via grep). Acyclic. +- `replay_input/__init__.py` re-exports `RouteSpec` from `tlog_route`. No back-edge to c11. +- No new cycles introduced. + +**Duplicate-symbol check**: see Phase 6 — no duplicates. + +**Cross-cutting concerns not locally re-implemented**: none observed. Logging via `logging.getLogger(_COMPONENT)`, FDR via `fdr_client`, helpers consumed from canonical locations. + +**ADR compliance**: `_docs/02_document/adr/` directory does **not exist**. The check is skipped per `code-review/SKILL.md` Phase 7 #6 ("If the directory does not exist or has only the index file, ADRs are skipped — log this skip in the report so the absence is visible"). Carry-over: `module-layout.md` references ADR-001 (monolith), ADR-002 (build-time exclusion), ADR-009 (interface-first DI), ADR-011 (replay-as-configuration) inline; these are documented in `architecture.md` but not as standalone ADR files. If the ADR directory is created in cycle-N (per a future retro action), this skip should retroactively re-evaluate the cycle-3 batches against any ADR whose `Evidence` overlaps the cycle-3 changed-file set. + +**Single Architecture finding**: F1 — c11.route_client imports a non-allow-listed package. Documented but unaddressed at the architecture level. + +## Notes + +- **No `## Baseline Delta` section**: `_docs/02_document/architecture_compliance_baseline.md` was identified in the cycle-2 LESSONS entry (2026-05-20 architecture) and again in the cycle-2 cumulative review notes as a cycle-2 Step 6 (Decompose) prerequisite. The baseline file was NOT created in cycle 2 retrospective and was NOT created in cycle 3 either. Carry-over → cycle-3 retrospective. Without the baseline, "carried over / resolved / newly introduced" structural-violation accounting is not possible; F1 is therefore counted as "newly introduced this cycle" by inspection (`route_client.py` is a cycle-3-new file), and F2 is "carried over from cycle 2 with worsening" by inspection of the cycle-2 cumulative review F1. +- **Cumulative-review cadence drift continues**: `/implement` Step 14.5 says K=3 default. Cycle 3 has 6 completed batches (104, 106, 107, 108, 108b, 109) without a cumulative review until this make-up review. Two cumulative reviews were due (after 104+106+107, after 108+108b+109). Cycle-2 cumulative review (`98-102`) noted the same drift and flagged it for the cycle-2 retrospective; the action did not land. Recurring. Cycle-3 retrospective should pick it up — possible mechanism: a `cumulative_review_pending: true` marker in `_docs/_autodev_state.md` that the implement skill flips on at K-batch boundaries and clears only on review file write, surfacing in the autodev Status Summary footer. +- **AZ-270 lint coverage gap**: F3 documents the gap explicitly. Adjacent: the existing-code flow's Phase A Step 2 (Architecture Baseline Scan) feeds Step 4 (Code Testability Revision) and would also benefit from a tighter lint, since baseline-mode code-review uses the same `module-layout.md` rule 9 as enforcement input. +- **Suite docs (parent)**: `/../docs` does not exist (probed during R1 reconciliation). No suite-level cross-reference applies to this review. + +## Artifacts + +- Verdict consumed by: `/implement` Step 14.5 gate (FAIL → STOP, escalate via Choose A/B/C — auto-fix not eligible for High Architecture). +- F1 carried forward to cycle-3 retrospective for action assignment; remediation candidate: 2-SP refactor task to relocate `RouteSpec` to `_types/route.py`. +- F2 carried forward to cycle-3 Step 13 (Update Docs) at minimum; severity escalation watch if the staleness persists into cycle 4. +- F3 carried forward to cycle-3 retrospective; remediation candidate: 1-SP test-update task to expand `test_ac6_only_compose_root_imports_concrete_strategies`. +- Architecture compliance baseline action: blocked across cycle 2 → cycle 3; surface in cycle-3 retrospective with explicit owner. diff --git a/_docs/04_refactoring/02-az507-routespec-relocation/analysis/adr_impact.md b/_docs/04_refactoring/02-az507-routespec-relocation/analysis/adr_impact.md new file mode 100644 index 0000000..bf58aaa --- /dev/null +++ b/_docs/04_refactoring/02-az507-routespec-relocation/analysis/adr_impact.md @@ -0,0 +1,15 @@ +# ADR Impact — Run 02-az507-routespec-relocation + +**Date**: 2026-05-23 + +## Scan result + +`_docs/02_document/adr/` does not exist in this workspace. No `Status: Accepted` ADR files are in scope. + +**Status**: `No ADRs in scope` — ADR Superseding Gate (refactor SKILL.md phase 2b.1) is satisfied trivially. No Violation rows. No Drift rows. No Aligned rows. Task creation may proceed. + +## Rationale (per SKILL.md phase 2b.1 step 1) + +> "If the directory does not exist or contains only the index, log `No ADRs in scope` to `RUN_DIR/analysis/adr_impact.md` and skip the rest of this gate." + +This run logs the result and proceeds. The architectural rule that the run does enforce — `module-layout.md` rule 9 (AZ-507 cross-component contract surface) — is documented in `module-layout.md` and `architecture.md § Architecture Vision`, not in an ADR. The refactor strengthens that documented rule (by widening its lint enforcement in C03) rather than overturning it; no supersede path is needed. diff --git a/_docs/04_refactoring/02-az507-routespec-relocation/analysis/refactoring_roadmap.md b/_docs/04_refactoring/02-az507-routespec-relocation/analysis/refactoring_roadmap.md new file mode 100644 index 0000000..6b38028 --- /dev/null +++ b/_docs/04_refactoring/02-az507-routespec-relocation/analysis/refactoring_roadmap.md @@ -0,0 +1,70 @@ +# Refactoring Roadmap — Run 02-az507-routespec-relocation + +**Date**: 2026-05-23 +**Run**: `_docs/04_refactoring/02-az507-routespec-relocation/` + +## Weak Points Assessment + +| # | Location | Description | Impact | Proposed Solution | +|---|----------|-------------|--------|------------------| +| W1 | `src/gps_denied_onboard/components/c11_tile_manager/route_client.py:56` | Imports `RouteSpec` from `gps_denied_onboard.replay_input.tlog_route`, violating module-layout.md rule 9 (AZ-507 cross-component contract surface). | High — the next task that imports a similarly-placed DTO compounds the drift; current AZ-270 lint cannot catch it (W3). | C01: relocate the DTO to `_types/route.py`. | +| W2 | `_docs/02_document/module-layout.md` (c11_tile_manager Internal list, shared/replay_input file list) | Stale relative to on-disk reality — cycle-3 additions (`route_client.py`, `tlog_route.py`) and 7 cycle-2-era cycle-internal files are unregistered in their respective sections. | Medium — `/implement` Step 4 ownership check would BLOCK any future task touching unregistered areas. Severity escalates to High if a fourth consecutive cycle leaves it stale. | C02: refresh the c11_tile_manager Internal list, the shared/replay_input file list, and add `_types/route.py`. Defer cycle-2 carry-overs outside these sections. | +| W3 | `tests/unit/test_az270_compose_root.py:194-219` | The AC-6 lint walks `components/**/*.py` and only flags `components. → components.` edges, not the full rule-9 allow-list. | Medium — rule-9 enforcement is partially honor-system; F1 is the concrete consequence. | C03: widen the AST walker to enforce the full allow-list. | + +## Gap Analysis + +| AC of this run | Current state | Target state | +|---|---|---| +| Rule-9 violations resolved | 1 (route_client → replay_input) | 0 | +| `module-layout.md` cycle-3 entries registered | Missing: `route_client.py`, `tlog_route.py`, plus 7 cycle-2-era omissions in two sections | All cycle-3 entries registered; 9 omissions in the c11 + replay_input sections fixed; new `_types/route.py` registered | +| AZ-270 lint scope = rule-9 scope | Narrow (one prefix only) | Full allow-list enforced | + +## Phased Roadmap + +This run is a single phase by intent — three small structural fixes that share the same root cause (rule-9 enforcement gap). Sequencing within the phase: + +1. **C01 → first** (the structural fix). Lands `_types/route.py`, retires the violating import, keeps producer-side back-compat via re-export. +2. **C02 → second** (depends on C01 because the new `_types/route.py` entry needs the file to exist). Documentation refresh; no code touch. +3. **C03 → third** (depends on C01 because the widened lint must see a clean codebase). The new lint becomes a gate for any future PR. + +| Phase | Items | Rationale | +|-------|-------|-----------| +| Phase 1 (this run) | C01, C02, C03 | All three resolve the same cumulative-review FAIL surface; bundling them ensures rule-9 enforcement is consistent across code, doc, and lint after the run. | + +No Phase 2 or Phase 3. The cumulative review's "out of scope" items (cycle-2 doc carry-overs, the shared_types/route.md contract doc, `architecture_compliance_baseline.md`) belong to other tasks and are explicitly deferred — not folded into this roadmap. + +## Hardening tracks + +| Track | Recommendation | Rationale | +|-------|----------------|-----------| +| A — Technical Debt | Skip | The run *is* technical-debt remediation (closing a rule-9 enforcement gap). Adding a separate track would expand scope artificially. | +| B — Performance Optimization | Skip | No performance concern in scope. Relocation is identity-preserving; tests do not measure perf deltas. | +| C — Security Review | Skip | No security surface affected. `RouteSpec` carries waypoint coordinates only (already shipped to operator's tlog input); the move does not change any auth, transport, or input-validation path. | +| D — All of the above | Skip | See A/B/C. | +| E — None | **Selected (default for this run)** | All three changes are themselves the structural fix; orthogonal hardening would dilute scope. The cycle-3 retrospective list captures the broader debt items (cycle-2 carry-overs, baseline doc) for separate runs. | + +This default is recorded explicitly so the user can override at the Phase 2 BLOCKING gate. If the user wants Track C (security audit on the route-extraction path) or Track A (folding the cycle-2 carry-overs into this run), the roadmap and task list will be regenerated. + +## Selected items + +All `Selected`: + +- C01 — Relocate `RouteSpec` to `_types/route.py` (2 SP, low risk). +- C02 — Refresh `module-layout.md` cycle-3 entries (2 SP, low risk). +- C03 — Widen `test_az270_compose_root` lint to full rule-9 allow-list (2 SP, medium risk). + +**Total**: 6 SP across 3 tasks. Each task is within the user-rule cap (≤ 5 SP per task; recommended 2-3). + +## Applicability gate + +| Recommendation | Status | Notes | +|---|---|---| +| C01 | Selected | No constraint mismatches; identity-preserving move; backward compat via re-export. | +| C02 | Selected | Doc-only; no test impact; scope-disciplined (cycle-2 carry-overs explicitly deferred). | +| C03 | Selected | Risk-flagged: widening may expose unrelated rule-9 violation. STOP-and-surface protocol applies if encountered. | + +No `Rejected`, no `Experimental only`, no `Needs user decision`. The Phase 2 applicability gate passes for task creation. + +## ADR-supersede gate + +`No ADRs in scope` — see `adr_impact.md`. Gate satisfied; no Violation/Drift/Aligned rows. diff --git a/_docs/04_refactoring/02-az507-routespec-relocation/analysis/research_findings.md b/_docs/04_refactoring/02-az507-routespec-relocation/analysis/research_findings.md new file mode 100644 index 0000000..cce8d26 --- /dev/null +++ b/_docs/04_refactoring/02-az507-routespec-relocation/analysis/research_findings.md @@ -0,0 +1,66 @@ +# Research Findings — Run 02-az507-routespec-relocation + +**Date**: 2026-05-23 +**Mode**: guided +**Scope**: structural relocation of one DTO + module-layout doc refresh + lint widening + +## Project Constraint Matrix (extracted) + +| Constraint | Source | Statement | +|-----------|--------|-----------| +| AZ-507 cross-component contract surface | `_docs/02_document/architecture.md` § Architecture Vision; `_docs/02_document/module-layout.md` rule 9 | `components//*.py` may only import from `_types/*`, `_types.inference_errors`, `helpers/*`, `config`, `logging`, `fdr_client`, `clock`, `frame_source` (interface only), and its own subpackage. | +| Cross-component DTOs live in `_types/*` | `_types/geo.py`, `_types/tile.py`, `_types/inference.py`, `_types/calibration.py`, `_types/pose.py`, `_types/state.py`, `_types/nav.py`, `_types/manifests.py`, `_types/vpr.py`, `_types/matcher.py`, `_types/matching.py`, `_types/rerank.py`, `_types/thermal.py`, `_types/emitted.py`, `_types/fc.py` (15 existing DTO files) | The user-confirmed precedent. Every shared DTO sits under `_types/`. The pattern is explicit at the package level: `_types/__init__.py` is just a marker (`"""Cross-component DTOs (type-only stubs)."""`). | +| AZ-270 lint coverage | `_docs/02_document/module-layout.md` rule 9 (cites `test_az270_compose_root.test_ac6_only_compose_root_imports_concrete_strategies`) | Documented as enforced by the lint; F3 of cycle-3 cumulative review confirms the lint scope is narrower than the rule. | +| Frozen + slots DTO contract | AZ-355 AC-2 (cited in `_types/geo.py`) | DTOs that cross component boundaries must use `frozen=True, slots=True` to prevent mutation-through-aliasing. | +| Epic AZ-835 acceptance criteria | `_docs/02_tasks/done/AZ-835_e2e_real_flight_validation_epic.md` and child task specs (AZ-836..AZ-840) | The replay-flow behaviour must remain functionally identical after the refactor — RouteSpec waypoint extraction, satellite-provider POST, e2e orchestrator behaviour. | +| Backward-compat for test imports | tests/* (5 files import RouteSpec from `replay_input.tlog_route` directly) | Test code is allowed to use module-level paths; only `components//*.py` is gated by rule 9. Re-export from `tlog_route.py` keeps test imports stable, so updating tests is hygiene rather than correctness. | + +## Current state analysis + +`RouteSpec` is currently defined at `gps_denied_onboard.replay_input.tlog_route:54-79` and re-exported from `gps_denied_onboard.replay_input` (`__init__.py:34`). The producer (`extract_route_from_tlog` at `tlog_route.py:82`) lives alongside the DTO in the same module — that part is correct (the function is a `replay_input/` concern, not a `_types/` concern). The DTO itself is consumed across a component boundary (c11) which makes it a cross-component DTO by behaviour, but its file home does not reflect that. Every other cross-component DTO in the codebase lives under `_types/*`. The asymmetry is the F1 finding. + +**Strengths to preserve**: + +- `RouteSpec` is `frozen=True, slots=True` — already AZ-355-compliant; the move does not relax this. +- The extractor (`extract_route_from_tlog`) is correctly placed in `replay_input/` and uses the DTO via local import; this composition is preserved post-move. +- Tests cover both producer-side (14 unit tests) and consumer-side (full route_client AC suite plus integration). Phase 6 has a strong safety net. + +**Weakness being corrected**: + +- The DTO's file home does not match its semantic role (cross-component contract surface). +- The AZ-270 lint cannot detect the asymmetry because its check is narrower than the rule it claims to enforce. + +## Alternative approaches considered + +| # | Approach | Verdict | Why | +|---|----------|---------|-----| +| 1 | Move `RouteSpec` to `_types/route.py` (the recommended path) | **Selected** | Matches the user-confirmed precedent (`_types/inference.py`, `_types/tile.py`, etc.), satisfies rule 9 at c11's import site, identity-preserving (Python class object identity is preserved across imports), behaviour-neutral. | +| 2 | Move `RouteSpec` to `_types/replay.py` (group with other replay-related types if they appear later) | Rejected | No other replay-related shared DTOs exist today. Naming the file `route.py` mirrors the naming convention of other `_types/*.py` files (one DTO topic per file: `geo`, `tile`, `pose`, `nav`, etc.). Premature speculative grouping. | +| 3 | Move `RouteSpec` to `_types/contracts/route.py` (introduce a sub-namespace) | Rejected | `_types/` is currently flat. Introducing a sub-namespace for one DTO is over-engineering and would require updating the rule-9 allow-list (`_types/*` already matches recursively in the lint, but the documentation pattern would diverge). | +| 4 | Amend rule 9 to admit `replay_input.tlog_route` as an allowed import for components | Rejected (architecture-change path; option D in the original FAIL gate) | The user explicitly chose option B (mechanical refactor) over option D (rule amendment). Option 4 would weaken rule 9 and break the layering invariant, which is why the user rejected it. | +| 5 | Keep `RouteSpec` in `replay_input/tlog_route.py` and add a custom shim under `_types/` that re-exports it (no real move) | Rejected | Cosmetic — does not satisfy the underlying rule because the c11 import would still resolve to a `replay_input` module via the shim. The lint's correct widened form (C03) would still flag the original location as the canonical home. | + +**Selected: Approach 1.** No library replacement, no SDK addition, no framework introduction. Therefore the `context7` per-mode verification gate (SKILL phase 2a) is not triggered — the gate fires only for replacement libraries/SDKs/frameworks/services. This is a structural code move within the existing codebase. + +## API capability verification + +**Not applicable.** The refactor introduces no new library, SDK, framework, or service. The "replacement" is the file home of a dataclass within the same Python package. No `context7` lookup is required (the gate is explicit: "for every replacement library/SDK/framework"). No MVE is required (no external API to verify). The project's pinned mode is unchanged because no mode exists to pin — it's a pure-Python dataclass relocation. + +## Constraint-fit table + +| Recommendation | Pinned mode/config | Constraints checked | API capability evidence | Mismatches/disqualifiers | Status | +|---|---|---|---|---|---| +| C01 — relocate `RouteSpec` to `_types/route.py` | N/A — Python dataclass, no library mode | AZ-507 rule 9, frozen+slots invariant (AZ-355), Epic AZ-835 ACs, test backward compat | N/A — no external API | None | Selected | +| C02 — refresh `module-layout.md` | N/A — documentation | AZ-507 rule 9 (the rule the doc enforces), scope discipline (cycle-2 carry-overs deferred to a separate task) | N/A | None | Selected | +| C03 — widen AZ-270 lint | N/A — internal AST walker, stdlib `ast` module | Rule-9 allow-list as the predicate; preserves existing AC-6 narrow check as a strict subset | N/A — stdlib only | Risk: may expose unrelated rule-9 violation (mitigated by STOP-and-surface protocol if encountered) | Selected | + +All three changes are `Selected`. No `Rejected`, `Experimental only`, or `Needs user decision` rows — the applicability gate (Phase 2 BLOCKING) passes for all three. + +## References + +- `_docs/02_document/architecture.md` § Architecture Vision (AZ-507 cross-component contract surface) +- `_docs/02_document/module-layout.md` rule 9 (AZ-507 enforcement) +- `_docs/03_implementation/cumulative_review_batches_104-109_cycle3_report.md` (F1, F2, F3 — the source findings) +- `src/gps_denied_onboard/_types/geo.py` (canonical pattern for `_types/.py`) +- `src/gps_denied_onboard/_types/inference.py`, `_types/tile.py`, `_types/calibration.py` (additional precedent — user-cited examples) +- `tests/unit/test_az270_compose_root.py:194-219` (current narrow lint) diff --git a/_docs/04_refactoring/02-az507-routespec-relocation/baseline_metrics.md b/_docs/04_refactoring/02-az507-routespec-relocation/baseline_metrics.md new file mode 100644 index 0000000..185bb1f --- /dev/null +++ b/_docs/04_refactoring/02-az507-routespec-relocation/baseline_metrics.md @@ -0,0 +1,100 @@ +# Baseline Metrics — Run 02-az507-routespec-relocation + +**Date**: 2026-05-23 +**Run**: `_docs/04_refactoring/02-az507-routespec-relocation/` +**Mode**: guided +**Source**: cycle-3 cumulative review (`_docs/03_implementation/cumulative_review_batches_104-109_cycle3_report.md`) — F1, F2, F3 +**Scope**: mechanical relocation of cross-component DTO + module-layout doc refresh + AZ-270 lint scope expansion + +## Why a minimal baseline is appropriate for this run + +The standard Phase-0 baseline metric grid (overall coverage, complexity, code smells, performance, dependencies, build time) is **not the right instrument** for this refactoring run. The work is a structural relocation of one frozen dataclass + a documentation refresh + a lint widening. Behaviour does not change; performance does not change; coverage does not change; dependency count does not change. A LOC-and-cyclomatic-complexity baseline would record near-zero deltas and would obscure the actual signal — whether the architectural rule (`module-layout.md` rule 9) is satisfied after the run. + +What matters here, and is captured below, is: + +1. The **structural baseline**: one rule-9 violation today (F1). +2. The **test baseline**: which tests cover the affected import paths and that they pass at HEAD (the safety net for Phase 4). +3. The **doc baseline**: which artifacts are stale (F2) and what "complete" looks like. +4. The **lint baseline**: what AZ-270 currently catches vs. what rule 9 says it should catch (F3). + +Phases 5/6 verify that (a) the structural baseline goes from 1 → 0 rule-9 violations, (b) every test still passes, (c) the doc baseline is reconciled, and (d) the lint baseline is widened. + +## 1. Structural baseline (rule-9 violations) + +Source of truth: `_docs/02_document/module-layout.md` rule 9 (AZ-507 cross-component contract surface). + +| # | File | Importer (Component) | Imported (Module) | Allow-listed for importer? | +|---|------|----------------------|-------------------|----------------------------| +| 1 | `src/gps_denied_onboard/components/c11_tile_manager/route_client.py:56` | `c11_tile_manager` | `gps_denied_onboard.replay_input.tlog_route` (`RouteSpec`) | **NO** — `replay_input` not in c11's allow-list | + +Search method: `rg "^from gps_denied_onboard\." src/gps_denied_onboard/components` filtered against the allow-list (`_types/*`, `_types.inference_errors`, `helpers/*`, `config`, `logging`, `fdr_client`, `clock`, `frame_source`). + +**Target post-run**: 0 violations. + +## 2. Test baseline (safety net for Phase 4) + +Files that import `RouteSpec`, `SatelliteProviderRouteClient`, or `RouteSeedResult` (i.e. the symbols the relocation touches): + +**Production source** (must be updated): +- `src/gps_denied_onboard/components/c11_tile_manager/route_client.py` — defines the import to be re-pointed +- `src/gps_denied_onboard/components/c11_tile_manager/__init__.py` — public API re-exports (no import path change) +- `src/gps_denied_onboard/replay_input/tlog_route.py` — defines `RouteSpec` today (will lose the local definition, gain an import + alias) +- `src/gps_denied_onboard/replay_input/__init__.py` — public API re-exports (will re-export from `_types.route` instead of `tlog_route`) + +**Tests** (verify still pass; update imports only if they reach into the pre-relocation internal path `replay_input.tlog_route` directly): +- `tests/unit/replay_input/test_tlog_route.py` (14 tests; producer-side) +- `tests/unit/c11_tile_manager/test_route_client.py` (consumer-side unit tests) +- `tests/integration/c11_tile_manager/test_route_client_e2e.py` (integration) +- `tests/e2e/replay/conftest.py` +- `tests/e2e/replay/_operator_pre_flight.py` +- `tests/e2e/replay/test_operator_pre_flight_driver.py` +- `tests/e2e/replay/test_operator_pre_flight_integration.py` +- `tests/e2e/replay/_e2e_orchestrator.py` +- `tests/e2e/replay/test_e2e_orchestrator_unit.py` +- `tests/fixtures/derkachi_c6/seed_route.py` + +**HEAD test status (asserted, not measured here)**: per cycle-3 batch reports 104, 106, 107, 108, 108b, 109, every committed batch ended with passing tests at the per-batch full run. The cumulative review (FAIL on F1) is a static-analysis verdict, not a test-run verdict — no test failures are attributable to F1 today. Phase 4 will run the affected test files first; Phase 6 runs the project's full test gate per the existing-code flow's test policy. + +## 3. Doc baseline (F2 surface area) + +`_docs/02_document/module-layout.md` is stale relative to on-disk reality. The following entries diverge today: + +**c11_tile_manager — Internal list** lists 2 files (`satellite_provider_downloader.py`, `satellite_provider_uploader.py`); on-disk has 8 internal files plus `route_client.py` (cycle-3 NEW). Missing entries: `_types.py`, `config.py`, `errors.py`, `idempotent_retry.py`, `signing_key.py`, `tile_downloader.py`, `tile_uploader.py`, `route_client.py`. + +**shared/replay_input file list** lists `__init__.py`, `interface.py`, `tlog_video_adapter.py`, `auto_sync.py`, `tests/`; on-disk adds `errors.py` (cycle-2 carry), `tlog_ground_truth.py` (cycle-2 carry), `tlog_route.py` (cycle-3 NEW). After the relocation, `tlog_route.py` stays (it still owns `extract_route_from_tlog`); `_types/route.py` is added. + +**Cycle-2 carry-overs** still unaddressed (out of this run's scope unless the user expands it; surfaced in F2 of the cumulative review): +- `replay_api/` package (7 files; needs Per-Component Mapping entry). +- `cli/render_map.py`, `cli/replay_api_entrypoint.py` (need Shared section entries). +- `helpers/gps_compare.py`, `helpers/accuracy_report.py` (need Shared section entries). + +**Target post-run** (in-scope): c11_tile_manager Internal list refreshed (route_client + the 7 long-standing internals); shared/replay_input file list refreshed (tlog_route + tlog_ground_truth + errors); new `_types/route.py` registered. Cycle-2 carry-overs are deferred to a separate doc-only task unless user expands scope. + +## 4. Lint baseline (F3) + +`tests/unit/test_az270_compose_root.py:194-219` (`test_ac6_only_compose_root_imports_concrete_strategies`) walks `src/gps_denied_onboard/components/**/*.py` and flags only edges whose `node.module` starts with `gps_denied_onboard.components.` AND whose leaf-component is not the importer's component. The full rule-9 allow-list (8 prefixes plus `frame_source` interface-only restriction) is NOT enforced. + +**Concrete miss demonstrated by F1**: the c11 → replay_input edge passes this lint silently because `replay_input` is not under `components/`. + +**Target post-run** (in-scope): expand the lint to enforce rule 9's full allow-list. Remaining design choices (whether to allow `frame_source` non-interface modules, whether to treat `runtime_root` exception case-sensitively) are addressed in C03's task spec. + +## 5. Functionality inventory + +This run touches no public-feature surfaces. The DTO `RouteSpec` continues to be re-exported from `gps_denied_onboard.replay_input` (the public package), so consumers using `from gps_denied_onboard.replay_input import RouteSpec` see no change. Consumers reaching into `replay_input.tlog_route` directly (an internal-module path) will need their imports updated — this set is small and lives entirely under `tests/`. There is no operator-facing CLI / endpoint / config schema change. + +## Self-verification + +- [x] RUN_DIR created with auto-incremented prefix (`02-az507-routespec-relocation`; previous: `01-testability-refactoring`) +- [x] All metric categories reasoned about — standard categories noted N/A with reason; relevant baselines (structural, test, doc, lint) captured +- [x] Functionality inventory complete (no functionality change in scope) +- [x] Measurements are reproducible (rg + glob commands documented) + +## BLOCKING — Phase 0 gate + +Awaiting user confirmation of: + +1. The minimal-baseline rationale (no LOC/coverage/perf metrics for a mechanical relocation). +2. The structural / test / doc / lint baseline above as the "before" state Phase 6 will compare against. +3. The scope decision: cycle-2 doc carry-overs are **OUT** of this run unless explicitly expanded. + +If confirmed, Phase 1 produces `RUN_DIR/list-of-changes.md` (already drafted alongside this file as the guided-mode input). diff --git a/_docs/04_refactoring/02-az507-routespec-relocation/discovery/logical_flow_analysis.md b/_docs/04_refactoring/02-az507-routespec-relocation/discovery/logical_flow_analysis.md new file mode 100644 index 0000000..d228577 --- /dev/null +++ b/_docs/04_refactoring/02-az507-routespec-relocation/discovery/logical_flow_analysis.md @@ -0,0 +1,59 @@ +# Logical Flow Analysis — Run 02-az507-routespec-relocation + +**Date**: 2026-05-23 +**Scope**: data path of `RouteSpec` from producer (replay_input) to consumer (c11_tile_manager) and back to operator-pre-flight orchestration + +## Documented flow (from architecture / Epic AZ-835 spec) + +``` +tlog (binary) ──► extract_route_from_tlog (replay_input/tlog_route) + └─► RouteSpec (frozen dataclass, immutable) + └─► SatelliteProviderRouteClient.seed_region (components/c11_tile_manager/route_client) + └─► RouteSeedResult ─► satellite-provider POST /api/satellite/route + ─► (HTTP success) tile coverage primed +``` + +## Trace through code (HEAD) + +| Step | File | Behaviour | +|------|------|-----------| +| 1. Produce | `replay_input/tlog_route.py:166` (`extract_route_from_tlog` return) | Constructs `RouteSpec(waypoints, suggested_region_size_meters, source_tlog, source_segment, total_distance_meters)` | +| 2. Hold | (consumer-side variable) | `RouteSpec` instance is `frozen=True, slots=True` — cannot be mutated by either side | +| 3. Consume | `components/c11_tile_manager/route_client.py:56` import | Reads `route.waypoints`, `route.suggested_region_size_meters` to build the satellite-provider POST body | +| 4. Validate | `components/c11_tile_manager/route_client.py` (RouteValidationError path) | Validates `route` shape against c11's RouteValidationError preconditions; pure read access | +| 5. Carry | `tests/e2e/replay/_operator_pre_flight.py:72` import | Operator-pre-flight harness threads the same RouteSpec through the e2e flow | + +## Identity & equality semantics post-relocation + +The relocation moves the **definition** of `RouteSpec` from `gps_denied_onboard.replay_input.tlog_route` to `gps_denied_onboard._types.route`. After the move: + +- Python's class identity is preserved across imports — `gps_denied_onboard.replay_input.tlog_route.RouteSpec is gps_denied_onboard._types.route.RouteSpec` ⇒ `True` (the same class object is bound at two names). +- `dataclasses.is_dataclass(...)`, `isinstance(...)`, `__eq__`, and `__hash__` are unchanged because they derive from the class object, not from the import path. +- `frozen=True, slots=True` semantics are preserved (no per-instance dict, no setattr after construction). +- The `__module__` attribute of the class becomes `gps_denied_onboard._types.route` (not `gps_denied_onboard.replay_input.tlog_route`). This is observable via: + - `pickle` (module path is encoded; pickled objects from before the move would fail to unpickle after — but no production code path pickles `RouteSpec`; checked: no `pickle.dumps(route)` or equivalent in src/ or tests/) + - `repr(RouteSpec)` (shows `` post-move) + - `RouteSpec.__module__` (changes — but no test inspects this; checked: no `__module__` assertion in tests/) + +## Contradictions / data-loss / wasted-work checks + +Per Phase 1 step 1c categories: + +- **Fixed-size vs dynamic-size assumptions**: N/A — `RouteSpec.waypoints` is `tuple[tuple[float, float], ...]`, length is data-driven (1 to `max_waypoints`). No fixed-size pad/truncate path. +- **Loop scoping**: N/A — RouteSpec is a leaf DTO, no internal loop semantics. +- **Wasted computation**: N/A — relocation does not change call sites. +- **Silent data loss**: N/A — relocation is a name-only change at the type level; the values stored in `RouteSpec` instances are unchanged. +- **Doc drift**: confirmed by F2 of cumulative review — `module-layout.md` diverges from on-disk reality. Remediation is in scope as C02. + +## Cross-component edge analysis (rule-9 audit, post-relocation) + +| Edge | Importer | Imported | Allow-listed? | Status | +|------|----------|----------|---------------|--------| +| Pre-relocation | `c11_tile_manager/route_client.py` | `replay_input.tlog_route.RouteSpec` | NO | violation (F1) | +| Post-relocation | `c11_tile_manager/route_client.py` | `_types.route.RouteSpec` | YES (`_types/*` is in c11's allow-list) | compliant | + +No other rule-9 cross-component edge becomes a violation as a side effect of this move. The producer side (`replay_input/tlog_route.py` → `_types/route.py`) is a coordinator → DTO edge, which is always allowed (DTOs have no allow-list restriction; they're consumed everywhere). + +## Conclusion + +The relocation is a pure structural change with no behavioural, performance, or contract-shape side effects. The only observable difference is `RouteSpec.__module__`, which is not asserted on by any code path. Phase 4 execution can proceed as a mechanical move; Phase 6 verification is satisfied if all tests pass and the rule-9 audit reports zero violations. diff --git a/_docs/04_refactoring/02-az507-routespec-relocation/list-of-changes.md b/_docs/04_refactoring/02-az507-routespec-relocation/list-of-changes.md new file mode 100644 index 0000000..5b10f19 --- /dev/null +++ b/_docs/04_refactoring/02-az507-routespec-relocation/list-of-changes.md @@ -0,0 +1,71 @@ +# List of Changes + +**Run**: 02-az507-routespec-relocation +**Mode**: guided +**Source**: `_docs/03_implementation/cumulative_review_batches_104-109_cycle3_report.md` (cycle-3 cumulative review, FAIL verdict, F1 + F2 + F3) +**Date**: 2026-05-23 + +## Summary + +Resolve the cycle-3 cumulative review's FAIL verdict by (a) relocating the `RouteSpec` DTO to its rule-9-compliant home in `_types/route.py`, (b) refreshing the stale `module-layout.md` cycle-3 file inventory, and (c) widening the AZ-270 lint to enforce the full rule-9 allow-list rather than only `components → components` edges. The work is mechanical — no behaviour, no performance, no contract shape changes. + +## Changes + +### C01: Relocate `RouteSpec` DTO from `replay_input/tlog_route.py` to `_types/route.py` + +- **File(s)**: + - **NEW**: `src/gps_denied_onboard/_types/route.py` — owns the `RouteSpec` dataclass definition (frozen, slots, with full docstring carried over verbatim). + - **MOD**: `src/gps_denied_onboard/replay_input/tlog_route.py` — remove the local `RouteSpec` class definition (lines 54–79); add `from gps_denied_onboard._types.route import RouteSpec` near the existing `_types.geo` import; keep `RouteSpec` in `__all__` so `from replay_input.tlog_route import RouteSpec` continues to resolve (test code uses this path; it's a re-export, not a violation). + - **MOD**: `src/gps_denied_onboard/replay_input/__init__.py` — change line 34 to import `RouteSpec` from `gps_denied_onboard._types.route` directly (canonical), keep importing `RouteExtractionError` and `extract_route_from_tlog` from `tlog_route` (they stay there). + - **MOD**: `src/gps_denied_onboard/components/c11_tile_manager/route_client.py:56` — change to `from gps_denied_onboard._types.route import RouteSpec` (the actual rule-9 fix). Also update the docstring snippet at file-top that reads `Takes a gps_denied_onboard.replay_input.tlog_route.RouteSpec` → `Takes a gps_denied_onboard._types.route.RouteSpec`. + - **MOD (optional, hygiene)**: test imports — 5 test files (`tests/unit/replay_input/test_tlog_route.py:46`, `tests/unit/c11_tile_manager/test_route_client.py:49`, `tests/e2e/replay/_operator_pre_flight.py:72`, `tests/e2e/replay/test_e2e_orchestrator_unit.py:37`, `tests/e2e/replay/test_operator_pre_flight_driver.py:61`) currently import `RouteSpec` from `replay_input.tlog_route`. They continue to work via the re-export (see above). Updating them to import from `_types.route` is hygiene, not correctness; recommended but not blocking. The integration test `tests/integration/c11_tile_manager/test_route_client_e2e.py:26` imports `extract_route_from_tlog` (not `RouteSpec`) — no change needed. The lazy import in `tests/e2e/replay/conftest.py:406` and the CLI fixture `tests/fixtures/derkachi_c6/seed_route.py:80` import `extract_route_from_tlog` only — no change needed. + +- **Problem**: `components/c11_tile_manager/route_client.py:56` imports `RouteSpec` from `gps_denied_onboard.replay_input.tlog_route`. Per `module-layout.md` rule 9, `components//*.py` may only import from a finite allow-list (`_types/*`, `_types.inference_errors`, `helpers/*`, `config`, `logging`, `fdr_client`, `clock`, `frame_source` interface only). `replay_input` is not in this list — it's a Layer-4 cross-cutting coordinator, and Layer-4 → Layer-4 cross-cutting edges are not declared as allowed in the layering table. The import was committed in batch 107 (AZ-838); the AZ-270 lint did not catch it because the lint walks only `components → components` edges (see C03). +- **Change**: Move the DTO definition to `_types/route.py`, where it sits among the other shared DTOs (`_types/geo.py`, `_types/tile.py`, `_types/inference.py`, etc.). Update the c11 import to point at the new location. Producer-side (`replay_input/tlog_route.py`) re-imports the DTO so its own return type, `__all__`, and existing test imports keep working — that's a coordinator importing from `_types/*`, a flow that is always allowed for non-`components/` modules. +- **Rationale**: `_types/*` is the architecturally designated home for cross-component DTOs (per AZ-507; per `_docs/02_document/architecture.md` `## Architecture Vision`). Every other shared DTO already lives there. Putting `RouteSpec` there makes the c11 → DTO edge a `components/` → `_types/*` edge, which is allow-listed. This matches the pattern for `_types/inference.py`, `_types/tile.py`, `_types/calibration.py`, `_types/pose.py`, etc. — the user-confirmed precedent. +- **Constraint Fit**: + - AZ-507 cross-component contract surface — satisfied (the violating edge becomes compliant). + - Epic AZ-835 acceptance criteria — preserved; behaviour unchanged. + - `RouteSpec` immutability (`frozen=True, slots=True`) — preserved verbatim. + - Backward compatibility for producer-side test imports (`from replay_input.tlog_route import RouteSpec`) — preserved via re-export. + - No public-API / CLI / endpoint shape change — confirmed in baseline_metrics §5. +- **Risk**: low (mechanical move; identity-preserving; logical-flow analysis confirms no observable side effects beyond `__module__`, which no code asserts on). +- **Dependencies**: None. + +### C02: Refresh `module-layout.md` to register cycle-3 additions + new `_types/route.py` + +- **File(s)**: `_docs/02_document/module-layout.md` (single file). +- **Problem**: The cumulative review's F2 surfaces that `module-layout.md` is stale. Cycle-2 carry-overs are still unaddressed; cycle 3 added more entries that are not registered. Specifically: + - **c11_tile_manager Internal list** is missing `_types.py`, `config.py`, `errors.py`, `idempotent_retry.py`, `signing_key.py`, `tile_downloader.py`, `tile_uploader.py`, **`route_client.py`** (cycle-3 NEW from batch 107). + - **shared/replay_input file list** is missing `errors.py` (cycle-2 carry), `tlog_ground_truth.py` (cycle-2 carry), **`tlog_route.py`** (cycle-3 NEW from batch 106). + - **`_types/` file list** does not yet include `route.py` (added in C01). +- **Change**: Append the missing entries to bring `module-layout.md` in sync with on-disk reality for the c11_tile_manager, replay_input, and `_types/` sections. Add `_types/route.py` to the `_types/` section with a one-line description (consistent with how the other `_types/*.py` files are listed). Cycle-2 carry-overs *outside* these three sections (`replay_api/`, `cli/render_map.py`, `cli/replay_api_entrypoint.py`, `helpers/gps_compare.py`, `helpers/accuracy_report.py`) are NOT in this run's scope — they remain on the cycle-3 retrospective list and should be addressed in a follow-up doc task that is independent of the architectural fix here. +- **Rationale**: `/implement` Step 4 (File Ownership) treats `module-layout.md` as authoritative; staleness there is a BLOCKING gate when a future task touches an unregistered area. F2 is currently Medium; the cumulative review notes severity escalates to High if a fourth consecutive cycle leaves it stale. Resolving the cycle-3 portion now keeps the fix scoped to the same surface as C01 + the route_client + tlog_route additions that triggered the cumulative review in the first place. +- **Constraint Fit**: + - `module-layout.md` rule 9 — strengthened (the document now reflects what `_types/*` actually owns). + - No code or behavioural change. + - Scope discipline — does NOT pull in cycle-2 carry-overs outside the run's three sections; they are deferred to a separate task. +- **Risk**: low (doc-only; reviewable by diff; no test impact). +- **Dependencies**: C01 (the `_types/route.py` entry depends on the file existing). + +### C03: Expand `test_az270_compose_root.test_ac6_only_compose_root_imports_concrete_strategies` to enforce the full rule-9 allow-list + +- **File(s)**: + - **MOD**: `tests/unit/test_az270_compose_root.py:194-219` — replace the current narrow check (`node.module.startswith("gps_denied_onboard.components.")` with a different leaf-component) with a check that walks `components/**/*.py`, parses each `ImportFrom`, and for any `node.module` starting with `gps_denied_onboard.` asserts the importable target is in the rule-9 allow-list (i.e. matches one of: `gps_denied_onboard.components..*`, `gps_denied_onboard._types.*`, `gps_denied_onboard._types.inference_errors`, `gps_denied_onboard.helpers.*`, `gps_denied_onboard.config`, `gps_denied_onboard.logging`, `gps_denied_onboard.fdr_client`, `gps_denied_onboard.clock`, `gps_denied_onboard.frame_source` interface-only). + - **MOD (test docstring)**: update the test's docstring to cite the full rule-9 paragraph, not just AC-6 of AZ-270. +- **Problem**: F3 of the cumulative review documents that `module-layout.md` rule 9 is described as "enforced by `test_az270_compose_root.test_ac6_only_compose_root_imports_concrete_strategies`", but the lint actually checks only one of the eight allow-listed prefixes — only the `gps_denied_onboard.components.` exclusion. Imports from `replay_input`, `replay_api`, `runtime_root`, `cli/*`, and `frame_source` non-interface modules pass silently. F1 is the concrete consequence; the next task that imports from a similarly-placed module would compound the drift. +- **Change**: Widen the AST walker to a single-branch decision: "is the imported module rooted in `gps_denied_onboard.` AND not in the rule-9 allow-list (parameterised against the importer's own component for the `components..*` clause)? → fail with a message that names the offending edge and the rule." The existing error message format (compose-root test failure) is preserved; only the predicate is widened. +- **Rationale**: Lint coverage matters more than rule wording. F3 surfaces a maintainability risk: the rule and its enforcement diverge silently. Closing the gap forecloses the F1 class of regressions at lint time, not at cumulative-review time. +- **Constraint Fit**: + - `module-layout.md` rule 9 — enforced as documented. + - Existing AZ-270 AC-6 — preserved (the new check is a strict superset of the old check). + - No behaviour change in production code. + - Self-check: running the widened lint at HEAD (before C01 lands) reproduces F1 as a lint failure; running it at the C01 + C02 tip reproduces zero violations. This is the test the run hinges on. +- **Risk**: medium — the widening will catch any *other* in-flight rule-9 violation hiding in the codebase, which could surface a second remediation task. If the widened lint exposes an unrelated violation, the implement skill should STOP and surface it for a scope decision rather than auto-bundle. Risk is reduced by the fact that rule-9 audits during code review have not flagged anything else. +- **Dependencies**: C01 must land first (otherwise the widened lint fails on the very edge C01 fixes; running tests in the order C01 → C03 means C03 sees a clean baseline). C02 ordering is independent. + +## Out of scope for this run + +- **Cycle-2 module-layout carry-overs** outside the three sections C02 touches (`replay_api/` Per-Component Mapping, `cli/render_map.py`, `cli/replay_api_entrypoint.py`, `helpers/gps_compare.py`, `helpers/accuracy_report.py`) — recorded as cycle-3 retrospective follow-up; needs a separate doc task with its own AZ ID. +- **Contract documentation for `RouteSpec` at `_docs/02_document/contracts/shared_types/route.md`** — the cumulative review noted this as a possible Spec-Gap follow-up. It is a documentation addition, not a refactor. Defer to whoever owns the Spec-Gap workflow; do not bundle here. +- **`architecture_compliance_baseline.md`** — separate cycle-2 retrospective action that has been outstanding for two cycles; recorded as such in the cumulative review's footer note. Out of this run's scope. diff --git a/_docs/_autodev_state.md b/_docs/_autodev_state.md index b90b7e2..b8ca973 100644 --- a/_docs/_autodev_state.md +++ b/_docs/_autodev_state.md @@ -6,9 +6,9 @@ step: 10 name: Implement status: in_progress sub_step: - phase: 7 - name: batch-loop - detail: "batch 110 next; full-suite gate after AZ-840 C4 ship" + phase: 3 + name: refactor-safety-net + detail: "02-az507; Phase 2 confirmed; ready for Phase 3 safety-net check in fresh session" retry_count: 0 cycle: 3 tracker: jira