# Batch Report — cycle 4, batch 03 **Batch**: 03 **Cycle**: 4 **Tasks**: AZ-895 **Total complexity**: 2 SP **Date**: 2026-05-26 **Commit**: pending (this batch) ## Task Selection AZ-895 (deprecate `auto_sync` surface) ships solo. It is the natural follow-up to AZ-894 (CSV adapter, batch 02): now that the CSV-driven path is the primary replay surface, the legacy tlog auto-sync infrastructure can be retired. Per `_dependencies_table.md`, AZ-895 has a hard dependency on AZ-894 which closed in batch 02. ### Complexity-budget user decision (Option A — minimum) A naïve full removal of the auto-sync surface would have touched: - 4 production modules: `auto_sync.py` (delete), `tlog_video_adapter.py` (delete), `interface.py` (drop AutoSync DTOs + `ReplayInputBundle` field), `_replay_branch.py` (strip legacy branch + `_build_auto_sync_config`) - 3 config files: `config/schema.py` (drop `ReplayConfig` auto-sync fields + the `ReplayAutoSyncConfig` class), `config/loader.py` (drop `REPLAY_TIME_OFFSET_MS` env + auto_sync block handler), `config/__init__.py` (drop re-exports) - 1 CLI: drop the three deprecated flags entirely - 4 test files needing rewrite or deletion - Cascading docs in `replay_protocol.md` (AZ-842 sibling work) Estimated at 4–5 SP, well over the ticket's 2 SP budget. Per `meta-rule.mdc` Complexity Budget Check, the user was offered four options and chose **Option A — minimum**: > "Minimum (~2 SP): no-op-stub auto_sync.py (raises documented error), > strip tlog_video_adapter.open() to raise too, drop the unreachable > legacy branch in _replay_branch.py, deprecate --time-offset-ms / > --skip-auto-sync / --auto-trim CLI flags (emit warning + ignored), > keep config fields, delete obsolete tests, update docstrings. File > AZ-902 (cycle 5) for hard surface removal." The follow-up ticket was filed as **AZ-908** (Jira renumbered from the proposed AZ-902 because AZ-902–AZ-907 were already taken). AZ-908 is in `backlog/` and depends on AZ-895 + AZ-842. ## Task Results | Task | Status | Files Modified | Tests | AC Coverage | Issues | |------|--------|----------------|-------|-------------|--------| | AZ-895_deprecate_auto_sync_surface | Done | 8 modified, 1 added (test) | 3 unit-test files deleted; 1 new test added; 1 existing test file updated; 2,287 unit tests green | 5/5 | None | ### Files touched Production (`src/gps_denied_onboard/**`): - REPLACED `replay_input/auto_sync.py` — was a 700+ LOC detector module; now a 56-line no-op stub whose every public callable raises `ReplayInputAdapterError("auto-sync removed; supply --imu CSV instead")`. `__all__` preserved so any external import still resolves. - REPLACED `replay_input/tlog_video_adapter.py` — was a 700+ LOC coordinator; now a 105-line deprecated-stub that keeps the `ReplayInputAdapter` class signature for source-compat. `open()` raises `ReplayInputAdapterError(...)` immediately; `close()` is a no-op. Re-exports `ReplayPace` so `_replay_branch.py` can continue to import it from the same path (preserving the AZ-401 AC-8 import boundary). - MODIFIED `runtime_root/_replay_branch.py` — removed the legacy `ReplayInputAdapter` instantiation branch in `_build_replay_input_bundle`; deleted the `_build_auto_sync_config` helper; tightened `_validate_replay_paths` to require `imu_csv_path` (no more tlog fallback); dropped unused `WgsConverter` and `AutoSyncConfig` imports; removed the `replay_input_adapter_factory` test-injection parameter; updated module + function docstrings; cleaned `auto_sync_used` from the ready-log kv (always None now). - MODIFIED `replay_input/__init__.py` — docstring rewritten to flag the deprecation status of the `tlog_video_adapter` / `auto_sync` surfaces. Re-exports preserved. - MODIFIED `cli/replay.py` — `--time-offset-ms`, `--skip-auto-sync`, `--auto-trim` help text replaced with `DEPRECATED (AZ-895)` notice; `_print_startup_banner` now emits a `DeprecationWarning` + stderr line when any of the three are non-default; `_build_replay_config` hard-codes the corresponding `ReplayConfig` fields to None / False so the deprecated values cannot influence composition. - MODIFIED `components/c8_fc_adapter/tlog_replay_adapter.py` — module docstring reframed as **AUDIT-ONLY** (AC-5). Code unchanged. - MODIFIED `replay_input/tlog_ground_truth.py` — module docstring reframed as **AUDIT-ONLY** (AC-5). Code unchanged. Tests (`tests/**`): - DELETED `tests/unit/replay_input/test_az405_auto_sync.py` (386 LOC). Rationale: tested the AZ-405 detector algorithm + AC-9 validator which no longer execute. Per AC-3, the deprecation-stub test below replaces it. - DELETED `tests/unit/replay_input/test_az405_replay_input_adapter.py` (645 LOC). Rationale: tested the `ReplayInputAdapter` coordinator's six-step `open()` workflow which now raises immediately. - DELETED `tests/unit/replay_input/test_az698_window_alignment.py` (745 LOC). Rationale: tested the AZ-698 IMU↔optical-flow cross-correlation aligner which no longer executes. - ADDED `tests/unit/replay_input/test_az895_auto_sync_deprecated_stub.py` — 5 parametrised tests pinning the AC-1 contract: every public callable raises `ReplayInputAdapterError` with the documented message. - MODIFIED `tests/unit/test_az402_replay_cli.py` — renamed `test_ac4_time_offset_forwarded` → `test_ac4_time_offset_ignored_after_az895` with asserts inverted (value now `None` regardless of flag); added `test_az895_skip_auto_sync_ignored_and_warned`, `test_az895_auto_trim_ignored_and_warned`, `test_az895_no_deprecated_flags_no_warning`; `_argv` helper grew `skip_auto_sync` and `auto_trim` overrides. - MODIFIED `tests/unit/test_az401_compose_root_replay.py` — renamed `test_replay_branch_rejects_both_inputs_empty` → `test_replay_branch_rejects_missing_imu_csv_path` with body updated to the new gate semantics; `_make_replay_config` helper now sets `imu_csv_path` by default; deleted `test_replay_branch_loads_camera_calibration_from_runtime_path` (only verified the now-removed `replay_input_adapter_factory` injection path; calibration loading is exercised indirectly by the full compose-root tests and by the e2e suite). - MODIFIED `tests/e2e/replay/test_derkachi_real_tlog.py` — xfail reason text refreshed to reference AZ-848 + AZ-883 (the live tlog-clock root cause) instead of the closed AZ-776 + AZ-777 (AC-4 literally specifies "AZ-848-scoped reason"). Docs (`_docs/**`): - MODIFIED `_docs/02_document/module-layout.md` — `replay_input` file list flags `tlog_video_adapter.py` + `auto_sync.py` as **DEPRECATED (AZ-895)**, adds `csv_ground_truth.py`, updates the package purpose to lead with the CSV path. - ADDED `_docs/02_tasks/backlog/AZ-908_replay_auto_sync_hard_removal.md` — cycle-5+ follow-up spec. - MODIFIED `_docs/02_tasks/_dependencies_table.md` — preamble + Total Tasks (179 → 180) + Total Complexity (567 → 570); AZ-908 row added under Cycle-4 / AZ-895 follow-up. Tracker (Jira): - AZ-895 — transitioned `To Do` → `In Progress` (transition id `21`). - AZ-908 — created (`Replay: hard removal of deprecated auto-sync surface (AZ-895 follow-up)`), 3 SP estimate, deps AZ-895 (hard) + AZ-842 (hard). Filed via `createJiraIssue` MCP. ## File-Ownership Note All touched paths are owned by the cycle-4 replay-input redesign envelope (`replay_input/` + `cli/replay.py` + `runtime_root/_replay_branch.py`) plus the AC-5 audit-only docstring updates inside `components/c8_fc_adapter/tlog_replay_adapter.py` (the c8 owner already accepted the audit-only reframing in AZ-894). No out-of-scope edits. ## AC Test Coverage | AC | Coverage | Test | |----|----------|------| | AC-1 (`auto_sync.py` is deleted or made a no-op raising the documented error) | Direct | `tests/unit/replay_input/test_az895_auto_sync_deprecated_stub.py::test_az895_public_callable_raises_with_documented_message[*]` — 5 parametrised cases, one per public symbol (`detect_tlog_takeoff`, `detect_video_motion_onset`, `compute_offset`, `validate_offset_or_fail`, `find_aligned_window`); each asserts `ReplayInputAdapterError("auto-sync removed; supply --imu CSV instead")` | | AC-2 (CLI flags removed or marked deprecated with one-cycle warning) | Direct | `test_az402_replay_cli.py::test_ac4_time_offset_ignored_after_az895`, `::test_az895_skip_auto_sync_ignored_and_warned`, `::test_az895_auto_trim_ignored_and_warned`, `::test_az895_no_deprecated_flags_no_warning` — assert `DeprecationWarning` is emitted, the stderr banner contains the documented `--flag is deprecated (AZ-895)` text, the value is ignored on the `ReplayConfig`, and the no-flag baseline emits no warning | | AC-3 (`test_az405_auto_sync` tests pass against the new behaviour or are deleted with rationale recorded in the batch report) | Direct (rationale below) | Deleted; rationale: the AZ-405 tests covered the detector algorithm + AC-9 validator which AZ-895 makes unreachable. Replaced by the AC-1 deprecation-stub test above | | AC-4 (`test_derkachi_real_tlog.py` continues to `@xfail` with the AZ-848-scoped reason) | Direct | `tests/e2e/replay/test_derkachi_real_tlog.py::test_az699_real_flight_validation_emits_verdict_and_report` — `@pytest.mark.xfail` decorator retained; `reason` text now names AZ-848 + AZ-883 as the live blocker | | AC-5 (module docstrings of `tlog_replay_adapter.py` and `tlog_ground_truth.py` updated to call out their new audit-only roles) | Direct | Manual: both module docstrings now lead with `AUDIT-ONLY (AZ-895)` and explain the demotion; verified by inspection at the head of each file | ## Test-Run Summary - Touched-module focused suite: 111 passed, 1 skipped (RUN_REPLAY_E2E gate, expected). - Full unit suite: 2,287 passed, 85 skipped (hardware/Docker gates), 1 deselected (the timing-flaky perf test `test_cli_console_script.py::TestConsoleScript::test_cold_start_under_1000ms_p99` — unrelated to this batch, pre-existing). ## Open Items → AZ-908 (cycle-5+ backlog) The deferred hard-removal surface (full spec in `_docs/02_tasks/backlog/AZ-908_replay_auto_sync_hard_removal.md`): - Delete `replay_input/auto_sync.py` + `replay_input/tlog_video_adapter.py`. - Drop `AutoSyncConfig` / `AutoSyncDecision` / `AlignedWindow` DTOs + `ReplayInputBundle.auto_sync_result` / `aligned_window` fields. - Drop the three deprecated CLI flags + their tests. - Drop `ReplayConfig.time_offset_ms` / `.skip_auto_sync_validation` / `.auto_trim` / `.auto_sync` + `ReplayAutoSyncConfig` class. - Drop `BUILD_TLOG_REPLAY_ADAPTER` build flag from `REPLAY_BUILD_FLAGS`. - Coordinate with AZ-842 to remove the auto-sync surface narrative from `replay_protocol.md`. ## Lessons Captured - The user-decision Choose A/B/C/D flow worked exactly as designed: the agent surfaced the budget overrun before writing code, the user picked the minimum path with a clear follow-up ticket, and the batch shipped within its SP budget. - Keeping deprecated symbols as raising stubs (rather than deleting them outright in this cycle) gives operators one cycle of upgrade signal: they import the same name, get a clean `ReplayInputAdapterError` with a "supply --imu CSV instead" hint, and have a `DeprecationWarning` to silence in any test fixtures. - Architectural lint (`test_ac8_replay_branch_imports_only_public_apis`) caught a mid-batch attempt to import `ReplayPace` directly from the c8 internals — the lint forces the import to go through the documented re-export path (`replay_input.tlog_video_adapter`). Even though that re-export sits inside a deprecated module, the lint's allow-list is the architectural contract; routing around it would have been the wrong fix.