# 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).