# MAVLink Ack Demux, Retry, and Signing

**Task**: AZ-643_mavlink_ack_demux_and_signing
**Name**: Command-ack demux + retry handle + optional MAVLink-2 signing
**Description**: Map outbound `COMMAND_LONG` requests to their `COMMAND_ACK` responses by `command_id`, enforce ack timeout, surface result to the originating caller; optionally enable MAVLink-2 message signing.
**Complexity**: 3 points
**Dependencies**: AZ-640_initial_structure, AZ-641_mavlink_transport_and_heartbeat, AZ-642_mavlink_codec
**Component**: mavlink_layer
**Tracker**: AZ-643
**Epic**: AZ-637

## Problem

Outbound MAVLink commands are async with respect to their acks. `mission_executor` (and other callers) need a synchronous-feeling `send_command(...) -> Result<CommandAck>` API that times out at a configurable wall-clock deadline (default 1 s) — the retry decision then belongs to the caller, not to `mavlink_layer`. Separately, when the autopilot link supports it, MAVLink-2 message signing should be enabled for outbound frames and validated for inbound frames; mismatched signatures are rejected.

## Outcome

- `MavlinkHandle::send_command(cmd) -> Result<CommandAck, AckTimeout>` resolves when a matching `COMMAND_ACK` arrives within the deadline, or returns `AckTimeout` otherwise.
- An in-flight command map (`command_id → (caller, deadline)`) is correctly populated and cleared on success and on timeout (no leaks).
- When `signing_enabled = true` at config time, outbound frames are signed; inbound frames with bad signatures are rejected and counted (`parse_errors_total{kind="signing_mismatch"}`).
- `signing_enabled` is reported in `health()`.

## Scope

### Included
- In-flight command map with deadline-driven eviction.
- Public `send_command(...) -> Result<CommandAck>` API.
- MAVLink-2 outbound signature + inbound signature validation (off-by-default; on when configured).
- Health fields: `commands_in_flight`, `signing_enabled`.

### Excluded
- The decision to retry on `AckTimeout` (belongs to `mission_executor`).
- Encoding the new commands themselves (task 03).

## Acceptance Criteria

**AC-1: Command-ack happy path**
Given a healthy SITL link
When `send_command(MAV_CMD_NAV_RETURN_TO_LAUNCH)` is called
Then within ≤1 s the result resolves with `MAV_RESULT_ACCEPTED` and `commands_in_flight` returns to 0.

**AC-2: Ack timeout returns explicit error**
Given a SITL instance that is configured not to ack commands (or is paused)
When `send_command(...)` is called with the default 1 s deadline
Then the call resolves with `Err(AckTimeout)`; the in-flight map is cleared; the link stays open.

**AC-3: Signing rejection counted**
Given `signing_enabled = true` and an inbound frame whose signature does not match
When the decoder runs on the frame
Then the frame is rejected, `parse_errors_total{kind="signing_mismatch"}` increments by 1, and the link stays open.

**AC-4: Optional signing — disabled path**
Given `signing_enabled = false`
When inbound frames arrive (signed or unsigned)
Then the signature field is ignored and `parse_errors_total{kind="signing_mismatch"}` stays at 0.

## Non-Functional Requirements

**Performance**
- Ack demux lookup: O(1); does not contribute measurably to the ≤50 ms per-message round-trip target.

**Reliability**
- No leaked entries in the in-flight map; every `send_command` either resolves or times out.

## Constraints

- Signing scheme decision (Q6) lives elsewhere — this task only wires the on/off mechanism using the spec-defined MAVLink-2 signing.

## Runtime Completeness

- **Named capability**: MAVLink-2 message signing (when enabled) + COMMAND_ACK demux.
- **Production code that must exist**: real signature computation + verification; in-flight map keyed by `command_id`.
- **Allowed external stubs**: SITL with signing disabled is the default test fixture; a separate fixture exercises the signing path.
- **Unacceptable substitutes**: signature stub that always returns "valid" is not acceptable in production.
