# MapObjects Dispatch + IgnoredItem Suppression + Degraded-Sync Behaviour

**Task**: AZ-685_scan_controller_mapobjects_dispatch
**Name**: Dispatch new/moved/existing/removed-candidate to mapobjects_store + IgnoredItem suppression + degraded-sync POI policy
**Description**: For each new detection or movement candidate: compute H3 cell, ask `mapobjects_store` to classify, only surface non-existing entries. Suppress new POIs whose `(MGRS, class_group)` matches an existing `IgnoredItem`. When `mapobjects_store::sync_state = degraded`, suppress diff classifications (don't corrupt the central log) but continue to surface POIs on Tier-1 + movement evidence alone.
**Complexity**: 3 points
**Dependencies**: AZ-640_initial_structure, AZ-682_scan_controller_state_machine, AZ-684_scan_controller_evidence_ladder, AZ-665_mapobjects_store_h3_classify, AZ-666_mapobjects_store_ignored_and_pass_sweep, AZ-667_mapobjects_store_hydrate_and_pending
**Component**: scan_controller
**Tracker**: AZ-685
**Epic**: AZ-635

## Problem

The classification step decides whether a candidate becomes a POI or is silently absorbed as an existing MapObject. The IgnoredItem suppression keeps the operator from re-seeing rejections from earlier in the mission (or earlier missions hydrated from the central store). Degraded sync is the special case: the local store is out of sync with the canonical central store, so classification could corrupt the central log on next push — in that case suppress classifications but keep surfacing POIs to the operator on direct evidence.

## Outcome

- On every new candidate (Tier-1 detection OR movement candidate): emit `MapObjectsClassifyRequest { detection }`; consume the returned classification (`new | moved | existing | ignored`).
- Surface only `new | moved` to the POI queue; `existing` silently absorbs into the existing MapObject (bump observation_count via mapobjects_store); `ignored` is suppressed.
- On operator decline: emit `MapObjectsAction::AppendIgnored(mgrs, class_group)`.
- On `mapobjects_store::sync_state = degraded`: skip the classify step entirely; surface POIs on Tier-1 + movement evidence alone; suppress `AppendIgnored` writes (they'd dirty the central log on next push); set health → red.
- On `sync_state` recovery → resume classify dispatch.

## Scope

### Included
- Classify request / response routing.
- Ignored suppression at surface time.
- Decline → AppendIgnored emission.
- Degraded-sync POI policy (Tier-1 + movement only).

### Excluded
- The store itself (tasks 26-28).
- POI queue (task 44).
- Evidence ladder (task 45).

## Acceptance Criteria

**AC-1: Existing classification absorbs silently**
Given a detection that `mapobjects_store` classifies as `existing`
When dispatched
Then no POI is surfaced; the existing MapObject's observation_count is bumped (via mapobjects_store).

**AC-2: New classification surfaces**
Given a detection that classifies as `new`
When dispatched
Then a POI is enqueued (subject to queue + cap rules).

**AC-3: Ignored suppression at surface**
Given a candidate whose `(MGRS, class_group)` is in the IgnoredItem set
When dispatched
Then the classify step returns `ignored`; no POI is surfaced.

**AC-4: Decline appends IgnoredItem**
Given an operator decline command (from `operator_bridge` task 41) for POI X
When processed
Then `MapObjectsAction::AppendIgnored(X.mgrs, X.class_group)` is emitted exactly once.

**AC-5: Degraded sync suppresses classify**
Given `mapobjects_store::sync_state = degraded`
When new detections arrive
Then no classify requests are dispatched; POIs are surfaced on Tier-1 + movement evidence alone; health → red.

**AC-6: Sync recovery resumes classify**
Given sync_state transitions degraded → synced
When new detections arrive
Then classify dispatch resumes; health returns from red.

## Non-Functional Requirements

**Performance**
- Classify dispatch latency: ≤2 ms p99.

## Contract

- Canonical typed models: `data_model.md §MapObject`, `§IgnoredItem`, `§POI`.

## Runtime Completeness

- **Named capability**: classify-then-dispatch + ignored suppression + degraded-sync policy.
- **Production code that must exist**: real classify routing; real ignored suppression; real degraded-sync gate.
- **Unacceptable substitutes**: continuing to dispatch classify under degraded sync (corrupts central log) is unacceptable.
