# BIT-DEGRADED Acknowledgement + Safety-Override Command Path

**Task**: AZ-681_operator_bridge_safety_and_bit_ack
**Name**: Forward signed BIT-degraded ack + safety-override commands to mission_executor
**Description**: Forward signed BIT-degraded acknowledgement commands (F9) and signed safety-override commands (F10: lost-link / battery suppression) to `mission_executor`. Severity check: operator MAY ack a DEGRADED but MUST NEVER ack a FAIL.
**Complexity**: 3 points
**Dependencies**: AZ-640_initial_structure, AZ-678_operator_bridge_command_auth, AZ-650_mission_executor_bit_f9, AZ-652_mission_executor_safety_and_resume
**Component**: operator_bridge
**Tracker**: AZ-681
**Epic**: AZ-628

## Problem

Two operator-command kinds bypass `scan_controller` and go straight to `mission_executor`: BIT-DEGRADED acknowledgement (gating takeoff after a non-FAIL BIT result), and safety-override (suppressing a lost-link RTL or battery-RTL trigger, scoped + bounded by `architecture.md` F10). Both MUST be signed (already enforced by task 39). The BIT severity check must reject any attempt to ack a FAIL.

## Outcome

- `SafetyCommandDispatcher::dispatch(validated_cmd) -> CommandAck` routes:
  - `BitDegradedAck { bit_report_id }` → `mission_executor::on_bit_degraded_ack(bit_report_id)`; rejected if report severity = FAIL.
  - `SafetyOverride { scope, duration }` → `mission_executor::on_safety_override(scope, duration)`.
- Severity check on BIT ack: looks up the originating `BitReport`; if `severity = Fail` returns `Ack::Error { reason: cannot_acknowledge_fail }` without dispatching.
- Idempotency cache (shared with task 41).
- Health: `safety_overrides_active`, `bit_ack_rejections_total{reason}`.

## Scope

### Included
- Dispatch switch for `BitDegradedAck` and `SafetyOverride`.
- Severity check against BIT report cache.
- Audit log entry for every safety command (signed, redacted payload).

### Excluded
- Auth (task 39).
- POI commands (task 41).
- The BIT machinery itself (lives in `mission_executor` task 11/AZ-650).
- The safety-override enforcement / scope-clamping (lives in `mission_executor` task 13/AZ-652).

## Acceptance Criteria

**AC-1: BIT-DEGRADED ack succeeds**
Given a validated `BitDegradedAck { bit_report_id: X }` where the BIT report severity = `Degraded`
When `dispatch(cmd)` runs
Then `mission_executor::on_bit_degraded_ack(X)` invoked exactly once; ack `Ok` returned.

**AC-2: BIT-FAIL ack rejected**
Given a validated `BitDegradedAck { bit_report_id: X }` where the BIT report severity = `Fail`
When `dispatch(cmd)` runs
Then `Ack::Error { reason: cannot_acknowledge_fail }` returned; `mission_executor::on_bit_degraded_ack` is NOT invoked; `bit_ack_rejections_total{reason: cannot_acknowledge_fail}` increments.

**AC-3: Safety-override forwards with scope + duration**
Given a validated `SafetyOverride { scope: BatteryRtl, duration_secs: 60 }`
When `dispatch(cmd)` runs
Then `mission_executor::on_safety_override(BatteryRtl, 60)` invoked exactly once; ack `Ok` returned; an audit log entry exists (operator id, scope, duration, ts).

**AC-4: Audit log redacts secrets**
Given any dispatched safety command
When the audit log entry is written
Then the entry contains the command id, timestamp, operator id, scope/duration — but NEVER the raw signature bytes or session token.

## Non-Functional Requirements

**Performance**
- Dispatch overhead: ≤2 ms p99.

**Security**
- Severity check is mandatory; no path bypasses it.
- Audit log entry is the only persistent trace of safety commands; redaction is mandatory.

## Contract

- Canonical typed model: `data_model.md §OperatorCommand`, `§BitReport`.

## Runtime Completeness

- **Named capability**: BIT-degraded ack + safety-override dispatch with severity gate + audit log.
- **Production code that must exist**: real severity check; real audit log writer (file or structured logger).
- **Unacceptable substitutes**: accepting a FAIL ack "because the signature was valid" defeats the whole F9 gate and is unacceptable.
