# Component — `mapobjects_store`

**Layer**: Decision + Memory
**Status**: forward-looking design (Rust); on-device working copy of the central MapObjects state, mission-bracketed

## 1. Purpose

On-device, H3-indexed working copy of the centrally maintained MapObjects state plus the IgnoredItems list, scoped to the active mission's bounding box. Computes new / moved / existing / removed diffs across survey passes and is the source of truth for the operator-decline suppression rule **for the duration of the active mission**.

This is **not** a private database. It is hydrated pre-flight from the central `missions` API (`/missions/{id}/mapobjects`) and the mission's full pass diff is pushed back post-flight. The central observation log + computed current view are authoritative across missions and across UAVs (`architecture.md §7.13`).

## 2. Inputs

| Input | Source | Cadence | Notes |
|---|---|---|---|
| Pre-flight pull payload | `mission_client` (from `missions` API) | once per mission | Hydrates `current_state` + `pending_ignored`. |
| New detection / movement candidate (with MGRS + class + size) | `scan_controller` | per detection | Each is classified as new / moved / existing. |
| `IgnoredItem` append | `scan_controller` (on operator decline) | event | `(MGRS, class_group)` plus operator metadata. |
| End-of-pass marker | `scan_controller` / `mission_executor` | event per pass over a region | Triggers the removed-candidate sweep. |
| Mission delete cascade | suite-level missions API hook (process-level config; not a network call) | event | Drops mission-scoped objects on mission deletion. |
| Post-flight push trigger | `mission_executor` | once per mission, on terminal state | Causes `mission_client` to drain `pending_observations` + `pending_ignored` to the central API. |

## 3. Outputs

| Output | Consumer | Shape |
|---|---|---|
| `MapObjectClassification` | `scan_controller` | `new \| moved \| existing \| removed_candidate` per detection |
| `IgnoredItem` match | `scan_controller` | suppression flag for (MGRS, class_group) |
| Pass diff | `mission_client` (post-flight upload) + `operator_bridge` (optionally surfaced in-flight) | new / moved / removed lists per pass |
| Sync state | `scan_controller`, health aggregator | `synced \| cached_fallback \| degraded`; `pending_observations_count`, `pending_ignored_count` |

## 4. Key Responsibilities

- **Pre-flight hydrate** from `mission_client` pull. Establish `current_state` and `pending_ignored`. Surface `sync_state` (`synced` or `cached_fallback` or `degraded`).
- Compute H3 cell for each detection at the configured resolution (default res 10, ~15 m edge).
- Build the composite key `H3_cell + class`. Maintain an in-memory hashmap; persist asynchronously to disk for crash recovery.
- Answer queries: `classify(detection) → new | moved | existing` using k-ring lookup and `(distance_threshold_m, move_threshold_m, similar_classes)` configuration.
- After a region's scan-pass ends, return objects in the region that were not re-observed as `removed_candidate`s (the operator decides on actual removal).
- Maintain the `IgnoredItem` set; answer suppression queries (`is_ignored(MGRS, class_group)`).
- Append every NEW / MOVED / EXISTING / REMOVED-CANDIDATE / IgnoredItem event to `pending_observations` / `pending_ignored` for the post-flight push (in-flight central writes are forbidden — Frozen choice 6 in `architecture.md §7.3`).
- **Post-flight push**: hand the contents of `pending_observations` + `pending_ignored` to `mission_client` for `POST /missions/{id}/mapobjects` and `POST /missions/{id}/mapobjects/ignored`. On ack, clear pending; on failure, persist for retry.
- On `DELETE /missions/{id}` cascade signal (received via `mission_client`), drop all objects scoped to that mission. The central side cascades as well.

## 5. Sync state machine

```text
fresh_boot
   │
   ├──> pre-flight pull
   │       │
   │       ├── 200 OK ────────────> synced
   │       ├── unreachable ────────> [operator ack required]
   │       │                           │
   │       │                           ├── ack on cache ──> cached_fallback
   │       │                           └── abort ─────────> BIT fail
   │       └── 4xx ─────────────────> BIT fail
   │
   ├── (during flight; in-process writes only)
   │       │
   │       ├── pending_observations grow
   │       └── pending_ignored grow
   │
   └── post-flight push
           │
           ├── 200 OK on both endpoints ──> synced (pending cleared)
           ├── partial ────────────────────> retry per-endpoint
           └── persistent failure ─────────> degraded (operator warning, manual replay)
```

## 6. Internal State

- In-memory hashmap of `(H3_cell + class) → MapObject`.
- `IgnoredItem` set keyed by `(MGRS, class_group)`.
- Per-region pass tracker for removed-candidate detection.
- `pending_observations`: ordered log of NEW / MOVED / REMOVED-CANDIDATE / EXISTING events not yet pushed centrally.
- `pending_ignored`: ordered log of IgnoredItem appends not yet pushed centrally.
- `sync_state`: enum + last-pull timestamp + last-push timestamp + last error.
- Persistence layer (engine TBD — see Open Questions) for crash recovery and post-flight upload durability.

## 7. Failure Modes

| Failure | Detection | Behaviour |
|---|---|---|
| Pre-flight pull unreachable | network | Surface BIT degradation; operator must acknowledge cached fallback or abort. Never silent. |
| Pre-flight pull stale beyond freshness window | last-fetch-at compared to configured staleness | `sync_state = degraded`; operator must acknowledge or abort. |
| Persistence write failure | engine error | Log + retry; in-memory state continues authoritative for this mission; health → yellow. |
| Persistence corruption on startup | checksum / open failure | Refuse to start with stale state; require explicit recovery (engine-specific); surface to operator at startup. |
| H3 query inconsistency near cell boundaries | algorithmic | Always query the k-ring (k=2 default) so boundary objects are matched anyway. |
| Mission cascade signal lost | absent signal | `DELETE /missions/{id}` is the only cleanup trigger; on lost signal, mission-scoped objects accumulate. Operator-driven manual purge is acceptable. |
| Post-flight push partial success | per-endpoint status | Independent retry per endpoint; do not roll back the successful one. |
| Post-flight push persistent failure | bounded retries exhausted | `sync_state = degraded`; pending diff persisted on disk; operator-visible warning; manual replay supported. Mission's central data integrity at risk until replayed. |
| In-flight crash | startup detects non-empty `pending_*` for a terminated mission | `mission_client` runs the post-flight push at startup before BIT completes for any new mission. |

## 8. Dependencies

**In-process**: `scan_controller`, `mission_client` (for pull/push round-trips), `mission_executor` (for post-flight trigger).

**External**: H3 spatial-index library (Rust crate). Persistent store engine — TBD (SQLite + H3 extension / KV / in-memory + snapshot — see Open Questions). Central API contract via `mission_client`'s extension of the `missions` API (per `architecture.md §7.13`).

## 9. Non-Functional Targets

| Concern | Target |
|---|---|
| Per-detection classify latency | O(1); p99 ≤1 ms |
| Pre-flight pull time | ≤30 s for a 30 km × 30 km mission area (per `architecture.md §6 NFR`) |
| Post-flight push time | ≤2 min for a 60 min mission's pass diff (per `architecture.md §6 NFR`) |
| Persistent-store size (single mission) | bounded; configurable retention |
| Crash recovery time | ≤2 s to a usable state; in-flight crash → next-boot push of pending |
| Boundary correctness | guaranteed by k-ring query |

## 10. Open Questions

- **Engine choice** (architecture.md §8 Q3): SQLite + H3 extension / KV / in-memory + snapshot.
- **Central API schema details** (architecture.md §8 Q7): paging strategy, photo-reference upload mechanism, observation-history retention policy.
- **Conflict resolution rules** (architecture.md §8 Q8): exact projection from observation log to current view; REMOVED-claim expiry window; multi-class disambiguation.
- Optimal H3 resolution per terrain class.
- Class-group definitions (`military_vehicle_group` vs `concealed_position_group` vs `movement_candidate`) — currently in `scan_controller` config.

## 11. References

- `architecture.md §3`, `§5 Architectural Principles` (MapObjects are mission-bracketed and centrally synchronised), `§6 NFR`, `§7.9 MapObjects (H3 spatial index)`, `§7.10 Sync Message Format`, `§7.11 Target Relocation`, `§7.12 New vs Existing object detection`, `§7.13 MapObjects Sync`.
- `system-flows.md §F7 MapObjects + ignored-items` (in-flight diff), `§F8 MapObjects sync (central DB, mission-bracketing)`.
- `data_model.md §MapObject`, `§IgnoredItem`, `§MapObjectObservation`, `§MapObjectsBundle`.
- `../_docs/02_missions.md` (mission cascade contract; new MapObjects endpoints).
