- 02-05-SUMMARY.md: full execution record for plan 02-05
- ROADMAP.md: plan progress updated (5 summaries of 7 plans complete)
- REQUIREMENTS.md: AC-06 and TEST-02 marked complete
Per-marker CI jobs and ac-traceability gate are now the CI contract.
All 21 orphan ACs annotated pending-phase-N; --check exits 0 locally.
Updated matrix reflects:
- _PENDING_RE addition: 21 orphan ACs now show DEFERRED (pending-phase-N)
- 4 hardware-deferred ACs show DEFERRED (hardware)
- 14 covered ACs show OK
- Summary stat line updated to 'deferred (hardware or pending-phase): 25'
git diff --exit-code .planning/AC-TRACEABILITY.md now exits 0.
- Add _PENDING_RE = re.compile(r'pending-phase-\d+', re.IGNORECASE)
- Initialize AC entries with deferred_reason: None alongside deferred: False
- Update collect_acs_from_doc() to set deferred_reason='hardware' or the matched pending-phase-N token
- Update render_md() to show DEFERRED ({reason}) using actual reason string
- Update summary stat line to reflect both hardware and pending-phase deferrals
- Add pytestmark = [pytest.mark.<category>] to all 23 root test files and 14 e2e test files
- Marker distribution: 22 unit, 7 integration, 1 blackbox, 1 sitl, 5 e2e + 2 e2e integration
- Add import pytest to test_models.py, test_download.py, test_synthetic_adapter.py (were missing)
- Convert test_sitl_integration.py's bare pytestmark to list form preserving skipif guard
- Union of all 5 markers = 298/298 = 100% coverage; 216 tests pass with --strict-markers
ADR 0002: hexagonal/ports-and-adapters architecture — components/ layout,
protocol.py per component, composition root, core/ for concentrated math.
ADR 0003: @dataclass(slots=True, frozen=True) on hot path; Pydantic retained
only at REST/config/DB boundaries. Pose/GPSPoint migration deferred to Phase 2.
ADR 0004: Stage 2 as independent iteration — own phases 1-6, own requirements,
stage1 code treated as MVP starting capital.
PROJECT.md: Stage 2 Key Decisions updated from Pending → Accepted with Phase 1
implementation notes, deferred work list, and final architecture summary.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- app.py: replace inline component wiring with build_pipeline(env=cfg.env)
- Store processor as app.state.processor (and backwards-compat pipeline_components)
- RuntimeConfig replaces get_settings(); MAVLink stop() on shutdown
- deps.py: get_flight_processor prefers app.state.processor from lifespan
- Falls back to build_pipeline() for test contexts without lifespan
- Per-request repo/streamer swap preserved
- core/vo.py is now ~30 LOC of pure re-exports from
components/vio/{protocol, orbslam_backend, cuvslam_backend, factory}.
- All 8 public symbols (VisualOdometry, ISequentialVisualOdometry,
ORBVisualOdometry, SequentialVisualOdometry, CuVSLAMVisualOdometry,
CuVSLAMMonoDepthVisualOdometry, create_vo_backend, _CUVSLAM_AVAILABLE)
remain importable from the legacy path with class identity preserved
(re-export, not redefinition — isinstance checks still hold).
- tests/test_vo.py: 22/22 passing unchanged. No test files edited.
- Shim is removed in Phase 2 when TEST-01 reorganizes test taxonomy.
- Lift the env-aware VO backend factory verbatim from core/vo.py.
- Body and parameter defaults preserved exactly (PATTERNS.md §4.1
mandate: 'Preserve this factory verbatim').
- Return-type annotation widened from ISequentialVisualOdometry to the
canonical VisualOdometry Protocol from Plan 01-02; the I-prefix alias
is still importable so legacy callers/type-checkers keep working.
- Imports route through the new components.vio.* modules; no
cross-package edits needed because Plan 08 (composition root) is the
only other call site planned.
- Append to the components.vio barrel.
- Extract CuVSLAMVisualOdometry (Inertial) + CuVSLAMMonoDepthVisualOdometry
(Mono-Depth) from core/vo.py into a dedicated cuVSLAM-bridge module.
- Preserve the optional 'try: import cuvslam / except ImportError' pattern
at module top with the _CUVSLAM_AVAILABLE flag — verified False on x86 dev,
True on Jetson (PATTERNS.md §6.5, §8.1).
- Both classes embed an ORBVisualOdometry instance for transparent dev/CI
fallback; metric scale semantics preserved (scale_ambiguous=False).
- Scaffold components/vio/native/ as Phase-1 placeholder for future native
SDK glue (PATTERNS.md §1.4); Phase 1 is intentionally empty.
- Append both classes to the components.vio barrel.
- Extract SequentialVisualOdometry and ORBVisualOdometry from core/vo.py
into a dedicated pure-Python OpenCV backend module.
- Module deliberately does NOT import cuvslam — keeps optional-SDK
isolation from the cuvslam backend (Plan 01-03 Task 1).
- Both classes inherit from the components.vio.protocol.ISequentialVisualOdometry
Protocol alias (Plan 01-02 surface).
- Barrel-export both classes from components/vio/__init__.py.
- core/vo.py is unchanged in this commit; the shim wires up in Task 4.
- Add @dataclass(slots=True, frozen=True) types for IMUSample, ESKFState,
RelativePose, Features, Matches, Motion, AlignmentResult,
ChunkAlignmentResult, Sim3Transform, RotationResult, TileCoords,
TileBounds, SatelliteAnchor, PositionEstimate
- FrameState uses slots=True only (frozen=False) per PATTERNS.md §6.1 —
processor.py mutates this object during frame handling
- eq=False on every dataclass with np.ndarray fields, matching prior
Pydantic incomparability under arbitrary_types_allowed
- Barrel __init__.py exposes all public names plus ARCH-02 aliases
IMUMeasurement → IMUSample and VOEstimate → RelativePose
- Pure addition: no consumer file edited, 216 tests still pass
Final review findings (Important):
- I1: e2e test docstring overclaimed — harness always uses ORBVisualOdometry.
Rewrite docstring to describe the actual scope: smoke test + ORB regression
guard. Wiring Mono-Depth wrapper through the harness is a sprint 2 task.
- I2: update_depth_hint had no tests. Add 2 tests: clamp at 1.0m for bogus
values, and verify next compute_relative_pose uses the updated scale.
- I3: add TODO marker for sprint 2 deduplication with CuVSLAMVisualOdometry.
No behavior change — only docstrings, TODO markers, and test coverage.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GlobalPlaceRecognition already implements AnyLoc-VLAD-DINOv2 (existing code).
This change makes the sprint 1 GPR technology selection explicit:
- Expand class docstring with selection rationale vs NetVLAD / SP+LG
- Document INT8 quantization as known-broken for ViT on Jetson
- Reference design doc §2.3 and stage2 backlog
- Add two marker tests asserting 4096-d descriptor + DINOv2 engine name
No behavioral change — existing Mock/TRT path unchanged.
Ref: docs/superpowers/specs/2026-04-18-oss-stack-tech-audit-design.md §2.3
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Documents baseline for CuVSLAMMonoDepthVisualOdometry on EuRoC MH_01.
ATE 0.2046m matches ORB baseline (dev/CI uses scaled ORB fallback).
Ceiling 0.5m — same as ORB. EuRoC indoor != production outdoor nadir.
Ref: docs/superpowers/specs/2026-04-18-oss-stack-tech-audit-design.md §4
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- test_depth_hint_scales_translation: replace assert True with mock-based verification of scale factor
- _init_tracker / _compute_via_cuvslam: logger.exception for stack traces
- _init_tracker: loud warning when Jetson path disabled
- drop personal attribution (git blame suffices)
- translate Ukrainian test docstrings to English
Adds: solution.md reconciliation (cuVSLAM Inertial→Mono-Depth gap),
migration steps through e2e harness, risk budget decision tree,
aero-vloc benchmark action plan with pass/fail criteria,
and SITL GPS_INPUT test decomposition with MAVProxy reference.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Research doc (2026-04-18 OSS stack audit) flagged NumPy 2.0 as silently
breaking GTSAM Python bindings (issue #2264). Pin numpy>=1.26,<2.0 and
constrain opencv-python-headless<4.11 (knock-on: 4.11+ requires numpy≥2).
Verified after downgrade:
- 196 passed / 8 skipped unit/component
- EuRoC MH_01 e2e PASS (no regression on 0.205m ESKF ATE baseline)
Plan updates in next_steps.md §5:
- cuVSLAM strategy clarified: Mono-Depth (barometer as synthetic depth),
not Mono-Inertial (needs stereo hardware we don't have)
- DINOv2-VLAD (AnyLoc) for GPR + FP16 TRT (INT8 broken for ViT on Jetson)
- GTSAM: documented that 4.2 stable is not on PyPI (only 4.3a0), so
deferred to post-sprint-1 ESKF-only path stays the right call
- VPAIR xfail root cause: no raw IMU + mock satellite index (verified
with scale=1.0 and scale=45.0 runs — ATE stays at ~1236m ESKF /
~1770km GPS regardless of scale)
- Flight controller H743 vs F405 check flagged as critical blocker
README "next steps" section rewritten to match the research-aligned plan.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace stale ATE ~10.9 km numbers with current baseline table (MH_01-05
all PASS with strict-assert, ESKF ATE 0.007–0.205 m). Add "next steps"
section split into dev-pipeline vs on-device work.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Records architectural gap (cuVSLAM Mono has no metric scale),
chosen path (Mono-Depth + barometer), and per-layer decisions
for VO, ESKF, GTSAM, Place Recognition, and MAVLink.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
conftest.py: add euroc_mh02..05_root fixtures (session-scoped, skip when absent)
test_euroc_mh_all.py: 10 parametrised tests — pipeline_completes + eskf_drift
for MH_01..05 with per-difficulty ESKF ATE ceilings (easy: 0.5 m, med/hard: 1.5 m)
Results on first 100 frames (vo_scale=5 mm/frame):
MH_01 easy ESKF ATE 0.205 m (< 0.5 m ceiling)
MH_02 easy ESKF ATE 0.131 m (< 0.5 m ceiling)
MH_03 medium ESKF ATE 0.008 m (< 1.5 m ceiling)
MH_04 difficult ESKF ATE 0.009 m (< 1.5 m ceiling)
MH_05 difficult ESKF ATE 0.007 m (< 1.5 m ceiling)
All 10 tests PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>