New operator-side console-script renders a self-contained HTML map
(folium / Leaflet) comparing the estimator's JSONL track against
the tlog ground-truth track. Pinned visual style: red truth + blue
estimated polylines, start/end markers per track, 100 m + 50 m
scale circles, optional AZ-699 accuracy-summary banner, and an
--offline-tiles mode (with optional local tile-URL template) for
Jetsons without internet.
folium is gated behind a new [operator-tools] optional-dep so the
airborne binary's cold-start NFR is unaffected (C12 binary doesn't
import the new module). 14 new unit tests pin polyline count,
marker count, scale-circle radii, summary embedding, offline-tile
behaviour, and full CLI smoke. Zero mypy --strict errors.
Refines the 2026-05-20 Jetson-only test policy: unit tests may run
locally, e2e/perf/resilience/security stay Jetson-only. Documented
in _docs/02_document/tests/environment.md (Where each tier runs)
and .cursor/rules/testing.mdc (Test environment for this project).
Co-authored-by: Cursor <cursoragent@cursor.com>
New e2e test runs gps-denied-replay --auto-trim against the real
derkachi.tlog + flight video + AZ-702 calibration, computes the
horizontal-error distribution (mean/p50/p95/p99 + 10/25/50/100 m
threshold-hit share), writes _docs/06_metrics/real_flight_
validation_{date}.md, and asserts honest PASS/FAIL with no @xfail
mask. AZ-404's 1-min test is untouched (sibling, not replacement).
Extends gps_compare.py with HorizontalErrorDistribution +
percentile_sorted (numpy-equivalent linear interpolation). New
test helper _report_writer.py renders the canonical Markdown
schema documented as FT-P-20 in blackbox-tests.md.
16 new unit tests pin distribution arithmetic, verdict gate,
failure-message templating (references calibration acquisition
method per AC-3), and report layout. 129 passed in focused
regression, 3 skipped (real video / Tier-2 prerequisites).
Zero new mypy --strict errors.
Co-authored-by: Cursor <cursoragent@cursor.com>
Real derkachi.tlog covers 3 takeoffs at the same field but the
uploaded video covers only the last. Original NCC argmax + AZ-405
head-takeoff fallback both biased toward flight 1, violating the
spec's "the last chunk in tlog is relevant" framing.
Patch: pre-NCC flight segmenter partitions the IMU energy stream
into distinct flights (threshold + gap walk); find_aligned_window
restricts NCC search to the last segment; low-confidence fallback
uses that segment's start instead of head-takeoff detection.
AlignedWindow gains flight_count_detected + selected_flight_index
for FDR-visible audit.
7 new unit tests (segmenter shapes + end-to-end multi-flight
pipeline + segmented fallback path). 19 AZ-698 tests pass, 113
in the regression slice. Zero new mypy --strict errors.
Co-authored-by: Cursor <cursoragent@cursor.com>
Adds find_aligned_window cross-correlation (NCC, per-window unit norm)
between IMU energy and video optical-flow magnitude. Returns
AlignedWindow{tlog_start_ns, tlog_end_ns, offset_ms, confidence,
used_fallback}, with fallback to head-takeoff on low confidence to
preserve AZ-405 behavior. TlogReplayFcAdapter honors tlog_start_ns and
skips pre-window messages. New --auto-trim CLI flag, mutex with
--time-offset-ms. AC-1..AC-4 covered by unit tests; AC-5 skipped (no
real flight_derkachi.mp4 in repo). 106 tests pass in regression slice.
Zero new mypy --strict errors.
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>
Pre-implement chore commit to land orchestration artifacts produced by
autodev cycle-2 Step 9 (New Task), so that Step 10 (Implement) starts
against a clean working tree.
What's included:
- .gitignore: exclude _docs/00_problem/input_data/**/*.{tlog,mp4,h264}
(derkachi.tlog is a 5.8 MB binary input and stays out-of-band).
- _docs/02_tasks/todo/AZ-697..AZ-702: 6 new PBI specs under epic AZ-696
(tlog ground-truth extractor, mid-flight trim+align, real-flight
validation runner, replay map viz, HTTP replay API, KHP20S30 calib).
- _docs/02_tasks/_dependencies_table.md: dep edges for the 6 PBIs.
- _docs/_autodev_state.md: status -> in_progress, step 10 cycle 2.
- _docs/_process_leftovers/...opencv_pin_deferred.md: replay-attempt
timestamp refreshed (gtsam-numpy-2 wheels still not published;
leftover remains open).
No source code is modified by this commit.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Added `.env.test` to `.gitignore` to exclude test environment variables.
- Enhanced `docker-compose.test.jetson.yml` to include the real satellite-provider .NET service and its PostgreSQL database, replacing the mock service.
- Updated test execution policy to mandate all tests run exclusively on Jetson hardware, deprecating the previous two-tier model.
- Revised documentation in `_docs/LESSONS.md`, `_docs/02_document/tests/environment.md`, and `_docs/04_deploy/ci_cd_pipeline.md` to reflect the new testing strategy and environment setup.
- Improved `run-tests-jetson.sh` script to ensure proper environment variable handling and satellite-provider integration.
This commit aligns the testing framework with production environments, enhancing reliability and coverage.
- Enhanced `.env.example` with detailed CMake build flags and replay-mode strategy flags for development and CI environments.
- Updated `.gitignore` to include a new deploy rollback bookmark.
- Revised `_docs/_autodev_state.md` to reflect the current task status and steps.
- Added new lessons to `_docs/LESSONS.md` regarding testing and architectural improvements.
- Documented changes in `_docs/02_document/deployment/ci_cd_pipeline.md` to reflect the relaxed OpenCV version pin.
- Updated test data documentation in `_docs/02_document/tests/test-data.md` to clarify fixture usage and paths.
This commit continues the cycle-1 documentation sync and addresses various configuration updates for improved clarity and functionality.
Batch 5b completes the helpers sweep for cycle-1 Step 13.
For each of the four remaining helpers (sha256_sidecar,
engine_filename_schema, ransac_filter,
descriptor_normaliser):
- Append "Cycle-1 operational reality" section to the
existing common-helpers/<NN>_*.md, documenting the
shipped interface, exception types, public constants,
determinism / validation invariants, and AZ-task
lineage.
Specific cycle-1 facts captured per helper:
- sha256_sidecar (AZ-280): single Sha256SidecarError
hierarchy, SIDECAR_SUFFIX public constant, sidecar
format is pure lowercase 64-char hex (no JSON),
verbatim ".sha256" suffix append, streaming digests
in 1 MiB chunks, verify-returns-False semantics for
missing payload vs. raise for missing sidecar,
byte-deterministic aggregate_hash with sorted-by-str
basenames.
- engine_filename_schema (AZ-281):
EngineFilenameSchemaError, ENGINE_SUFFIX and
ALLOWED_PRECISIONS public constants, strict model
validation ([a-z0-9_]+ ≤64 chars no __), dotted
version regex, non-bool sm validation, matches_host
ignores precision by design.
- ransac_filter (AZ-282 / AZ-623): RansacFilterError,
frozen RansacResult dataclass, cv2.setRNGSeed(0)
determinism, median-not-mean residual, NaN for empty
inliers, min_inliers is informational only,
filter_correspondences uses perspectiveTransform vs.
compute_reprojection_residual uses projectPoints, OK
to import se3_utils (both Layer 1).
- descriptor_normaliser (AZ-283 / AZ-338):
DescriptorNormaliserError, ALLOWED_DTYPES =
(float16, float32), float32 norm computation with
dtype-preserving cast-back, new
intra_cluster_normalise method for NetVLAD per-cluster
L2 (AZ-338), descriptor_metric returns
"inner_product" string.
Two contract files (descriptor_normaliser.md and
ransac_filter.md mention follow-up) need follow-up
minor revisions to match shipped surface; queued for
the contracts-folder sweep.
Bumps _docs/_autodev_state.md sub_step to
tests-doc-updates phase 9.
Co-authored-by: Cursor <cursoragent@cursor.com>
Batch 5a of the cycle-1 doc sync. For each of the four
foundation helpers (imu_preintegrator, se3_utils,
lightglue_runtime, wgs_converter):
- Append "Cycle-1 operational reality" section to the
existing common-helpers/<NN>_*.md, documenting what the
shipped implementation actually exposes vs. the design-
intent sketch (interfaces, exception types, public
constants, AZ-task lineage).
Specific cycle-1 facts captured per helper:
- imu_preintegrator (AZ-276): make_imu_preintegrator
factory, BMI088-class noise defaults, single
ImuPreintegrationError exception, actual return type is
PreintegratedCombinedMeasurements (consumer builds the
CombinedImuFactor), destructive reset_with_bias semantics,
first-sample-not-integrated dt=0 handling.
- se3_utils (AZ-277): SE3 = gtsam.Pose3 re-export,
Se3InvalidMatrixError, strict caller-orthogonalisation
invariant, _DEFAULT_ROT_ATOL=1e-6 and small-angle Taylor
cutoff for exp_map, is_valid_rotation predicate, strict
dtype=float64 everywhere.
- lightglue_runtime (AZ-278 / R14 fix): EngineHandle
Protocol-typed constructor, LightGlueRuntimeError +
LightGlueConcurrentAccessError, non-blocking concurrent-
access guard (raises rather than serialises),
match_batch equal-length precondition, composition-root
single-instance into C2.5 + C3.
- wgs_converter (AZ-279 + AZ-490): WEB_MERCATOR_MAX_LAT_DEG
and MAX_ZOOM constants, WgsConversionError, ECEF arrays
are ndarray(3,) float64, new horizontal_distance_m method
(AZ-490 takeoff-origin bounded-delta gate), slippy-map
tile math hand-rolled to match satellite-provider on-disk
layout.
Two contract files (imu_preintegrator.md and
wgs_converter.md) need follow-up minor revisions to match
shipped surface; queued for the next contracts-folder
sweep, noted inline in each helper's new section.
Also refresh D-CROSS-CVE-1 opencv-pin leftover replay
timestamp (8-min debounce — gtsam upstream state cannot
change in that window).
Bumps _docs/_autodev_state.md sub_step detail.
Co-authored-by: Cursor <cursoragent@cursor.com>
Batch 4 of the cycle-1 component-doc sync. For each of C10
(provisioning), C11 (tilemanager), C12 (operator_orchestrator),
and C13 (fdr):
- Append "Cycle-1 operational reality" paragraph to § 1
documenting the actual cycle-1 wiring path:
- C10: operator-side / cross-tier; NOT in _STRATEGY_REGISTRY;
composed via runtime_root/c10_factory.py with six per-service
factories; reuses C7 InferenceRuntime for engine compile;
AZ-323 Ed25519 signer + C10ManifestConfig signing-mode gate;
AZ-324 ManifestVerifierImpl with airborne/operator modes;
AZ-507 c6 cuts kept in c10_factory; AZ-687 N/A.
- C11: operator-workstation-only; airborne build target
excludes source tree (ADR-004 / AC-8.4); composed via
runtime_root/c11_factory.py with three per-service factories;
distinct FdrClient producer_ids for signing_key + tile_uploader;
AZ-320 IdempotentRetryTileUploader wraps by default;
AZ-507 keeps c6 surfaces caller-injected; AZ-687 N/A.
- C12: operator-workstation CLI binary; airborne build excludes
source tree (ADR-004 + Principle #9); composed via
runtime_root/c12_factory.py; OperatorOrchestratorServices
dataclass aggregates AZ-326/327/328/329/330/489 services with
sibling fields defaulting to None; AZ-507 cuts via
RemoteCacheProvisionerInvoker + TileDownloaderCut/UploaderCut;
AZ-687 N/A.
- C13: airborne infrastructure; pre_constructed[c13_fdr] seeded
FIRST via make_fdr_client(AIRBORNE_MAIN_PRODUCER_ID, config)
(AZ-619 Phase A); per-producer _CACHE gives AC-619.2 singleton;
AZ-274 drop-oldest overrun policy wired at construction;
c1_vio / c5_state require it, c2_5/c3/c3_5/c4 optional; AZ-687
guard explicitly does NOT apply — seed runs before any block
presence check so replay binaries still write FDR.
Also bump _docs/_process_leftovers/2026-05-11_d_cross_cve_1_opencv_pin_deferred.md
replay timestamp to 17:18 (start of this /autodev invocation);
gtsam==4.2.1 still requires numpy<2.0.0 so the relaxed opencv pin
remains in effect.
Update _docs/_autodev_state.md sub_step.detail to record batch
4/~5 done; next batch is the 8 helpers under common-helpers/.
Co-authored-by: Cursor <cursoragent@cursor.com>
Batch 3 of the cycle-1 component-doc sync. For each of C6
(tile_cache), C7 (inference), C8 (fc_adapter):
- Append "Cycle-1 operational reality" paragraph to § 1
documenting the actual cycle-1 wiring path:
- C6: infrastructure seeded via build_pre_constructed's
c6_descriptor_index (BUILD_FAISS_INDEX-gated) and
c6_tile_store slots; no _STRATEGY_REGISTRY slot;
AZ-687 replay-mode guard skips both seeds when the
minimal replay Config omits the c6_tile_cache block.
- C7: single InferenceRuntime built once via
_build_c7_inference, identity-shared as the engine
source for c3_lightglue_runtime (AZ-622 phase D);
C7_AIRBORNE_BUILD_FLAGS lists tensorrt (production-
default) + pytorch_fp16 (Tier-0 fallback);
onnx_trt_ep deliberately omitted from airborne flags;
AZ-687 replay-mode guard cascades to c3_lightglue_runtime.
- C8: composed via a SEPARATE registry path
(runtime_root/fc_factory.py) with its own _FC_REGISTRY
+ _GCS_REGISTRY; per-binary bootstrap modules register
concrete strategies under BUILD_FC_* / BUILD_GCS_*
flags; bind_outbound_emit_thread enforces the
single-writer outbound invariant (AC-6).
- Add "Cycle-1 Tier-2 follow-up dependencies" subsection
in § 7 of C7 only: onnx_trt_ep is implemented and the
inference_factory recognises BUILD_ONNX_TRT_EP_RUNTIME,
but airborne config selecting it raises a clean
AirborneBootstrapError pointing only at the two airborne
options. C6 and C8 have no parked Tier-2 strategies for
cycle-1.
None of c6/c7/c8 import cv2 directly, so no OpenCV pin
row is added to § 5 (D-CROSS-CVE-1 leftover stays as it
is; the relaxed pin is recorded against c2.5/c3/c3.5/c4/c5
where the imports actually live).
Also refresh the D-CROSS-CVE-1 leftover replay timestamp
(condition still upstream-gated: gtsam wheels remain
numpy<2) and bump the autodev state's sub_step.detail to
record "batch 3/~5 done (c6/c7/c8); 4 components + 8
helpers + tests/ remain".
Co-authored-by: Cursor <cursoragent@cursor.com>
Batch 2 of the cycle-1 component-doc sync. For each of C3.5
(AdHoP), C4 (Pose), C5 (State):
- Append "Cycle-1 operational reality" paragraph to § 1
documenting the _STRATEGY_REGISTRY wiring, the
AIRBORNE_REQUIRED_PRE_CONSTRUCTED_KEYS slot, and the
composition-time errors raised on missing seeds.
- Relax the OpenCV pin in § 5 to >=4.11.0.86,<4.12 with a
pointer to the D-CROSS-CVE-1 leftover (C5 adds a new row
for the AZ-389 orthorectifier subsystem's cv2 import).
- Add "Cycle-1 Tier-2 follow-up dependencies" subsection
in § 7 where applicable: C3.5 calls out the airborne
registry's omission of PassthroughRefiner; C5 calls out
the AZ-389 orthorectifier wiring (default OFF) and the
AZ-624 operator-supplied flight metadata that must land
before flipping orthorectifier.enabled=True. C4 has no
parked Tier-2 (only opencv_gtsam is defined).
Also refresh the D-CROSS-CVE-1 leftover replay timestamp
(condition still upstream-gated: gtsam wheels remain
numpy<2) and bump the autodev state's sub_step.detail to
record "batch 2/~5 done (c3_5/c4/c5); 7 components + 8
helpers + tests/ remain".
Co-authored-by: Cursor <cursoragent@cursor.com>
Item 2 (C1) + item 3 batch 1 of ~5 (C2 VPR, C2.5 Rerank, C3 Matcher)
of the cycle-1 component-description reconciliation called out in
ripple_log_cycle1.md.
For each touched description.md:
- Add a "Cycle-1 operational reality" paragraph in section 1 that
names the _STRATEGY_REGISTRY + register_airborne_strategies()
runtime gate (AZ-591), the pre_constructed dict path through
compose_root (AZ-618 umbrella), the per-component
AIRBORNE_REQUIRED_PRE_CONSTRUCTED_KEYS row, and any cycle-1
strategy-default vs documented-primary disambiguation
(net_vlad as the C2 default; xfeat parked from the C3 airborne
registry).
- Relax the OpenCV row in section 5 Key Dependencies to the
D-CROSS-CVE-1 cycle-1 pin (>=4.11.0.86,<4.12) wherever the
component imports cv2 (C2 preprocessors, C2.5 ORB placeholder,
C3 RANSAC + reprojection).
- Add a "Cycle-1 Tier-2 follow-up dependencies" subsection in
section 7 only for components with a strategy module that is
built but parked from the airborne registry (C3 xfeat).
Refresh ripple_log_cycle1.md follow-up ordering with per-batch
progress + extracted batch pattern so the next batch session has
a self-contained recipe. Bump _autodev_state.md sub_step.detail
to reflect batch 1 completion (10 components + 8 helpers + tests/
remain).
Co-authored-by: Cursor <cursoragent@cursor.com>
Replay check on 2026-05-19: PyPI still shows gtsam==4.2.1 (built
against numpy<2 ABI). Replay precondition (numpy>=2 stable wheels
for SE(3) backend) still NOT met; leftover remains open.
Co-authored-by: Cursor <cursoragent@cursor.com>
Backfill the uncommitted Step 12 (Test-Spec Sync) output for the
resilience-tests and traceability-matrix surfaces; these were
produced by the test-spec skill in cycle-update mode but never
landed as a git commit before the flow moved to Step 13.
Co-authored-by: Cursor <cursoragent@cursor.com>
Item 1 of the deferred Step 13 refresh set per
_docs/02_document/ripple_log_cycle1.md.
architecture.md:
- Components C1: KltRansac is the cycle-1 operational default while
AZ-332/AZ-333 are BLOCKED awaiting Tier-2 prerequisites; ADR-001 /
ADR-002 unchanged (the seam holds; the selection shifted).
- Principle #3: same KltRansac note (cross-link to Components).
- § Technology Stack: OpenCV pin row reflects the cycle-1 relaxation
to >=4.11.0.86,<4.12 with the leftover-file pointer; OKVIS2 + VINS-
Mono rows note BLOCKED with AZ-592 / AZ-593 follow-ups.
- § NFR: Dependency CVE pinning row notes the relaxation and the
CVE-2025-53644 re-validation owed before close.
- § ADR-001: cycle-1 operational note (KltRansac default; AZ-332/333
facade-only; AZ-589/590 closed Won't-Fix).
- § ADR-009: new Cycle-1 implementation subsection covers
_STRATEGY_REGISTRY + register_strategy (AZ-591) and the
pre_constructed kwarg + build_pre_constructed (AZ-618 umbrella;
Phases A-F including AZ-625 / AZ-687).
module-layout.md:
- shared/runtime_root entry: package layout (was single file in the
Plan-era sketch); new public-surface table covering __init__.py,
airborne_bootstrap.py, _replay_branch.py, and the per-component
factory modules; ownership rows extended (AZ-591, AZ-618, AZ-625,
AZ-687).
system-flows.md: intentionally not modified — F2 / F8 narratives are
at the component-flow abstraction level and do not reference
compose_root / pre_constructed mechanics, so they have not drifted.
Items 2-4 of the ripple-log refresh set (C1 description, the other
13 components, 8 helpers, tests/*.md) remain deferred to subsequent
sessions.
State: Step 13 stays in_progress; sub_step advanced to phase 6
(component-doc-updates).
Co-authored-by: Cursor <cursoragent@cursor.com>
Step 8 testability_assessment.md already exists (2026-05-16 verdict
"Code is testable -- no changes needed"). Step 9 (Decompose Tests),
Step 10 (Implement Tests), Step 11 (Run Tests) all completed earlier
in cycle 1; their artifacts are intact. Next un-done step is Step 12
which needs to fold AZ-591, AZ-618 umbrella (AZ-619..AZ-625), and
AZ-687 implementation-learned ACs into the test-spec files (last
touched 2026-05-09, no AZ-6xx references).
Co-authored-by: Cursor <cursoragent@cursor.com>
Appends a 2026-05-19 addendum to implementation_completeness_cycle1
acknowledging AZ-591, the AZ-618 umbrella (AZ-619..AZ-625), and AZ-687.
All landed since the 2026-05-16 verdict was written. Updated counts:
116 audited tasks (was 107) / 114 PASS / 0 FAIL / 4 BLOCKED-with-
Tier-2-handle (AZ-332->AZ-592, AZ-333->AZ-593, AZ-624 AC-5, AZ-687
AC-687-3 -- the last two share a single Jetson run artifact).
Gate verdict: Step 7 CLEARED to advance. Auto-chain -> Step 8 (Code
Testability Revision). Pending Tier-2 evidence files are tracked
inside the report addendum and rewind the flow only if the Deploy
gate (Step 16) rejects them.
Co-authored-by: Cursor <cursoragent@cursor.com>
The 9bdc868 commit landed AZ-687 code + review + spec move but missed
the batch_97_cycle1_report.md write. This commit backfills that report
with the same template batch 96 uses (Task Results / Files Changed /
AC Test Coverage / Test Run / Code Review / Constraint Compliance /
Tracker / Loop Status), recording AC-687-3 (Jetson Tier-2 e2e) as
BLOCKED on operator-supplied hardware evidence per the AZ-332/AZ-333
precedent.
Co-authored-by: Cursor <cursoragent@cursor.com>
Replay CLI synthesizes a minimal Config whose `components` mapping
omits the strategy-component blocks (`c6_tile_cache`, `c7_inference`,
`c5_state`) the airborne bootstrap historically read unconditionally.
Add `_replay_omits_component_block` and gate the c6 seeds, the c7 +
c3_lightglue_runtime pair, and the c5 (estimator, handle) eager build
on `config.mode == "replay" AND block absent`. Live mode and any
replay config that DOES populate the blocks remain unchanged — the
guard is conditional, not blanket.
The skip is safe because compose_root's per-component wrappers only
run for slugs in `config.components`; absent blocks mean absent
wrappers, so the seeded slots would never be read. Fix lives at the
BUILD-PRE-CONSTRUCTED layer per the spec's explicit "no silent fallback
in `_c6_config`" constraint.
Covers AC-687-1 / AC-687-2 / AC-687-4. AC-687-3 (Jetson Tier-2 e2e
replay) requires an out-of-band hardware re-run; evidence destination
documented in autodev state.
Co-authored-by: Cursor <cursoragent@cursor.com>
Replay condition still unmet: PyPI shows gtsam==4.2.1 as the latest
stable with requires_dist numpy<2.0.0,>=1.11.0. Leftover remains open
pending upstream gtsam wheels that target numpy>=2.
Co-authored-by: Cursor <cursoragent@cursor.com>
Jetson Tier-2 e2e on 2026-05-19 11:27 surfaced a NEW gap one phase
deeper than where Rerun 3 died: build_pre_constructed seeds
c6_descriptor_index unconditionally, which reads
config.components["c6_tile_cache"] via storage_factory._c6_config.
The replay CLI synthesizes a Config that has no c6_tile_cache
block, so AC-1/2/5/6 fail with KeyError 'c6_tile_cache'.
Bootstrap (no source code changes):
- AZ-687 (Story, To Do, 2pt, Epic AZ-602; blocks AZ-618)
- Task spec in _docs/02_tasks/todo/
- _dependencies_table.md row + header narrative
- _docs/_autodev_state.md detail repointed at AZ-687
- _docs/03_implementation/jetson_runs/ Tier-2 evidence
The fix itself lives in batch 97 (next session): guard the c6/c7
seeds at the BUILD-PRE-CONSTRUCTED layer when config.mode ==
"replay". Per existing storage_factory._c6_config docstring the
silent-fallback path is explicitly rejected — the bootstrap layer
is the right seam.
Co-authored-by: Cursor <cursoragent@cursor.com>
Wire register_airborne_strategies + build_pre_constructed +
compose_root(config, pre_constructed=...) into runtime_root.main(). The
existing exception block now catches AirborneBootstrapError distinctly
before the broader (ConfigurationError, StrategyNotLinkedError,
RuntimeError) clause so the operator-facing "airborne_bootstrap:"
prefix carried by every bootstrap error reaches stderr cleanly with
EXIT_GENERIC_FAILURE rather than getting absorbed into a generic
backtrace.
This closes the AZ-618 umbrella: AZ-619..AZ-623 + AZ-625 had built
each pre_constructed key; this batch lands the integration that the
production main() actually invokes them. Both the live
gps-denied-onboard and replay gps-denied-replay binaries dispatch
through this main() per ADR-011, so both reach takeoff with
pre_constructed populated end-to-end.
Tests: tests/unit/runtime_root/test_az618_pre_constructed.py adds 6
tests covering AC-618-1..AC-618-4 + AZ-624 local handler-ordering
regression guard. The strategy factories are stubbed at the
airborne_bootstrap module boundary so the test exercises the
integration seam without standing up gtsam / FAISS / TensorRT /
PyTorch / OpenCV at unit-test scope.
AC-618-5 (Jetson tier-2 e2e) is BLOCKED on operator-supplied hardware
evidence: scripts/run-tests-jetson.sh
tests/e2e/replay/test_derkachi_1min.py must run on Jetson Orin Nano
(JetPack 6.2.2+b24) and the terminal log path + JetPack version + run
timestamp captured per _docs/02_document/tests/tier2-jetson-testing.md.
Quality gates: ruff format clean, ruff lint clean, 6/6 new umbrella
tests pass, 261/261 runtime_root + c5_state regression suite passes,
25/25 test_az401_compose_root_replay regression passes, full Tier-1
unit suite 2150/2151 passes (1 unrelated pre-existing failure:
c12_operator_orchestrator subprocess cold-start NFR fails on Mac dev
host's Python startup ~700 ms; not regressed by AZ-624). Code review
verdict PASS (1 Low finding; full report in
_docs/03_implementation/reviews/batch_96_review.md).
Archives AZ-624 task spec + AZ-618 umbrella reference to done/.
Co-authored-by: Cursor <cursoragent@cursor.com>
Wire the airborne bootstrap to seed pre_constructed['c5_isam2_graph_handle']
so c4_pose's compose-time lookup is satisfied (c4_pose runs before c5_state in
topological order; the iSAM2 graph handle is built INSIDE the C5 estimator's
constructor and so must be produced eagerly at bootstrap time).
build_pre_constructed now invokes a new internal _build_c5_state_estimator_pair
helper that calls state_factory.build_state_estimator once, captures the
(estimator, handle) tuple, and seeds two slots: 'c5_isam2_graph_handle' for
C4's lookup, and an internal '_c5_prebuilt_estimator' look-aside key for the
C5 wrapper's short-circuit. _c5_state_wrapper checks the look-aside key first
and returns the prebuilt instance as-is — the SAME object the handle was
extracted from, so c4_pose._isam2_handle and c5_state._isam2_handle reference
ONE object across the C4 / C5 seam (AC-625.3 cross-seam identity invariant).
C5_STATE_BUILD_FLAGS mirrors state_factory._STATE_BUILD_FLAGS so the bootstrap
can name the gating BUILD_STATE_* flag in operator errors before the lower
level StateEstimatorConfigError fires (AC-625.2). When the factory itself
rejects the configuration with the flag ON, the error wraps into
AirborneBootstrapError with __cause__ preserved (matches AZ-621 / AZ-622
patterns).
Constraints respected per AZ-618 umbrella: no per-component factory signature
changed; additive on top of AZ-619..AZ-623; no edits under state_factory,
pose_factory, or c5_state internals.
Tests: tests/unit/runtime_root/test_az625_c5_isam2_graph_handle_ordering.py
adds 8 tests covering AC-625.1..3 (presence + Protocol conformance, internal
key invariant, BUILD-flag-OFF error, unknown-strategy error, factory error
wrapping, cross-seam identity, wrapper short-circuit, wrapper fallback).
Autouse stubs added to test_az619/620/621/622/623 so prior phase tests stay
isolated from the new builder.
Quality gates: ruff format clean, ruff lint clean, 32/32 phase tests pass,
255/255 runtime_root + c5_state regression suite passes. Code review verdict
PASS (2 Low findings; full report in
_docs/03_implementation/reviews/batch_95_review.md).
Co-authored-by: Cursor <cursoragent@cursor.com>
build_pre_constructed now populates c3_lightglue_runtime
(LightGlueRuntime) + c3_feature_extractor (FeatureExtractor) on top
of AZ-619/620/621. Strategy-specific BUILD_MATCHER_* flag mismatch
raises AirborneBootstrapError naming the missing flag and the c3_matcher
consumer; the c7 InferenceRuntime built earlier in the bootstrap is
reused as the engine source so no double-build at this layer.
C3MatcherConfig gains optional lightglue_weights_path: Path | None
for the operator's deployment config; production main() (AZ-624)
populates it. Real LightGlue inference correctness is verified by
AZ-624's Jetson AC-5 run per the AZ-622 Tier-2 Note.
Phase tests for AZ-619/620/621 gain an autouse _stub_c3_matcher_builders
fixture so additivity assertions remain valid as the bootstrap grows.
Code review: PASS_WITH_WARNINGS (3 Low: signature drift from spec,
_is_build_flag_on duplication across 3 runtime_root modules, and
BuildConfig literal mirrored with per-strategy build configs). All
deferred to future hygiene PBIs.
Co-authored-by: Cursor <cursoragent@cursor.com>
Catches up implement skill Step 14.5 cadence (K=3 missed since
batches 82-84): one review covering the 88-92 window after the
previous session backfilled the missing 85-87 review at the wrong
path. Renames reviews/cumulative_review_batches_85_87.md to the
canonical cumulative_review_batches_85-87_cycle1_report.md so the
implement skill's resumability detects it.
Cumulative review 88-92 verdict: PASS_WITH_WARNINGS.
- CR-F1/F2 carry-overs from 85-87 escalated (write_csv_evidence +
_resolve_fixture_path duplication now in 17 files each).
- CR-F3 process: batch_90/91_review.md missing on disk; batches'
inline self-reviews substitute.
- Phase 7 architecture clean: airborne_bootstrap.py imports all
Layer-5 sibling or lower, no new cycles, public APIs respected.
State: still Step 7 (Implement) sub_step 16 batch-loop. Next: batch
93 = AZ-622 (Phase D, 3cp) — fresh session recommended.
Co-authored-by: Cursor <cursoragent@cursor.com>
Third subtask of AZ-618. Extends airborne_bootstrap.build_pre_constructed
additively with c7_inference (GPU InferenceRuntime). Wraps the existing
inference_factory.build_inference_runtime so a BUILD_TENSORRT_RUNTIME /
BUILD_PYTORCH_FP16_RUNTIME mismatch surfaces a clear operator-facing
AirborneBootstrapError naming BOTH airborne C7 flags plus the consuming
component slug, rather than bubbling up RuntimeNotAvailableError with no
context.
New public const C7_AIRBORNE_BUILD_FLAGS pairs each airborne runtime
with its gating env flag (onnx_trt_ep deliberately omitted — research
only). Tests stub at the factory boundary; real GPU/TensorRT load
remains Tier-2 only (consolidated at AZ-624). AZ-619 and AZ-620 test
files extended with a _stub_c7_inference_builder autouse fixture
mirroring the AZ-620 pattern for _build_c6_*.
18/18 runtime_root unit tests pass.
Co-authored-by: Cursor <cursoragent@cursor.com>
Second of six subtasks of AZ-618. Extends
airborne_bootstrap.build_pre_constructed(config) additively with the
two C6 storage entries on top of AZ-619's c13_fdr + clock contract:
- c6_descriptor_index: via storage_factory.build_descriptor_index
- c6_tile_store: via storage_factory.build_tile_store
When BUILD_FAISS_INDEX=OFF, the lower-level RuntimeNotAvailableError
from the descriptor index factory is translated into an
AirborneBootstrapError that names the missing key
(c6_descriptor_index), the gating flag (BUILD_FAISS_INDEX), and the
consuming component slug(s) drawn from
AIRBORNE_REQUIRED_PRE_CONSTRUCTED_KEYS. The original error is
preserved as __cause__ so operators still see the upstream reason.
Tests: 3 new unit tests cover AC-620.1 + AC-620.2 (twice, with and
without a configured consumer, so the bootstrap fails loudly in
either branch). AZ-619 tests updated to add an autouse stub for the
Phase B builders (keeps them focused on Phase A keys) and to relax
the "exactly two keys" assertion to "AZ-619 keys remain present
under AZ-620 additivity" per the original test's own forward-pointer.
Bonus: ruff --fix removed 12 pre-existing UP037 quoted-annotation
warnings in airborne_bootstrap.py (covered by `from __future__ import
annotations`). All in modified-area scope per quality-gates.mdc.
Run: pytest tests/unit/runtime_root/ -q -> 15/15 passed in 1.06s.
Spec moved to _docs/02_tasks/done/ in the previous commit (audit-trail
backfill of batch_90 also landed there).
Co-authored-by: Cursor <cursoragent@cursor.com>
Prior session committed AZ-619 (Phase A of AZ-618) as 8abfb02,
transitioned the tracker, and archived the spec, but did not write
the batch report. Content reconstructed from git show + the AZ-619
task spec + the prior _docs/_autodev_state.md sub_step.detail.
No code change. Pure audit-trail housekeeping.
Co-authored-by: Cursor <cursoragent@cursor.com>
Adds airborne_bootstrap.build_pre_constructed(config) returning a
dict with the two foundational keys: a per-binary shared FdrClient
under "c13_fdr" (via make_fdr_client with the new
AIRBORNE_MAIN_PRODUCER_ID constant) and a fresh WallClock under
"clock". Phases B..F (AZ-620..AZ-624) extend this function
additively without breaking the AZ-619 contract.
The c13_fdr instance is identity-stable across calls (per the
make_fdr_client per-producer cache) so callers can call
build_pre_constructed twice and get the same FdrClient back -
AC-619.2.
Replay-mode override is unchanged: compose_root merges
replay_components over pre_constructed so the WallClock here is
replaced by TlogDerivedClock in replay binaries (existing
contract documented in compose_root's docstring).
Tests: 5 new unit tests under tests/unit/runtime_root/
test_az619_pre_constructed_phase_a.py, all passing. AZ-591 not
regressed (12/12 in the combined run).
Spec moved to _docs/02_tasks/done/.
Co-authored-by: Cursor <cursoragent@cursor.com>
The AZ-618 spec author flagged "likely a true 8" with a recommended
6-subtask split; combined with the user-rule cap on PBI complexity
(create at 2-3pt, max 5pt) the right move was to split before any
implementation began. Subtasks created in Jira as children of AZ-618:
AZ-619 (Phase A) c13_fdr + clock 2pt
AZ-620 (Phase B) c6_descriptor_index + c6_tile_store 3pt
AZ-621 (Phase C) c7_inference engine 3pt
AZ-622 (Phase D) c3_lightglue_runtime + c3_feature_extractor 3pt
AZ-623 (Phase E) c282_ransac_filter + c5 helpers 3pt
AZ-624 (Phase F) wire main() + AC-1..AC-5 + Jetson 2pt
Aggregate: 16pt actionable work (vs. AZ-618's original 5pt filing,
which the author had already qualified as understated). AZ-618 stays
In Progress in Jira as the umbrella tracker; its task spec file is
now an umbrella reference pointing to the 6 phase-specific spec files.
Deps table updated: AZ-618 row reduced to 0pt with subtask deps; six
new rows added; header counts refreshed (156 -> 162 tasks, 522 -> 533
points). Autodev state set to phase=1 (parse) for the next batch =
AZ-619 (Phase A) only.
Co-authored-by: Cursor <cursoragent@cursor.com>
Codifies that Tier-1 (local pytest + Docker) is necessary but NOT
sufficient: Tier-2 (Jetson Orin Nano via run-tests-jetson.sh) is the
product-completeness gate for runtime_root, c7_inference, c3_matcher,
c2_5_rerank, replay_input, and the replay CLI. Documents the
mandatory-Tier-2 scope, what Tier-1-only stubs cannot prove, the
operating procedure, and what batch reports must capture for in-scope
changes. Surfaced by the Step-11 cycle-1 finding that AZ-618 was only
caught because Tier-2 was actually run.
Co-authored-by: Cursor <cursoragent@cursor.com>
Append AZ-618 row to _dependencies_table.md (5pt, 12 dep tasks all in
done/, epic AZ-602) and refresh totals (155→156 tasks, 517→522 pts).
Mark autodev state in_progress at sub_step phase 1 (parse) so the
implement skill can pick up batch 90 with a clean tree per the
2026-05-18 lesson on rewinds-as-session-boundaries.
Co-authored-by: Cursor <cursoragent@cursor.com>
Captures the pattern observed this cycle: when /autodev rewinds from
Step 11 (Run Tests) back to Step 7 (Implement) due to a gate fail,
the rewind itself eats real context (task spec drafting + state
update + dependencies survey). Continuing into the destination
step's batch loop in the same conversation risks context truncation
mid-batch. Treat the rewind as a session boundary; let a fresh
/autodev invocation start the implement loop cleanly.
Co-authored-by: Cursor <cursoragent@cursor.com>
Cycle-3 addendum captures the layered Jetson rerun progression:
synth time-base fix (AZ-614) drops offset_ms from 1.7e12 to -4334;
AZ-611 skip-auto-sync then crosses the AC-9 validator; AZ-602
build-flag completeness opens VideoFileFrameSource and
TlogReplayFcAdapter; composition root logs
'replay.compose_root.ready: auto_sync_used=false', then crashes
inside runtime_root.airborne_bootstrap because production main()
never builds c13_fdr / c6_* / c7_inference / c3_lightglue_runtime /
c3_feature_extractor / c2_82_ransac_filter into pre_constructed.
The bootstrap gap is filed as AZ-618 (Story under AZ-602). It
affects both live and replay binaries -- every prior Reality-Gate
run died at auto-sync before the composition graph was walked, so
the gap was hidden. The 38 compose_root unit tests pass only via
the replay_components_factory stub kwarg, which bypasses the
bootstrap entirely.
Autodev sub_step advances to phase 8
'az614-az611-landed-bootstrap-gap-discovered' pending the user's
decision on whether to start AZ-618 immediately or close out
Step 11 with the current Reality-Gate signal.
Co-authored-by: Cursor <cursoragent@cursor.com>
REMOTE_DIR defaults to ~/gps-denied-onboard. rsync expands the
leading tilde server-side, but the later 'bash -s <<EOF' heredoc
embeds the value literally inside cd "$REMOTE_DIR" -- and bash does
NOT expand ~ inside double quotes, so the heredoc step bails out
with 'No such file or directory'. Resolve any leading ~ against the
remote $HOME up-front so the value is safe to double-quote in both
contexts.
The previous successful Jetson runs (tasks 2388 / 915484) were
one-off ssh commands that never hit this code path; this commit
makes the script actually work end-to-end.
Co-authored-by: Cursor <cursoragent@cursor.com>
REPLAY_BUILD_FLAGS contains three names but the test compose files
only ever set BUILD_REPLAY_SINK_JSONL. Every prior Reality-Gate run
hit the auto-sync hard-fail before reaching the VideoFileFrameSource
or TlogReplayFcAdapter build-flag gates, so the omission stayed
hidden. AZ-611 makes tests bypass auto-sync, which exposes the next
gate: VideoFileFrameSource raises FrameSourceConfigError
("BUILD_VIDEO_FILE_FRAME_SOURCE is OFF; ... unavailable").
Mirror the airborne binary's flag requirements in both
docker-compose.test.yml (Colima Tier-1) and
docker-compose.test.jetson.yml (Jetson Tier-2). Comment block in
both files documents why all three must be ON.
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>
The Derkachi auto-sync coordinator compares absolute tlog timestamps
(from pymavlink's 8-byte record header) against absolute video
timestamps (CAP_PROP_POS_MSEC, which starts at 0). Anchoring the
synthetic tlog at 1_700_000_000_000_000 us (2023-11-14) produced a
~53-year offset (offset_ms=1699999995666) that always tripped the
AC-9 frame-window match validator at 0% match.
Setting the base to 0 puts the tlog on the same axis as the video
(and matches the CSV's `Time` column, which is seconds since row 0
per `_docs/00_problem/input_data/flight_derkachi/README.md`: "the
video and telemetry align at exactly three video frames per
telemetry row").
Verified on Colima with GPS_DENIED_TIER=2: the offset reported by
the auto-sync coordinator drops from 1699999995666 ms to -4334 ms.
The remaining 4.3 s offset is NOT a synth issue — it's the tlog
take-off detector (no signal in the steady-cruise CSV → defaults to
samples.accel[0][0] == 0) vs the video motion-onset detector (which
fires on a scenery-contrast false positive at ~4.3 s). The synth
cannot fabricate a take-off spike at the right time without knowing
the video motion-onset moment a priori, and the README confirms the
fixture is mid-flight footage with no take-off in either signal.
Resolving the remaining 4.3 s mismatch requires SUT-side work to
honor the documented "manual offset bypasses auto-sync" contract —
that's the scope of AZ-611. Filed as a known limitation in the
commit message; AC-1..AC-6 still red until AZ-611 lands.
Co-authored-by: Cursor <cursoragent@cursor.com>
Records the first Jetson Tier-2 run results in the step-11 report:
17 pass / 5 fail / 1 skip / 1 xfail (24 total, 10m09s) — identical to
Colima because all 5 failures hit AZ-614 (tlog time-base mismatch)
BEFORE reaching the GPU. So the infrastructure is proven (image
builds, GPU exposed inside container, SUT subprocess runs to the
auto-sync stage) but the heavy ACs haven't yet exercised ALIKED /
DISK LightGlue. Fixing AZ-614 is the gating prerequisite to actually
drive the GPU stages.
Also captures lessons learned that are now in the setup doc:
* Only dustynv/l4t-pytorch:r36.4.0 is a usable Jetson PyTorch base
on Docker Hub for R36 / JetPack 6 (l4t-base deprecated, official
l4t-pytorch has no R36 tags).
* The dustynv image bakes a maintainer-LAN-only pip mirror into
/etc/pip.conf — must be wiped + --index-url pinned to pypi.org.
* pip 24.2 (image default) rejects gtsam-4.3a0 pre-release; pip 26.x
accepts the same wheel for `gtsam<5.0,>=4.2` because there are no
stable aarch64 builds. Upgrade pip in the build, don't relax pin.
* nvidia-container-runtime mounts nvidia-smi from host, so the GPU
smoke test needs only ubuntu:22.04 (80 MB), not l4t-jetpack (5 GB).
Autodev state advances to phase 7 / jetson-harness-online.
Co-authored-by: Cursor <cursoragent@cursor.com>
Three discoveries from on-Jetson build (image builds clean in ~3m18s
after fixes; gtsam-4.3a0, torch 2.4.0+cuda, cv2 4.11.0 all import OK
inside container running --runtime=nvidia):
1. dustynv/l4t-pytorch's /etc/pip.conf bakes in a local Jetson mirror
(jetson.webredirect.org) that's only reachable from the maintainer
LAN. pip's DNS lookup fails everywhere else. Wipe the config and
pin --index-url to upstream PyPI.
2. The image ships pip 24.2. The SUT's `gtsam<5.0,>=4.2` constraint
matches ONLY gtsam-4.3a0 on PyPI (no stable aarch64 wheels), and
pip 24.x rejects pre-releases unless --pre is set. The Colima
image lands on the same wheel because its pip 26.x has explicit
fallback-to-pre-release logic. Bump pip before installing the SUT
to align resolver behavior across both harnesses.
3. Skip the [inference] extra entirely — the base image ships
Tegra-tuned torch / torchvision that re-pip would clobber with
x86 builds lacking cuDNN/cuBLAS for Orin.
Co-authored-by: Cursor <cursoragent@cursor.com>
macOS ships BSD rsync, which doesn't support GNU's --info=progress2.
Drop the flag (added --stats so we still get a summary at the end)
and document the LFS-pointer pre-smudge requirement that bit during
the first end-to-end attempt.
Co-authored-by: Cursor <cursoragent@cursor.com>
Two doc lessons learned from on-Jetson verification:
1. The `cat >> ~/.ssh/config <<'EOF'` heredoc needs a leading blank
line. Without it, the appended block fused onto the previous
file line and produced "unsupported option yesHost" at parse
time. Added an explicit blank line + comment.
2. The smoke test for nvidia-container-runtime doesn't need a 5 GB
l4t-jetpack pull — nvidia-container-runtime mounts nvidia-smi
from the host into any container, so `ubuntu:22.04 nvidia-smi`
(80 MB) is sufficient. Switched the doc.
Operator verified end-to-end:
* `ssh jetson-e2e true` works from both terminal and Cursor Shell
* `jetson` user already in `docker` group (no sudo needed)
* `docker run --runtime=nvidia ubuntu:22.04 nvidia-smi` returns
Orin GPU info inside the container
Co-authored-by: Cursor <cursoragent@cursor.com>
Operator-reported: `nvcr.io/nvidia/l4t-base:r36.4.0` fails to pull.
Investigation against the live registries confirmed:
* `nvcr.io/nvidia/l4t-base` — deprecated in JetPack 6, no r36 tags
(forum thread "L4T Base docker image for Jetpack 6.2 (r36.4.3)",
GitHub dusty-nv/jetson-containers#883).
* `nvcr.io/nvidia/l4t-pytorch` — no r36 tags at all. Newest is
r35.2.1-pth2.0-py3 (too old for our torch>=2.2 floor).
* `nvcr.io/nvidia/l4t-jetpack:r36.4.0` — exists but ships no PyTorch.
* `dustynv/l4t-pytorch:r36.4.0` (Docker Hub) — exists, ~6.3 GB ARM64,
PyTorch + torchvision + opencv pre-baked, maintained by dusty-nv
(NVIDIA's Jetson containers maintainer).
Switched Dockerfile.jetson base to `dustynv/l4t-pytorch:r36.4.0`.
Forward-compatible with the host's R36.5 BSP (NVIDIA containers
tolerate one minor BSP ahead on the host side).
Setup doc fixes:
* smoke-test command now uses `l4t-jetpack:r36.4.0` (the official
replacement for the deprecated `l4t-base`)
* keygen step explicitly states it produces BOTH halves (private +
.pub) in one go
* ssh-copy-id + ssh config show how to specify a custom port
* troubleshooting table gets a new row for the `l4t-base not found`
case so the next dev hits the answer in 30 seconds
Co-authored-by: Cursor <cursoragent@cursor.com>