The batch 108 fixture built tile_store + descriptor_index from
the static operator config (root_dir baked into YAML) but built
the AC-3/AC-6 verifier from cache_root/descriptor.index (fresh
tmp path). On Tier-2 the descriptor_batcher would write under
the YAML root and the verifier would open the tmp path, raising
IndexUnavailableError before the fixture could yield a
PopulatedC6Cache. Unit tests missed it because every test
stubbed descriptor_index_factory.
Mutate the c6_tile_cache config block in-memory at fixture entry
so root_dir = cache_root and faiss_index_path falls back to
<cache_root>/descriptor.index. Production C6 components and the
verifier now share one path source. Align tile_store_path with
PostgresFilesystemStore's <root_dir>/tiles layout so the
integration test's tile_store_path.is_dir() assertion holds.
Driver and unit tests are path-agnostic and unaffected. Batch
108b report documents the defect, the fix, and the self-review
miss.
Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the placeholder operator_pre_flight_setup pytest fixture (the
mkdir stub at tests/e2e/replay/conftest.py:293-310) with a real driver
that wires C1 (AZ-836 RouteSpec) + C2 (AZ-838 SatelliteProviderRoute
Client) + C11 (AZ-316 HttpTileDownloader) + C10 (AZ-322 Descriptor
Batcher) end-to-end and yields a typed PopulatedC6Cache. AZ-306 FAISS
sidecar triple-consistency is verified post-rebuild via a caller-
supplied descriptor_index_factory; partial sidecars are cleaned up on
failure (AC-7) while pre-existing warm-cache files are preserved.
Algorithm lives in tests/e2e/replay/_operator_pre_flight.py with
pure dependency injection so the AC-8 unit suite (11 tests covering
happy / transient-retry / terminal-failure / validation-error /
tamper-detection / cleanup-on-failure) runs against stubs and the
AC-9 Tier-2 integration test runs the same algorithm against the
real Jetson harness. The conftest fixture skip-gates on RUN_REPLAY
_E2E + SATELLITE_PROVIDER_URL/API_KEY + BUILD_FAISS_INDEX +
GPS_DENIED_OPERATOR_CONFIG_PATH and wires deps through the existing
runtime_root factories. Supersedes AZ-777 Phase 3.
Co-authored-by: Cursor <cursoragent@cursor.com>
Batch 98 (cycle 2) — first two PBIs of epic AZ-696 (real-flight
validation harness):
AZ-697: direct binary-tlog GPS-truth extractor
- New src/gps_denied_onboard/replay_input/tlog_ground_truth.py reads
GLOBAL_POSITION_INT (with GPS_RAW_INT fallback) from a binary
ArduPilot tlog via pymavlink.mavutil and returns a frozen+slotted
TlogGroundTruth DTO with per-record ts_ns / lat_deg / lon_deg / alt_m
/ hdg_deg / vx_m_s / vy_m_s / vz_m_s.
- Promoted l2_horizontal_m + match_percentage + GroundTruthRow from
tests/e2e/replay/_helpers.py into the new production module
src/gps_denied_onboard/helpers/gps_compare.py. The e2e helper now
re-exports the same objects (identity, not copies) so existing test
imports continue working untouched.
- tests/e2e/replay/conftest.py prefers the real derkachi.tlog when
present, falls back to the CSV synth path otherwise.
- 22 new unit tests cover AC-1..AC-5 (mypy --strict subprocess test
included). All passing.
AZ-702: Topotek KHP20S30 factory-sheet camera calibration
- New _docs/00_problem/input_data/flight_derkachi/khp20s30_factory.json:
fx = fy = 4644.444, cx = 960, cy = 540, HFOV ~ 23.3 deg, VFOV ~ 13.2
deg, computed from the published 8.5 mm focal length + 1/2.8" sensor
+ 1920x1080 capture at lowest zoom step. Distortion zeroed,
body_to_camera_se3 = identity with nadir convention. Acquisition
method explicitly recorded as factory_sheet so downstream code can
expect higher residual error than a lab calibration.
- _docs/00_problem/input_data/flight_derkachi/camera_info.md updated
to document the assumptions, expected residual error window, and
conftest pick-up rule.
- tests/e2e/replay/conftest.py::_calibration_path() prefers
khp20s30_factory.json when present, falls back to adti26.json.
- 9 new unit tests cover AC-1..AC-4 (schema, intrinsics traceback,
doc reference, conftest pick-up). All passing.
Test run: 45 new tests, all passing. Full-suite gate deferred to
Step 16 (after the last batch in cycle 2 per the implement skill).
Adjacent note (not fixed in this batch, recorded in the batch report):
auto_sync.py has the same redundant pymavlink type:ignore + a few
numpy/cv2 mypy --strict issues. None on this batch's path.
Refs: _docs/03_implementation/batch_98_cycle2_report.md
Refs: _docs/02_tasks/done/AZ-697_tlog_ground_truth_extractor.md
Refs: _docs/02_tasks/done/AZ-702_khp20s30_calibration.md
Co-authored-by: Cursor <cursoragent@cursor.com>
Mid-flight fixtures (Derkachi) and stationary-still scenarios
(FT-P-01) have no take-off spike for the IMU detector and produce
false-positive video motion onsets, so the AC-9 frame-window
validator rejects every plausible offset. Add an operator-acknowledged
opt-out: a new ReplayConfig.skip_auto_sync_validation flag that
suppresses validation, paired with a hard requirement that
time_offset_ms also be set (silent-zero guard at both schema and
adapter layers).
Wired through schema -> CLI (--skip-auto-sync) -> composition root
-> ReplayInputAdapter; Derkachi e2e fixture now passes
time_offset_ms=0 + skip_auto_sync=True by default since the synth
tlog and the video share the same t=0 anchor by construction.
5 new unit tests:
* schema gate rejects skip=True without manual offset
* schema gate accepts the legal pair
* default field value is False (default-construction safety)
* adapter constructor mirrors the schema gate
* adapter open() bypasses validate_offset_or_fail when flag is set
All 38 unit tests in test_az401 + test_az405 pass on Mac.
Co-authored-by: Cursor <cursoragent@cursor.com>