mirror of
https://github.com/azaion/autopilot.git
synced 2026-06-21 12:21:10 +00:00
576a0d6a30448ed9067bc58366a667934342a314
17 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
251ebed1c2 |
[AZ-658] frame_ingest H.264/265 decoder (NVDEC + sw fallback)
Wires a real ffmpeg-next 8.1 decoder into the frame_ingest lifecycle loop. NVDEC is probed at runtime via h264_cuvid / hevc_cuvid; CUDA-less hosts transparently fall back to software h264 / hevc. Each decoded frame is stamped with capture_ts (taken at packet receipt) and decode_ts (taken after decode returns) so movement_detector sees accurate frame-arrival times. Single-frame decode errors are counted toward decode_errors_total and dropped; the stream is never aborted. Adds new public API on FrameIngestHandle: decoder_backend(), decode_errors_total(), frames_decoded_total(), decode_ms_first_frame(), decode_ms_p50(), decode_ms_p99(). Integration tests under crates/frame_ingest/tests/decoder_pipeline.rs cover AC-1, AC-3, AC-4 end-to-end through the real FfmpegDecoder using libx264-encoded synthetic streams; AC-2 positive (NVDEC selection) is opt-in via --ignored on a CUDA host. AZ-657 lifecycle tests retained via a StubDecoder. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
ccf929af69 |
[AZ-676] [AZ-677] [AZ-678] [AZ-679] telemetry+operator foundation
Batch 15 ships the four foundation tickets sitting on top of AZ-675 (gRPC server) and AZ-667 (mapobjects_store hydrate): * AZ-676: telemetry_stream video path (rtsp_forward + bytes_inline) with ai_locked atomic + session counter, SubscribeVideo RPC. * AZ-677: MapObjects snapshot-on-subscribe + diff broadcast + reconnect-resync (StartThen stream-prepend pattern). * AZ-678: HmacOperatorValidator with per-session monotonic seq, in-process session registry + TTL, constant-time HMAC compare, rejection-reason counters, sliding 60 s sig-failure red-health gate. Trait OperatorCommandValidator in shared::contracts::operator_auth. * AZ-679: PoiSurfaceMapper produces OperatorPoiEvent per architecture §7.10; PoiDequeued events on rotate/age-out/complete; pushed via new TelemetrySink::push_operator_event extension on Topic::OperatorEvent. Cross-task wiring: TelemetrySink trait extended with push_operator_event; OperatorBridge gets optional builder methods with_telemetry_sink / with_validator (composition root wires in AZ-680). Workspace deps: hmac = "0.12"; per-crate adds bytes, serde_json, parking_lot, chrono, uuid, sha2, thiserror. Tests: 14/14 ACs verified locally (4 + 3 + 5 + 3 by AC) plus 6 supporting unit tests + 7 integration tests + 2 shared serde roundtrips. cargo clippy clean on touched crates. Cumulative review for batches 13-15 produced; verdict PASS_WITH_WARNINGS (0 Critical, 0 High, 1 Medium, 4 Low — all carry-overs or deferred-producer notes for AZ-680/AZ-684). Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
ff790bd639 |
[AZ-675] telemetry_stream Tonic gRPC server + per-client lossy queue
ci/woodpecker/push/build-arm Pipeline failed
Pins operator-link transport to gRPC server-streaming (closes architecture Q2 in favour of gRPC). Adds first-time tonic / prost / tonic-build infrastructure to the workspace; uses protoc-bin-vendored so neither dev machines nor CI need system protoc installed. Design — back-pressure lives in the per-topic tokio::sync::broadcast ring, drained directly by the tonic-streamed response via BroadcastStream + StreamMap. No intermediate mpsc buffer that could absorb back-pressure invisibly. Slow client overrun -> Lagged(n) event -> per-(client_id, topic) drop counter incremented; healthy clients on the same topic are unaffected. Service surface — Subscribe(SubscribeRequest) -> stream TelemetryMessage; five topics (TelemetrySample, GimbalState, DetectionEvent, MovementCandidate, MapObjectsBundle); empty topics list defaults to subscribe-all; empty client_id rejected; stream drop decrements subscribed_clients via StreamGuard. TelemetrySink push_detections is now real; push_frame still NotImplemented(AZ-676 video path). Tests — 6 unit + 5 integration (AC-1..AC-3 via in-process gRPC client, plus subscribe-all default + empty-client_id rejection). Clippy on telemetry_stream clean. Pre-existing mission_executor ac3 test polling race surfaces more reliably under the new tonic build pressure; documented as _docs/_process_leftovers/2026-05-20_mission_executor_ac3_flake.md and unchanged by this batch. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
9fe0bbeac9 |
[AZ-683] scan_controller POI queue + 5/min cap + decision window
ci/woodpecker/push/build-arm Pipeline failed
Adds the prioritized POI queue on top of the AZ-682 FSM substrate: priority = confidence x proximity x age_factor; rolling 60s window caps surfaces at 5; confidence-scaled decision window (40% -> 30s, 100% -> 120s, linear; <40% never surfaces); tick() runs the timeout sweep and silently forgets expired POIs (no IgnoredItem per spec); DeclinePoi via operator command returns a DeclineAction for AZ-685 to persist. ScanControllerHandle gains submit_poi_candidate / next_poi_for_surface / decline_poi / poi_queue_len / pois_in_window. submit_operator_cmd return type widens from Result<()> to Result<SubmitOutcome>. ScanMetrics and health() surface queue depth and counters. Tests: 26 unit + 11 integration in scan_controller (all AC1..AC5 + DeclinePoi end-to-end). Workspace clippy on scan_controller clean. Pre-existing autopilot::Runtime::vlm_provider_name dead-code error from batch 4 still open (see cumulative C5). Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
745ab806f1 |
[AZ-657] [AZ-682] frame_ingest RTSP lifecycle + scan_controller FSM (batch 12)
ci/woodpecker/push/build-arm Pipeline failed
AZ-657 (frame_ingest): RTSP session lifecycle FSM with bounded exponential backoff (1 s → 30 s cap), AI-lock plumb through watch::Sender that stamps every emitted Frame, and SPS/PPS hard-fail via OpenError::UnsupportedProfile. The actual RTSP wire client is abstracted behind an RtspTransport trait so AZ-658 can pin retina/FFmpeg alongside the decoder; the lifecycle FSM itself is production code today. tokio::select! around every transport call so a hung open/read cannot wedge graceful shutdown. 10 unit + 5 integration tests cover happy path, bounded reconnect, stream- drop reopen, hard-fail no-retry, and AI-lock toggle. AZ-682 (scan_controller): typed ScanState (ZoomedOut / ZoomedIn / TargetFollow) with a complete pure transition catalogue, every (state, trigger) → next_state from description.md §1/§4/§5 covered; spec-disallowed combos return TransitionOutcome.accepted = false with RejectReason::UnsupportedTransition (loud, not silent). Frame- rate floor monitor with hysteresis suppresses ZoomedOut → ZoomedIn while sustained FPS < 10 fps per description.md §5/§6. Rolling 100-sample tick-latency window surfaces p99; health goes yellow above the 10 ms budget. 18 unit + 5 integration tests cover the catalogue, fps-floor activate/clear, and tick-latency budget. Cumulative review (batches 10-12): all OPEN findings carried forward without regressions. See _docs/03_implementation/batch_12_cycle1_report.md §6. Notes: pre-existing dead-code error in autopilot::Runtime:: vlm_provider_name (origin batch 4) blocks workspace -D warnings clippy. Recorded in _docs/_process_leftovers/ — not in batch 12 scope. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
4c63829ccd |
[AZ-654] [AZ-655] [AZ-656] gimbal_controller primitives + monotonic clock fix (batch 11)
ci/woodpecker/push/build-arm Pipeline failed
AZ-654 SweepEngine: pendulum default, Raster/LawnMower variants reserved and explicitly NotImplemented (no silent fallback per AC-3). Time injected via next_step(now) for deterministic dwell tests. AZ-655 PlanExecutor: linear yaw/pitch interpolation between PanGoals with self-throttle (default 50 ms); stats expose commands_emitted/dropped_to_throttle counters. PanGoal/PanPlan added to shared::models::gimbal (spec drift: data_model.md §PanPlan flagged for next doc sync). AZ-656 CentreOnTarget: zoom-aware proportional control loop (correction ~ 1/zoom); target_lost debounced — fires once per loss streak, resets on bbox return. Also fixes the misleadingly-named monotonic_ns() helper introduced by AZ-653 that used SystemTime::now(): GimbalController now owns a shared::clock::MonoClock and stamps GimbalState::ts_monotonic_ns via clock.elapsed_ns(). AZ-656 AC-2 forced the correction; integration test verifies the fix end-to-end. 58/58 gimbal_controller tests green (47 unit + 7 AZ-653 integration + 4 new batch_11 integration). Workspace test suite green this run. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
288e7f8c46 |
[AZ-653] gimbal_controller ViewPro A40 vendor UDP transport (batch 10)
ci/woodpecker/push/build-arm Pipeline failed
Implements the vendor wire protocol for the A40 gimbal (XOR-8 checksum, not CRC16 — task spec corrected against ArduPilot AP_Mount_Viewpro.h): frame encode/decode, typed FrameId/CameraCommand/ImageSensor, A1 angles, C1 camera, C2 set-zoom command builders, and a tokio UdpSocket transport with bounded retry, per-command deadline, and atomic vendor-fault counters surfaced via faults()/health(). GimbalControllerHandle::set_pose and zoom now ride the transport when wired; remain disabled when no transport is bound. 32/32 gimbal_controller tests green; workspace test suite green except for a pre-existing flake in mission_executor::state_machine::ac3_bounded_retry_then_success that reproduces only under parallel workspace test load (passes 5/5 in isolation; flagged in batch 8 report, unrelated to this batch). Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
358b2fbb53 |
[AZ-652] mission_executor safety + resume + middle-waypoint (batch 9)
Geofence (INCLUSION+EXCLUSION, ≤500 ms detect→RTL), battery thresholds (RTL@25%/land@15% + signed override), middle-waypoint re-upload (CLEAR_ALL→upload→SET_CURRENT(0)), and post-flight mapobjects push trigger. Adds production MAVLink command issuers for both geofence and battery failsafe families. Implements 6 ACs with 12 integration tests + module unit tests; full workspace test suite green. See batch_09_cycle1_report.md for AC coverage and known limitations. Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
8a4bd00526 |
[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>
|
||
|
|
2bcd4a8059 |
[AZ-651] [AZ-668] lost-link failsafe ladder + mapobjects persistence (batch 7)
AZ-651 (mission_executor lost-link ladder):
- LostLinkLadder pure-logic state machine (LinkOk -> Degraded -> Lost
-> LinkLostInFollow + MavlinkLost branch). Configurable thresholds
via LostLinkConfig.
- LostLinkCommandIssuer trait + MavlinkCommandIssuer production impl
emitting MAV_CMD_NAV_RETURN_TO_LAUNCH via MavlinkHandle::send_command.
- LostLinkDriver task wires the ladder to operator-link watch, MAVLink
LinkEvent broadcast, and optional target-follow signal. On RTL,
driver calls the issuer THEN MissionExecutorHandle::failsafe_trigger.
- failsafe_trigger(LinkLost | LinkLostInFollow) short-circuits FlyMission
-> Land via direct FSM state mutation + TransitionEvent emission;
Paused state is intentionally NOT overridden.
- Tests: 4/4 ACs locally green (degraded-no-rtl; lost-fires-once;
follow-grace; mavlink-loss-no-rtl) plus driver + FSM integration.
AZ-668 (mapobjects_store persistence):
- Snapshot serializable shape + Store::{to_snapshot,from_snapshot}
round trip.
- MapObjectsPersistence async trait + JsonSnapshotEngine default impl
(write to .tmp, sync_all, atomic rename, best-effort parent fsync).
- PersistenceError::{Corrupt, SchemaMismatch} surfaces explicit errors
on bad blob; PersistenceMetrics tracks last_snapshot_ts,
snapshot_size_bytes, snapshot_errors_total.
- MapObjectsStore::from_snapshot factory for crash recovery from the
composition root.
- Tests: 4/4 ACs locally green (round-trip; atomic rename ignores
partial .tmp; crash recovery preserves pending; corruption returns
explicit error) plus schema-mismatch + metrics smoke checks.
Quality gates:
- cargo fmt: clean.
- cargo clippy -p mission_executor -p mapobjects_store --tests: 0 warns.
- cargo test --workspace: all green.
Co-authored-by: Cursor <cursoragent@cursor.com>
|
||
|
|
e56d428753 |
[AZ-649] [AZ-674] [AZ-667] telemetry + vlm schema + mapobjects hydrate batch 6
AZ-649 mission_executor telemetry forwarding: - shared::models::telemetry::UavTelemetry canonical model - TelemetryForwarder with atomic ArcSwap snapshot + 3 lossy tokio::sync::broadcast channels (MissionExecutor, ScanController, MavlinkUplink) + per-consumer drop counters - MavlinkProjection::from_mavlink for HEARTBEAT/GLOBAL_POSITION_INT/ ATTITUDE/SYS_STATUS - spawn_mavlink_pump bridges mavlink_layer into the forwarder at the binary edge AZ-674 vlm_client schema validation + model_version tracking: - AssessmentParser owns schema validation + model-version state - wire::read_response_raw splits raw bytes from parsing so invalid payloads can be logged size-capped - VlmStatus gains an Inconclusive variant; exhaustive-match test guards downstream consumers - VlmPipelineStatus mirrors the new variant in shared::models::poi AZ-667 mapobjects_store hydrate + pending logs + cascade: - SyncState enum aligned with description.md (FreshBoot, Synced, CachedFallback, Degraded, Failed) - Store::hydrate(MapObjectsBundle) replaces in-memory map atomically; freshness=Stale -> CachedFallback - classify() + end_of_pass append MapObjectObservation events to pending_observations (New/Moved/Existing/RemovedCandidate) - apply_decline + LocalAppended ignored items append to pending_ignored - drain_pending() returns and clears both logs - cascade_mission(id) purges by_cell + IgnoredSet + pending logs - Health surface reports sync_state, pending_obs, pending_ign Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
b5cc0c321c |
[AZ-666] [AZ-673] [AZ-648] ignored set + UDS VLM + mission FSM batch 5
ci/woodpecker/push/build-arm Pipeline failed
AZ-666 mapobjects_store: - internal/ignored.rs (HashSet<(mgrs, class_group)> for O(1) suppression) - internal/passes.rs (per-region PassTracker with observed-id set and end-of-pass removed-candidate sweep) - Classification::Ignored wired into classify; apply_decline + is_ignored + pass_start + end_of_pass on MapObjectsStoreHandle - new tests/ignored_and_sweep.rs (3 AC + 2 supplementary) AZ-673 vlm_client: - internal/peer_cred.rs (Linux SO_PEERCRED via libc getsockopt; PeerCredOutcome::SkippedNonLinux on macOS dev hosts per description.md §8) - internal/prompt.rs (pre-send ROI size + format + prompt non-emptiness validation) - internal/wire.rs (length-prefixed JSON envelope with base64 ROI) - internal/uds_client.rs (tokio UnixStream client; bounded reconnect; hard-stop on peer-cred mismatch; per-request deadline) - VlmClient with both eager (open/connect) and lazy (new) ctor - workspace Cargo.toml: base64 + libc as workspace deps AZ-648 mission_executor: - internal/types.rs (Variant, MissionState, TransitionKey, Telemetry, TransitionEvent, StepOutcome) - internal/driver.rs (MissionDriver trait + DriverError + DriverAction) - internal/fsm.rs (variant-agnostic Transition + FsmCore + step_one with per-transition retry budget keyed by TransitionKey) - internal/multirotor.rs + internal/fixed_wing.rs (typed transition tables; multirotor has Armed/TakeOff, fixed-wing parks in WaitAuto for operator AUTO) - public API: MissionExecutor::run spawns the FSM task and returns a clone-safe MissionExecutorHandle (state, health, subscribe, paused_reason, retry_count) - new tests/state_machine.rs (AC-1..AC-4 via ScriptedDriver fake; SITL conformance lands with AZ-649 telemetry forwarding) Workspace: cargo fmt + clippy -D warnings clean; full cargo test --workspace --all-features green (1 ignored = AZ-665 perf gate). Tasks moved todo/ → done/, autodev state set to batch 6 selection. Refs: _docs/03_implementation/batch_05_cycle1_report.md Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
69c0629350 |
[AZ-643] [AZ-665] [AZ-672] mavlink+mapobjects+vlm batch 4
ci/woodpecker/push/build-arm Pipeline failed
AZ-643 mavlink_layer:
- ack demux on COMMAND_LONG/COMMAND_ACK with oneshot dispatch and
configurable deadline; MavlinkHandle::send_command + SendCommandError
- MAVLink-2 signing: Signer/Verifier built on SHA-256, key + timestamp
source, incompat-flag wiring in encoder, reject + counter in decoder
- new tests: tests/ack_demux.rs (3) + tests/signing.rs (5)
AZ-665 mapobjects_store:
- internal/h3_index.rs (h3o wrapper, cell_of, grid_disk, haversine)
- internal/store.rs (in-memory (cell -> Vec<MapObject>) hashmap with
k-ring classify and class-group resolution)
- public API: MapObjectsStoreHandle::classify(ClassifyInput) ->
Classification {New|Moved|Existing}
- AC1-4 in tests/classify.rs; AC5 perf gate (#[ignore], passes in
--release)
AZ-672 vlm_client + autopilot:
- DisabledVlmProvider in shared::contracts; VlmProvider::name() for
composition-root diagnostics
- vlm_client::VlmClient gated behind feature = "vlm"; placeholder
until AZ-673 lands the real NanoLLM IPC
- autopilot: vlm_client is now optional = true under feature vlm;
Runtime::select_vlm_provider picks DisabledVlmProvider when feature
off OR config.vlm.enabled = false
Workspace deps: +sha2 (mavlink signing), +h3o (mapobjects index).
Batch report: _docs/03_implementation/batch_04_cycle1_report.md
Co-authored-by: Cursor <cursoragent@cursor.com>
|
||
|
|
0a87c0f716 |
[AZ-645] [AZ-646] [AZ-647] mission_client: middle-waypoint POST + mapobjects pull/push
ci/woodpecker/push/build-arm Pipeline failed
Batch 3 of greenfield Step 7 — mission_client epic AZ-638 close-out.
AZ-645 (Middle-waypoint POST)
- post_middle_waypoint(mission_id, &Mission) -> Result<MissionUpdateAck, PostError>
- Bounded retry (default 3 attempts) shared with the rest of missions_api
- Health: last_middle_waypoint_post_status (ok/error)
AZ-646 (Pre-flight MapObjects pull)
- pull_mapobjects(mission_id) -> Result<MapObjectsBundle, PullError>
- Schema-validated against bundled shared/contracts/mapobjects-bundle.json
- Typed errors: Unreachable / SchemaInvalid / MaxRetriesExceeded / Internal
- Health: mapobjects_pull_state, last_mapobjects_pull_ts
AZ-647 (Post-flight push + durable disk queue)
- push_mapobjects_diff(mission_id, MapObjectsDiff) -> PushReport
- recover_pending_pushes() -> Vec<PushReport> for crash recovery
- Write-ahead atomic-rename persistence under ${state_dir}/mapobjects_push/
- Per-endpoint independent retry: observations + ignored_items
- Partial success rewrites the disk file with only the failing portion
- Health: mapobjects_push_pending, last_push_ts, per-endpoint last error
Infrastructure
- Schemas: shared/contracts/mapobjects-{bundle,observations,ignored}.json
- Restructured schema/ into mission.rs + mapobjects.rs sub-modules
- New mapobjects_sync/ (pull, push, queue)
- workspace dep tempfile=3; mission_client dev-deps add tempfile + chrono
Tests
- 12/12 ACs verified locally (4 AZ-645 + 4 AZ-646 + 5 AZ-647)
- mission_client suite: 15 unit + 18 integration = 33 tests pass
- AZ-646 AC-4 proxy: 1000-object + 1000-ignored bundle within 30s
- AZ-647 AC-5 proxy: 5000-obs + 500-ignored push within 2min
Code review verdict: PASS_WITH_WARNINGS (inline). Cumulative review
(K=3 trigger) PASS_WITH_WARNINGS — full report in
_docs/03_implementation/cumulative_review_batches_01-03_cycle1_report.md.
Open follow-ups (non-blocking):
- module-layout.md: rename push_mapobjects -> push_mapobjects_diff (Step 13)
- ExponentialBackoff still duplicated across crates; promote to shared::retry
when the third caller lands (likely detection_client AZ-660/661)
- state_dir default is relative; composition root must override
Co-authored-by: Cursor <cursoragent@cursor.com>
|
||
|
|
740bf37d76 |
[AZ-641] [AZ-642] [AZ-644] mavlink transport + codec + mission pull
Lands the second batch under epic AZ-626's implementation plan.
mavlink_layer (AZ-641 + AZ-642):
- Hand-rolled MAVLink v2 codec covering the §7.7 surface: HEARTBEAT,
SYS_STATUS, SET_MODE, ATTITUDE, GLOBAL_POSITION_INT, MISSION_* (7),
COMMAND_LONG, COMMAND_ACK, EXTENDED_SYS_STATE, STATUSTEXT (17 total).
- Streaming decoder demuxes arbitrary-sized byte arrivals, drops malformed
frames with typed parse-error counters (crc/truncated/unknown_id/seq_gap),
and surfaces sequence gaps without hard-failing the link.
- Encoder tracks the per-link tx_seq counter and applies the MAVLink v2
trailing-zero payload truncation rule.
- UDP and POSIX-serial transports behind a single async Transport trait;
the run loop owns transport open with bounded exponential backoff
(2 s serial / 5 s UDP cap) and a tokio::select! per-link read+write
loop.
- 1 Hz outbound HEARTBEAT scheduler + inbound-heartbeat watchdog that
fires LinkUp / LinkLost on a broadcast channel and feeds health detail
(connected, last_heartbeat_age_ms, signing_enabled, parse_errors).
mission_client (AZ-644):
- HTTPS GET /missions/{id} over rustls (no OpenSSL on the airframe).
- Bundled JSON Schema (crates/shared/contracts/mission-schema.json,
draft-07, additionalProperties:false) validates every response;
schema-invalid bodies surface as FetchError::SchemaInvalid with a
1 KiB sample of the raw body for offline analysis.
- Transient failures (timeout, 5xx, 429) retry with bounded exponential
backoff up to MissionClientOptions.max_attempts (default 5); permanent
failures (4xx, malformed URL) abort immediately.
- Health surface mirrors AC-1's contract: last_fetch_ts,
fetch_errors_total, schema_version, connection_state.
Caught and fixed before commit (NOT a code-review finding — caught by
the unit test that hand-computed CRC("123456789")): the hand-rolled
X.25 CRC accumulator was operating in u16 throughout. The MAVLink C
reference declares `tmp` as uint8_t, which silently truncates the
shifted-in bits. Round-trip tests passed (encoder and decoder shared
the bug); a real MAVLink peer would have rejected every frame. Fixed
by mirroring the C reference: `let mut tmp: u8 = …; tmp ^= tmp.wrapping_shl(4);`.
Added a regression test asserting CRC("123456789") == 0x6F91 against
pymavlink's reference value (NOT the textbook 0x29B1 — MAVLink uses a
byte-wise variant, not the bit-reflected CCITT).
AC verification (full detail in
_docs/03_implementation/batch_02_cycle1_report.md):
AZ-641: AC-1 + AC-3 + AC-4 verified via UDP loopback integration tests;
AC-2 (serial) requires a socat pty pair and runs in the SITL/CI
tier (test exists as #[ignore]-marked stub).
AZ-642: AC-1 + AC-2 + AC-3 verified via exhaustive codec round-trip and
decoder negative-path tests; AC-4 (SITL round-trip) requires
ArduPilot SITL — the CRC fix above means the codec is now
wire-correct, ready for the sitl-conformance Woodpecker stage.
AZ-644: all four ACs verified via wiremock-driven integration tests.
Workspace gates green:
- cargo check --workspace clean
- cargo check --workspace --no-default-features clean
- cargo fmt --all -- --check clean
- cargo clippy --workspace --all-targets -- -D warnings clean
- cargo test --workspace pass (1 expected ignore)
Layering invariants from module-layout.md hold: mavlink_layer and
mission_client are Layer 2 actors importing only `shared`; no sibling
Layer-2 imports; MavlinkHandle implements shared::contracts::MavlinkSink.
Jira: AZ-641, AZ-642, AZ-644 transitioned To Do → In Progress at batch
start; the matching In Testing transitions follow this commit.
Co-authored-by: Cursor <cursoragent@cursor.com>
|
||
|
|
a1ce3a6903 |
[AZ-640] Bootstrap Rust workspace, CI/Docker, observability scaffold
ci/woodpecker/push/build-arm Pipeline failed
Lands the first task of the implementation epic AZ-626: a cargo workspace
with 14 crates (shared + autopilot binary + 12 component crates), a
multi-stage Dockerfile + dev/test compose stacks, a Woodpecker CI pipeline,
the on-airframe systemd unit with flight-gate wiring, three environment
TOML configs, and the canonical entity catalogue from data_model.md as
`shared::models`.
Per-AC verification (full detail in
_docs/03_implementation/batch_01_cycle1_report.md):
- AC-1 cargo check --workspace clean
- AC-2 cargo test --workspace passes; per-crate it_compiles() <0.01 s
- AC-6 cargo build/test --no-default-features clean; VlmClient default
impl returns VlmAssessment::disabled()
- AC-9 tracing-subscriber emits JSON logs with ts/level/target/fields
- AC-10 runtime::ensure_state_directories creates mapobjects/, audit/,
pending_pushes/ under storage.state_dir
Deferred to external infra (artifacts written, verification re-runs in CI
and in downstream tasks):
- AC-3 Woodpecker runner; CI yml in place
- AC-4 docker-compose mocks land with AZ-660/AZ-644/AZ-675
- AC-5 SITL conformance lands with AZ-641/AZ-648/AZ-652
- AC-7 aarch64 cross-compile via cargo-zigbuild stage
- AC-8 systemd unit (Linux + systemd host)
Layering invariants from module-layout.md hold: shared (L1) imports
nothing; Layer 2 actor crates import only shared; Layer 3 coordinators
(operator_bridge, mission_executor) import only their documented Layer 2
deps; Layer 4 (scan_controller) imports its documented Layer 2 + Layer 3
deps; the autopilot binary (L5) is the only consumer of every component.
cargo fmt --all --check + cargo clippy --all-targets -- -D warnings both
clean. Jira AZ-640 transitioned to In Progress at the start of this batch;
the matching In Testing transition follows this commit.
Co-authored-by: Cursor <cursoragent@cursor.com>
|
||
|
|
bc40ea7300 |
[AZ-626] Decompose complete: 47 tasks + docs + module layout
Greenfield Steps 1-6 baseline for the autopilot rewrite from legacy Qt/C++ to a Rust workspace. - Remove legacy Qt/C++ tree (ai_controller, drone_controller, misc/camera, python_scaffold, root Dockerfile, autopilot.pro, legacy main.py / requirements.txt). - Add _docs/00_problem (problem, restrictions, acceptance criteria, security approach, input data + fixtures). - Add _docs/01_solution/solution_draft01. - Add _docs/02_document (architecture, system-flows, data_model, glossary, decision-rationale, deployment, 13 component descriptions, tests/ specs, FINAL_report, module-layout). - Add _docs/02_tasks/todo with 47 task specs (AZ-640..AZ-686, one bootstrap + 46 component tasks) and _dependencies_table.md. - Add .cursor/rules/artifact-srp.mdc (single-responsibility rule for canonical _docs artifacts). - Track autodev state in _docs/_autodev_state.md (Step 6 completed, ready for Step 7 Implement). Jira: bootstrap AZ-626; component epics AZ-627..AZ-639; tasks AZ-640..AZ-686. Total complexity 173 points across 12 epics. Co-authored-by: Cursor <cursoragent@cursor.com> |