mirror of
https://github.com/azaion/autopilot.git
synced 2026-06-21 08:31:10 +00:00
[AZ-650] mission_executor pre-flight BIT (F9) gate (batch 8)
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>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
# 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.rs`
|
||||
- `BitEvaluator` trait — pluggable per-item evaluator.
|
||||
- `BitItem`, `BitItemStatus { Pass, Degraded, Fail, Skipped }`, `BitOverall`, `BitReport` — typed report surface.
|
||||
- `BitDegradedAck` — pre-validated by `operator_bridge` (AZ-689 lane); this layer only matches `report_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.rs`
|
||||
- `StateDirFreeSpaceEvaluator` — 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` — reads `MapObjectsStoreHandle::sync_state` and 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 pure `next_state` table.
|
||||
- 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` — concrete `BitEvaluator` impls only.
|
||||
- Pure `next_state` function 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)
|
||||
|
||||
1. **`StateDirFreeSpaceEvaluator` does not call `statvfs`**. The current implementation verifies that the directory is creatable/readable. A real free-space check requires either `fs2`, `nix::sys::statvfs`, or a platform-specific syscall. The evaluator preserves `min_free_bytes` in its API so the upgrade is a one-file change. Logged here so the operator-surface team knows the field is approximate.
|
||||
|
||||
2. **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.
|
||||
|
||||
3. **`mission_loaded` mirror channel.** `MissionLoadedEvaluator` reads an `Arc<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 in `lost_link.rs` carried over from batch 7; `cargo fmt` adjusted some struct-variant formatting in the same file. No logic changes.
|
||||
- Initial `next_state` had a bug where the Degraded branch reset `*ack_deadline` on every tick (the report id changed each cycle). Fixed by making the `AwaitingAck` branch sticky — same `report_id`, untouched deadline — and by introducing a `sticky_pass` flag so Pass is one-shot (BIT is a pre-flight gate, not a continuous monitor).
|
||||
- Clippy `doc-overindented-list-items` fix on `MapObjectsSyncedEvaluator`'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) in `done/`.
|
||||
- **AZ-653** `gimbal_a40_transport` — opens up the `gimbal_link` BIT 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).
|
||||
@@ -6,8 +6,8 @@ step: 7
|
||||
name: Implement
|
||||
status: in_progress
|
||||
sub_step:
|
||||
phase: 10
|
||||
name: batch-8-selection
|
||||
phase: 12
|
||||
name: batch-9-selection
|
||||
detail: ""
|
||||
retry_count: 0
|
||||
cycle: 1
|
||||
|
||||
Reference in New Issue
Block a user