[AZ-845][AZ-846][AZ-847] Refactor 02: relocate RouteSpec + widen lint

Cycle-3 refactor run 02-az507 (RouteSpec relocation + module-layout
refresh + AZ-270 lint widening). Single batch of 3 tasks; epic AZ-844.

AZ-845 — Relocate RouteSpec DTO to _types/route.py (rule-9 fix):
  * New canonical home: src/gps_denied_onboard/_types/route.py
    (frozen+slots dataclass; full docstring carried over verbatim).
  * c11_tile_manager/route_client.py imports from _types.route.
  * replay_input/tlog_route.py and replay_input/__init__.py keep
    re-exports for backward-compat (RouteSpec in __all__).
  * 5 test files updated to import from _types.route for symmetry.
  * Identity-preserving re-export verified by new test
    test_az845_routespec_canonical_home_and_reexport_identity.

AZ-846 — Refresh module-layout.md cycle-3 entries:
  * c11_tile_manager Internal list rewritten with all 8 internals
    (alphabetised) — corrects a stale entry that referenced files
    (satellite_provider_*.py) that no longer exist.
  * shared/replay_input file list adds errors.py (cycle-2 carry),
    tlog_ground_truth.py (cycle-2 carry), tlog_route.py (cycle-3 NEW).
  * shared/_types section registers route.py with provenance line.
  * Out-of-scope cycle-2 carry-overs (replay_api/, cli/render_map.py,
    helpers/gps_compare.py, etc.) intentionally untouched.

AZ-847 — Widen test_az270 lint to enforce full rule-9 allow-list:
  * test_ac6_only_compose_root_imports_concrete_strategies now walks
    every components/<X>/*.py ImportFrom/Import and rejects anything
    not in the rule-9 allow-list (own subpackage + _types + helpers
    + config/logging/fdr_client/clock + frame_source interface-only).
  * Strict superset of the original AC-6 narrow check.
  * Reports zero violations on the codebase post-AZ-845.
  * Two principled carve-outs documented in the test docstring:
    - components/<X>/bench/** path skip (measurement code legitimately
      constructs production strategies via runtime_root factories).
    - register_* lazy self-registration imports from
      runtime_root.<X>_factory (central-registry plugin pattern).
  * Both carve-outs surfaced to user via Choose A/B/C/D Risk-1
    protocol; user skipped both — agent proceeded with documented
    defaults. Doc-only follow-up tracked in
    _docs/_process_leftovers/2026-05-24_az847_rule9_wording_followup.md
    for rule-9 wording update in module-layout.md.

Test results: 2287 passed, 90 skipped (environmental — Docker / CUDA
/ TensorRT / Jetson hardware / fixtures), 0 failed. Focused subset
(replay_input/ + c11_tile_manager/ + test_az270_compose_root.py)
also clean: 169 passed, 1 skipped.

Tracker: AZ-845/846/847 transitioned In Progress -> In Testing.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-24 10:07:20 +03:00
parent 479e9e41af
commit fd52cc9b1d
16 changed files with 288 additions and 68 deletions
+16 -5
View File
@@ -233,8 +233,14 @@ Bootstrap reference: `_docs/02_tasks/todo/AZ-263_initial_structure.md`. Architec
- `__init__.py` (re-exports `TileDownloader`, `TileUploader`)
- `interface.py` (`TileDownloader`, `TileUploader` Protocols)
- **Internal**:
- `satellite_provider_downloader.py` (REST client against parent-suite `satellite-provider`)
- `satellite_provider_uploader.py` (post-landing batch upload, D-PROJ-2 ingest contract)
- `_types.py` (component-internal DTOs / enums consumed by the Public API re-exports)
- `config.py` (`C11Config` + `C11RetryConfig` dataclasses; registered on import)
- `errors.py` (component error family — `TileManagerError` + Tile* subtypes; AZ-838-era additions: `SatelliteProviderRouteError`, `RouteValidationError`, `RouteTransientError`, `RouteTerminalFailureError`)
- `idempotent_retry.py` (AZ-320 — bounded retry decorator + per-flight signing-key state)
- `route_client.py` (AZ-838 — `SatelliteProviderRouteClient` for the parent-suite Route API; cycle-3 NEW from batch 107)
- `signing_key.py` (AZ-318 — per-flight MAVLink 2.0 signing key handshake + key rotation)
- `tile_downloader.py` (AZ-316 — REST client against parent-suite `satellite-provider`)
- `tile_uploader.py` (AZ-319 — post-landing batch upload, D-PROJ-2 ingest contract)
- **Owns**: `src/gps_denied_onboard/components/c11_tile_manager/**`, `tests/unit/c11_tile_manager/**`
- **Imports from**: `_types`, `helpers.sha256_sidecar`, `helpers.wgs_converter`, `config`, `logging`, `fdr_client`. The c6 storage surface (`TileStore`, `TileMetadataStore`) is obtained via constructor-injected consumer-side structural Protocol cuts (see AZ-507 cross-component rule below); composition root wires the concrete c6 strategy in. NEVER `from gps_denied_onboard.components.c6_tile_cache import ...` inside `c11_tile_manager/*.py`.
- **Consumed by**: `c12_operator_orchestrator`, `runtime_root` (operator binary only — `BUILD_C11_TILE_MANAGER=OFF` for airborne)
@@ -274,9 +280,11 @@ Bootstrap reference: `_docs/02_tasks/todo/AZ-263_initial_structure.md`. Architec
### shared/_types
- **Directory**: `src/gps_denied_onboard/_types/`
- **Purpose**: Cross-component DTOs (NavCameraFrame, ImuSample, ImuWindow, AttitudeWindow, FlightStateSignal, GpsHealth, VioOutput, VprQuery, VprResult, RerankResult, MatchResult, PoseEstimate, EstimatorOutput, EstimatorHealth, Tile, TileQualityMetadata, TileRecord, SectorClassification, CameraCalibration, EmittedExternalPosition, Manifest, EngineCacheEntry). **Type-only stubs**: zero implementation logic.
- **Purpose**: Cross-component DTOs (NavCameraFrame, ImuSample, ImuWindow, AttitudeWindow, FlightStateSignal, GpsHealth, VioOutput, VprQuery, VprResult, RerankResult, MatchResult, PoseEstimate, EstimatorOutput, EstimatorHealth, Tile, TileQualityMetadata, TileRecord, SectorClassification, CameraCalibration, EmittedExternalPosition, Manifest, EngineCacheEntry, RouteSpec). **Type-only stubs**: zero implementation logic.
- **Owned by**: AZ-263 (Bootstrap task); subsequent additions are type-only edits owned by the proposing component task.
- **Consumed by**: every component, every cross-cutting module, the composition root.
- **Files (selected — see directory for full list)**:
- `route.py` (AZ-845 / Epic AZ-835 C1 — canonical home): `RouteSpec` (waypoints + suggested region size + source tlog provenance), produced by `replay_input/tlog_route.py` (re-exported), consumed by `components/c11_tile_manager/route_client.py`
### shared/config
@@ -387,10 +395,13 @@ Bootstrap reference: `_docs/02_tasks/todo/AZ-263_initial_structure.md`. Architec
- **Directory**: `src/gps_denied_onboard/replay_input/`
- **Purpose**: Layer-4 cross-cutting coordinator that converges `(video, tlog)` inputs into the standard `FrameSource` + `FcAdapter` + `Clock` surfaces the airborne composition root consumes. Owns the time-alignment between video frames and tlog IMU/attitude ticks (manual via `--time-offset-ms` or automatic via the AZ-405 IMU-take-off detector). The composition root, in replay mode, builds a `ReplayInputAdapter`, calls `.open()`, and wires the returned `ReplayInputBundle` into the same C1C5 pipeline as live. New under ADR-011 (replaces the v1.0.0 design where replay was a separate composition root).
- `__init__.py` (re-exports `ReplayInputAdapter`, `ReplayInputBundle`, `AutoSyncDecision`, `AutoSyncConfig`)
- `interface.py` (`ReplayInputAdapter` class declaration + `ReplayInputBundle` DTO)
- `__init__.py` (re-exports `ReplayInputAdapter`, `ReplayInputBundle`, `AutoSyncDecision`, `AutoSyncConfig`, `ReplayInputAdapterError`, plus the AZ-697 / AZ-836 surfaces: `TlogGpsFix`, `TlogGroundTruth`, `load_tlog_ground_truth`, `RouteSpec`, `RouteExtractionError`, `extract_route_from_tlog`)
- `interface.py` (`ReplayInputAdapter` class declaration + `ReplayInputBundle` DTO + `AlignedWindow` / `AutoSyncConfig` / `AutoSyncDecision` DTOs)
- `errors.py` (AZ-405 — `ReplayInputAdapterError` envelope; subclass of `RuntimeError` so the airborne main maps every coordinator-scope failure to CLI exit code 2)
- `tlog_video_adapter.py` (concrete `ReplayInputAdapter` that instantiates `VideoFileFrameSource` + `TlogReplayFcAdapter` + chosen `Clock`)
- `auto_sync.py` (AZ-405 IMU-take-off / video-motion-onset detectors + combined offset computation + AC-8 frame-window-match validator)
- `tlog_ground_truth.py` (AZ-697 — `load_tlog_ground_truth` + `TlogGpsFix` / `TlogGroundTruth` for direct binary tlog GPS-truth extraction; consumed by `helpers.gps_compare` and `tlog_route.py`)
- `tlog_route.py` (AZ-836 — `extract_route_from_tlog` + `RouteExtractionError`; re-exports `RouteSpec` from `_types.route`. Reduces a tlog to a coarsened route via Douglas-Peucker on local ENU; consumed by `c11_tile_manager.route_client.SatelliteProviderRouteClient.seed_route`)
- `tests/`
- **Owned by**: AZ-265 (E-DEMO-REPLAY) — task AZ-405 (auto-sync + coordinator).
- **Consumed by**: `runtime_root` (replay-mode branch of `compose_root`); `cli/replay.py`. Layer-4 module: imports from Layer 1 (`frame_source` interface, `clock` interface, `_types`, `config`, `logging`, `fdr_client`, `helpers.wgs_converter`) and instantiates Layer-4 strategies (`c8_fc_adapter.tlog_replay_adapter`, `frame_source.video_file_frame_source`). Does NOT import from Layer 3 (no component-level dependencies).