AZ-650 (mission_executor pre-flight Built-In Test):
- BitEvaluator trait + BitItemStatus { Pass, Degraded, Fail, Skipped }
+ BitReport + BitOverall fusion. Pluggable per-item evaluators so
the composition root decides which dependencies are wired today.
- BitController owns evaluator list + mpsc ack channel + sticky-pass
+ ack deadline. Publishes bit_ok via tokio watch — composition root
pipes it into the telemetry projection where the existing FSM
bit_ok guard already consumes it (no FSM changes needed).
- BitState { Idle, Pass, AwaitingAck { report_id }, Failed { reason } }
with broadcast::Sender<BitEvent> for operator-side observability.
Sticky-pass semantics: once Pass is reached (directly or via signed
ack on a Degraded report), the controller stops re-evaluating —
BIT is a one-shot pre-flight gate, not a continuous monitor.
- BitDegradedAck arrives pre-validated by operator_bridge; the
controller only matches report_id and applies the operator id to
the audit log.
- Concrete evaluators landed today (3 of 12 spec items, the rest
depend on components still in todo/):
- StateDirFreeSpaceEvaluator (dir creatable/readable; statvfs is
documented follow-up).
- WallClockBoundEvaluator (chrono::Utc::now vs configurable bound).
- MissionLoadedEvaluator (waypoint count via Arc<Mutex<usize>>).
- MapObjectsSyncedEvaluator (maps SyncState -> BIT status per Q9).
Tests:
- ac1_all_pass_proceeds, ac2_fail_blocks_transition,
ac3_degraded_requires_signed_ack (+ mismatched_ack supplement),
ac4_degraded_ack_timeout_fails_the_bit — all 4 ACs green.
- Pure next_state table covered by lib unit tests.
- Per-evaluator unit tests for Pass/Fail/Degraded branches.
Quality gates:
- cargo fmt: clean.
- cargo clippy -p mission_executor --tests -- -D warnings: 0 warns.
- cargo test --workspace: all green.
- Pre-existing flake in state_machine::ac3_bounded_retry_then_success
(batch 7 report) remains pre-existing — passes on rerun.
Co-authored-by: Cursor <cursoragent@cursor.com>
6.6 KiB
Batch 8 (cycle 1) implementation report
Tasks: AZ-650
Component scope: mission_executor
Result: PASS_WITH_WARNINGS — proceed; flagged items below.
Tasks
AZ-650 mission_executor_bit_f9 — Pre-flight Built-In Test (F9)
Outcome: Implemented. All four acceptance criteria green.
Production code added:
-
crates/mission_executor/src/internal/bit.rsBitEvaluatortrait — pluggable per-item evaluator.BitItem,BitItemStatus { Pass, Degraded, Fail, Skipped },BitOverall,BitReport— typed report surface.BitDegradedAck— pre-validated byoperator_bridge(AZ-689 lane); this layer only matchesreport_id.BitController— owns evaluators + ack mpsc + sticky-pass semantics + ack timeout deadline.BitControllerHandle— read-side:bit_ok()watch,state()watch,subscribe()broadcast,last_report().BitState { Idle, Pass, AwaitingAck { report_id }, Failed { reason } }.BitEvent { Generated, StateChanged, AckTimedOut }.
-
crates/mission_executor/src/internal/bit_evaluators.rsStateDirFreeSpaceEvaluator— verifies the state directory is creatable/readable. (See limitations.)WallClockBoundEvaluator— sanity-checks wallclock vs. configurable minimum (default 2024-01-01).MissionLoadedEvaluator— fails if waypoints empty.MapObjectsSyncedEvaluator— readsMapObjectsStoreHandle::sync_stateand maps to BIT status per spec (Synced/FreshBoot=Pass, CachedFallback=Degraded, Degraded/Failed=Fail).
Tests:
crates/mission_executor/tests/bit_controller.rs(5 tests):ac1_all_pass_proceeds(AC-1).ac2_fail_blocks_transition(AC-2).ac3_degraded_requires_signed_ack(AC-3).ac3_mismatched_ack_is_ignored— supplement.ac4_degraded_ack_timeout_fails_the_bit(AC-4).
- Module unit tests in
internal::bit::tests(5 tests) cover the purenext_statetable. - Module unit tests in
internal::bit_evaluators::tests(7 tests) cover each concrete evaluator.
AC coverage
| AC | Behaviour | Test | Status |
|---|---|---|---|
| AC-1 | All-pass → bit_ok = true; controller in Pass; overall = Pass |
ac1_all_pass_proceeds |
PASS |
| AC-2 | Any Fail → bit_ok = false; controller Failed { reason }; report observable |
ac2_fail_blocks_transition |
PASS |
| AC-3 | Degraded → AwaitingAck; matching signed ack → Pass; bit_ok = true |
ac3_degraded_requires_signed_ack |
PASS |
| AC-4 | Degraded ack timeout → Failed { reason: "ack_timeout …" }; bit_ok stays false |
ac4_degraded_ack_timeout_fails_the_bit |
PASS |
Code review
Spec compliance: PASS. All four ACs implemented with test seams that demonstrate the spec'd state transitions.
Architecture compliance: PASS. Controller follows the same pattern as LostLinkDriver (AZ-651): owns its inputs (evaluators + ack mpsc), publishes a bit_ok watch channel that the composition root pipes into the telemetry projection where the existing FSM bit_ok guard already consumes it. No FSM changes required.
SRP: PASS.
bit.rs— controller + types + state machine.bit_evaluators.rs— concreteBitEvaluatorimpls only.- Pure
next_statefunction isolated for table-driven testing.
Runtime completeness: PASS_WITH_WARNINGS. Three of the twelve BIT items listed in the spec have concrete production implementations today (state_dir_free_space, wall_clock_bound, mission_loaded, mapobjects_synced_or_cached_acked). The remaining nine (mavlink_link, gimbal_link, camera_rtsp, detection_grpc, movement_telemetry_sync_ready, tier2_session_ready, vlm_session_ready, operator_bridge_session) depend on components that are still in _docs/02_tasks/todo/ (gimbal — AZ-653..656; frame_ingest — AZ-657..659; operator_bridge — AZ-689; tier2/vlm sessions — TBD). The trait + registry is in place; each remaining evaluator is one file's worth of work that lands alongside its component. This matches the existing project convention (skill-driven sequential implementation; no premature stubs).
Test discipline: PASS. Each AC maps to one named test. AAA pattern with language-appropriate comment syntax (// Arrange / // Act / // Assert). Mocks are used for BitEvaluator-injection only — controller behaviour is exercised end-to-end.
Known limitations (warnings)
-
StateDirFreeSpaceEvaluatordoes not callstatvfs. The current implementation verifies that the directory is creatable/readable. A real free-space check requires eitherfs2,nix::sys::statvfs, or a platform-specific syscall. The evaluator preservesmin_free_bytesin its API so the upgrade is a one-file change. Logged here so the operator-surface team knows the field is approximate. -
Nine BIT items are not yet wired (see Runtime completeness above). When their components land, each evaluator is one ~30-line file that plugs into the existing
BitController::new(_, evaluators, _)registry. -
mission_loadedmirror channel.MissionLoadedEvaluatorreads anArc<Mutex<usize>>that the composition root mirrors from the FSM's mission vec each time it changes. This adds one cheap clone per mission update; documented in the type's docstring.
Auto-fix attempts during the batch
tracing::warn!Send-safety fix inlost_link.rscarried over from batch 7;cargo fmtadjusted some struct-variant formatting in the same file. No logic changes.- Initial
next_statehad a bug where the Degraded branch reset*ack_deadlineon every tick (the report id changed each cycle). Fixed by making theAwaitingAckbranch sticky — samereport_id, untouched deadline — and by introducing asticky_passflag so Pass is one-shot (BIT is a pre-flight gate, not a continuous monitor). - Clippy
doc-overindented-list-itemsfix onMapObjectsSyncedEvaluator's docstring.
Test reproduction
cargo build -p mission_executor --tests
cargo test -p mission_executor # 29 tests; 0 failed
cargo clippy -p mission_executor --tests -- -D warnings
cargo test --workspace # all green; pre-existing flake in
# state_machine::ac3_bounded_retry_then_success
# remains pre-existing per batch 7 report
Candidates for batch 9
- AZ-652
mission_executor_safety_and_resume— 5 pts. All deps (AZ-648/649/643/647) indone/. - AZ-653
gimbal_a40_transport— opens up thegimbal_linkBIT evaluator slot.
Batch 9 sizing: AZ-652 alone is a sensible scope (geofence + battery thresholds + middle-waypoint re-upload + post-flight push are 6 ACs across 3 concerns).