mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-21 20:01:12 +00:00
[AZ-294] [AZ-295] [AZ-296] Finish C13: tile snapshot + record-kind policy + takeoff abort
AZ-294: MidFlightTileSnapshotSink writes orthorectified tile JPEGs atomically to flight_root/<flight_id>/tiles/<tile_id>.jpg, emits a kind="mid_flight_tile_snapshot" pointer record, and evicts the oldest tile when the per-flight 64 MiB cap is exceeded. Adds optional frame_id to the snapshot payload (fdr_record_schema bump). AZ-295: RecordKindPolicy with two paired gates: - enforce_or_raise (producer-side) raises RawFrameWriteForbiddenError for raw_nav_frame / raw_ai_cam_frame at the call site, defending AC-8.5 / RESTRICT-UAV-4. - gate_for_writer (writer-side) tumbling-window rate-caps failed_tile_thumbnail records at <= 0.1 Hz; over-cap drops are coalesced into kind="overrun" records with the originating producer slug. AZ-296: take_off() composition-root sequence with strict ordering (writer.__init__ -> start -> open_flight -> fc_adapter.__init__ -> fc_adapter.open). On FdrOpenError, logs ERROR record, calls writer.stop(), prints the documented FATAL line to stderr, and sys.exit(EXIT_FDR_OPEN_FAILURE=2). composition_root_protocol bumped to v1.1.0 with the new constants + takeoff-sequence section. 29 new tests; full suite 356 passed / 2 skipped / 0 failures. No new dependencies (stdlib only). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,9 +3,9 @@
|
||||
**Component**: shared_config (cross-cutting concern owned by E-CC-CONF / AZ-246)
|
||||
**Producer tasks**: AZ-269 (config loader + outer Config) and AZ-270 (compose_root + compose_operator + StrategyNotLinkedError)
|
||||
**Consumer tasks**: every component task that takes a config block; `runtime_root.py` and `operator_tool/__main__.py` (the two composition-root entrypoints)
|
||||
**Version**: 1.0.0
|
||||
**Version**: 1.1.0
|
||||
**Status**: draft
|
||||
**Last Updated**: 2026-05-10
|
||||
**Last Updated**: 2026-05-11
|
||||
|
||||
## Purpose
|
||||
|
||||
@@ -76,8 +76,46 @@ class StrategyNotLinkedError(RuntimeError):
|
||||
| compose-operator-no-airborne | operator-side config | returns `OperatorRoot` containing only operator-tier components (e.g. C11, C12) | wrong-tier components excluded |
|
||||
| load-config-purity | call `load_config(env, paths)` twice with same inputs | identical `Config` objects (or deep-equal) | reproducibility |
|
||||
|
||||
## Takeoff Sequence (AZ-296 / E-C13 / AC-NEW-3)
|
||||
|
||||
The airborne entrypoint MUST execute the takeoff sequence in strict order:
|
||||
|
||||
1. Construct `FileFdrWriter`.
|
||||
2. Call `writer.start()`.
|
||||
3. Call `writer.open_flight(header)`.
|
||||
4. **Only if step 3 succeeded**, construct the C8 FC adapter and call its
|
||||
`open()`. The FC adapter MUST NOT be constructed before `open_flight`
|
||||
returns; this is the AC-NEW-3 every-payload-class-from-t=0 gate.
|
||||
5. Construct + start every other component.
|
||||
|
||||
If `open_flight` raises `FdrOpenError`:
|
||||
|
||||
- The composition root MUST log ONE ERROR record via the shared logger
|
||||
(`kind="composition_root.takeoff_aborted"`, `level="ERROR"`,
|
||||
`kv.reason="fdr_open_error"`, `kv.flight_root=<configured path>`,
|
||||
`kv.underlying=<str(exc)>`).
|
||||
- It MUST call `writer.stop()` to release the filelock + segment file.
|
||||
- It MUST print exactly one line to stderr:
|
||||
`FATAL: cannot open FDR at <flight_root>: <underlying message>; aborting takeoff (exit 2)`.
|
||||
- It MUST exit the process with `sys.exit(EXIT_FDR_OPEN_FAILURE)`; if
|
||||
intercepted, fall back to `os._exit(EXIT_FDR_OPEN_FAILURE)`.
|
||||
|
||||
The abort path MUST complete in ≤ 500 ms (NFR-perf-abort).
|
||||
|
||||
### Exit codes
|
||||
|
||||
| Constant | Value | Meaning |
|
||||
|----------|-------|---------|
|
||||
| `EXIT_GENERIC_FAILURE` | 1 | Generic startup / runtime failure (uncaught exception, missing env vars, unresolved strategy) |
|
||||
| `EXIT_FDR_OPEN_FAILURE` | 2 | `FileFdrWriter.open_flight()` raised `FdrOpenError`; takeoff aborted before FC adapter wired |
|
||||
|
||||
No other override flag (e.g. `--ignore-fdr-failure`) is permitted; adding
|
||||
one is a major-version bump on this contract AND a security-review-required
|
||||
change (AC-NEW-3 / RESTRICT-UAV-4).
|
||||
|
||||
## Change Log
|
||||
|
||||
| Version | Date | Change | Author |
|
||||
|---------|------|--------|--------|
|
||||
| 1.0.0 | 2026-05-10 | Initial contract derived from E-CC-CONF epic (AZ-246) | autodev decompose Step 2 |
|
||||
| 1.1.0 | 2026-05-11 | Add takeoff sequence section + `EXIT_FDR_OPEN_FAILURE` (AZ-296) | autodev batch 7 |
|
||||
|
||||
@@ -50,7 +50,7 @@ class FdrRecord:
|
||||
| `overrun` | E-CC-FDR-CLIENT itself | `{producer_id, dropped_count}` (`dropped_count > 0`) | AC-NEW-3: never silent. Emitted by drop-oldest hook |
|
||||
| `segment_rollover` | E-C13 (writer) | `{old_segment, new_segment, total_bytes_after}` | Emitted on segment rotation, including 64 GB-cap drops |
|
||||
| `failed_tile_thumbnail` | C6 / C11 | `{frame_id, tile_id, jpeg_bytes_b64}` (≤ 0.1 Hz rate cap) | AC-8.5 forensic exception |
|
||||
| `mid_flight_tile_snapshot` | C13 (snapshot path) | `{snapshot_path, captured_at}` | AC-8.4 mid-flight snapshot pointer |
|
||||
| `mid_flight_tile_snapshot` | C13 (snapshot path) | `{snapshot_path, captured_at, frame_id?}` | AC-8.4 mid-flight snapshot pointer (envelope `producer_id="shared.fdr_client"`); `frame_id` optional (AZ-294) |
|
||||
| `flight_header` | C13 (writer) | `{flight_id, flight_started_at_iso, flight_started_at_monotonic_ns, config_snapshot, signing_key_rotation_event, manifest_content_hashes, build_info}` | Single record at flight open (envelope `producer_id="shared.fdr_client"`) |
|
||||
| `flight_footer` | C13 (writer) | `{flight_id, flight_ended_at_iso, flight_ended_at_monotonic_ns, records_written, records_dropped_overrun, bytes_written, rollover_count, clean_shutdown}` | Single record at flight close (envelope `producer_id="shared.fdr_client"`) |
|
||||
|
||||
|
||||
Reference in New Issue
Block a user