# 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).
