# Oya Build Checklist — End-to-End Software Task Breakdown

> **Source of truth for scope**:
> `docs/proposals/OYA_RUST_REWRITE_AND_COMPANION_PLAN.md` (Part I = Rust
> rewrite + companion product; Part II = home-robotics hive). This file
> decomposes that plan into granular, actionable software tasks.
>
> **Status (corrected 2026-09-18)**: about half of the boxes are checked — 21
> crates under `libs/oya/engine/crates` and 15 services under `apps/oya` exist.
> (This line said "every box is `[ ]` — nothing here is built yet" until then;
> that was the initial state.) `./eve next oya-build` lists what is open. The
> checkbox is the sole source of truth (per repo `CLAUDE.md`); do not infer
> completion from prose. Process tasks **one at a time**: read the code, verify
> domain-correct implementation (no stubs), run the specific tests, then mark
> `[x]`. Never batch-mark.
>
> **Scope**: all _software_ in this repo — Rust crates, TS libs/services, sim,
> CI, contracts, infra wiring. **Out of scope**: physical fabrication of
> airframes, arms, perch/charging electronics, and dock mechanisms (a parallel
> hardware workstream). Firmware-facing protocols, SITL/HITL sim, and edge
> runtimes **are** in scope.
>
> **Phase tags** `(A)`–`(I)` map to the unified roadmap (plan §14). Conventions
> per crate/service: every Rust crate ships `Cargo.toml`, comprehensive types,
> domain-correctness tests, `clippy -D warnings`, and a napi/wasm bridge where a
> TS tier needs it; every TS lib/service ships `project.json` + `package.json` +
> `tsconfig.json`, path mapping in `tsconfig.base.json`, Vitest tests, and an
> `nx:run-commands` build (matching lilith/yemaya).

---

## 0. Program-level foundations

### 0.1 Repo & tooling setup

- [x] (A) Create `libs/oya/engine/` Cargo workspace root (`Cargo.toml`,
      `rust-toolchain.toml` pinned to the neith/maya toolchain) — per-domain
      workspace, no root workspace (matches `libs/maya/engine-core`).
- [x] (A) Add `libs/oya/engine/package.json` exposing
      `cargo:build|test|clippy|fmt|check` scripts (matches `@neith/core`).
- [x] (A) Add `libs/oya/engine/project.json` with `nx:run-commands` targets
      `build|test|lint|fmt` → `cargo … --manifest-path`.
- [x] (A) Add `.github/workflows/oya-rust-premerge.yml` (pattern:
      `v2-rust-premerge.yml`) — `cargo test`, `clippy -D warnings`,
      `fmt --check`, sccache, build-time baseline; path filter
      `libs/oya/engine/**`, `libs/oya-edge-runtime/**`. _(fmt-check +
      clippy-deny-warnings + cargo test + the differential parity oracle, on PRs
      touching `libs/oya/engine/**` or `libs/oya/core/**`; engine is now
      clippy-clean + rustfmt-formatted. sccache/build-time-baseline and the
      `oya-edge-runtime` path deferred until that crate exists.)_
- [x] (A) Register all new TS projects' path mappings in `tsconfig.base.json`.
      _(wildcard `@oya/*` → `libs/oya/*/src/index.ts`
      (`tsconfig.base.json:1771`, mirroring the proven `@lilith/*` pattern at
      :1263) registers all 17 TS libs; verified every one of the 10 distinct
      `@oya/*` import specifiers used across `apps/`+`libs/` resolves to an
      existing `src/index.ts`. The 3 crates without `src/index.ts`
      (engine/node-bridge/wasm-bridge) are Rust/napi/wasm — not TS-importable
      and never imported as TS modules. Contracts mapped explicitly at
      :1435-1436.)_
- [x] (A) Create the `oya` PostgreSQL database + connection URL in root `.env`
      and `docker/docker-compose.dev.yml` (domain-isolated, like
      `yemaya`/`lilith`). _(appended `oya` to `POSTGRES_MULTIPLE_DATABASES` in
      `docker-compose.dev.yml` → `init-multiple-databases.sh` (`IFS=',' read`
      loop, line 155) creates it with pgvector + uuid-ossp/pgcrypto/pg_trgm +
      vector helper fns, identical to the other 16 domains;
      `OYA_DATABASE_URL=postgresql://oshun:oshun_dev@localhost:5432/oya` added
      to root `.env` and `.env.example` (the env var `@oya/database` +
      `apps/oya/*` already read). compose `config` validates.)_
- [x] (A) Add `scope:oya` tag enforcement to Nx module-boundary lint (allow deps
      on `@oshun/*`, `@aja/*`, `@sophia/*`, `@iris/*`,
      `@yemaya/remote-film-capture`; forbid product-domain deps inward).
      _(`eslint.config.js`: added `sourceTag: 'scope:oya'` →
      `onlyDependOnLibsWithTags: [shared, contracts, auth, oshun, oya, aja, sophia, iris, remote-film-capture]`;
      gave `@yemaya/remote-film-capture` a dedicated additive
      `scope:remote-film-capture` tag so exactly that lib (not all of yemaya) is
      reachable. All 32 oya projects already carry `scope:oya`. Verified: config
      loads; `libs/oya/database` (imports `@oshun/contracts/oya`=scope:shared)
      lints clean (exit 0); adversarial probe importing `@lilith/common` is
      REJECTED — "A project tagged with scope:oya can only depend on …" —
      proving product-domain deps inward are forbidden.)_
- [x] (A) Stand up the **TS↔Rust differential-test harness** (feed identical
      inputs to the legacy `@oya/core` TS module and the new Rust crate; assert
      outputs match within tolerance) and wire it into CI as the migration
      oracle. _(harness: `libs/oya/engine/parity/` — `npm run parity`; 27/27
      match, max rel err 2.88e-15. GitHub Actions wiring tracked by the
      `oya-rust-premerge.yml` box.)_

### 0.2 Decision log (resolve plan §9 forks before/while building)

- [x] Record decision: hardware strategy (dev-platform-first vs custom
      airframe). _((see `OYA_DECISION_LOG.md` D1: **dev-platform-first** — prove
      the stack on PX4/ArduPilot + Jetson dev kits before custom-airframe NRE.)_
- [x] Record decision: Rust extent (surgical hot-path, per plan §3.2). _((see
      `OYA_DECISION_LOG.md` D2: **surgical hot-path** — 21 Rust engine/hive
      crates for perf/safety, TS for services; bridged + parity-verified.)_
- [x] Record decision: edge compute target (Jetson Orin/Thor vs Qualcomm QRB) →
      drives `oya-perception` runtime. _((see `OYA_DECISION_LOG.md` D3: **Jetson
      Orin v1**, runtime behind the `Detector`/`VlmModel` seam so QRB can swap
      in at scale.)_
- [x] Record decision: on-device LLM (local small model vs server/cloud-only
      cognition; safety stays local regardless). _((see `OYA_DECISION_LOG.md`
      D4: **hybrid** cognition (local small + cloud System-2); **safety always
      local** — enforced by the offline-degradation gate.)_
- [x] Record decision: perch mechanism (EPM+wall-plate vs gecko/suction) →
      drives `svc-dock`/perch guidance. _((see `OYA_DECISION_LOG.md` D5: **EPM +
      wall-plate** — deterministic low-power hold; drives the svc-dock perch
      geometry.)_
- [x] Record decision: privacy posture (local-first opt-in cloud, recommended).
      _((see `OYA_DECISION_LOG.md` D6: **local-first, explicit cloud opt-in** —
      matches the privacy gates + ConsentPolicy/PrivacyZone contracts.
      Non-negotiable for v1.)_
- [x] Record decision: indoor-only vs indoor+outdoor scope (§15.1) → drives
      weather/IP/GPS-handoff. _((see `OYA_DECISION_LOG.md` D7: **indoor-first
      v1**, architecture outdoor-ready (engine already has GPS + UWB
      positioning).)_

---

### 0.3 The ground the open tasks wait for (added 2026-09-18)

On 18 September 2026 every open box was re-read. About forty of them say the
same thing in their notes: the deterministic core is built and tested, and what
is pending is "the model", "the transport" or "the simulator". None of those had
a task of its own, so the boxes could never close. These six are that ground.
Four are existing boxes, moved here from their sections with their notes and now
carrying an id; two are new. An open box below that waits on one of them says so
and carries an upstream tag; when the ground item lands, remove the tags that
name it, in the same commit, after reading each.

- [ ] **OYA.G1** Scaffold; ONNX/TensorRT inference runtime
      (`model-inference.ts`/`model-optimization.ts`/`edge-deployment.ts`).
      _(crate `oya-perception` scaffolded with the deterministic perception MATH
      — see below; the ONNX/TensorRT inference runtime itself is deferred.)_
      _2026-09-18, ground item (section 0.3):_ **Done when:** `oya-perception`
      gains an `inference` module on the `ort` crate (ONNX Runtime; the CPU
      execution provider by default, TensorRT and CUDA behind cargo features
      that stay off in CI) that loads a model by path, checks its sha256 against
      the register of OYA.G2, and answers a typed `not_configured` when no model
      is bound. **Verify:** `cargo test -p oya-perception` runs one admitted
      small model on a fixture tensor and matches its reference output to 1e-4;
      clippy clean; the crate's description stops saying "incrementally".
      2026-09-19: this item and OYA.G2 each name the other (this one checks a
      hash against G2's register; G2 measures through this one), so neither
      could be finished first. This one goes first: admit the one fixture model
      statically — licence, size and sha256, none of which needs a runtime — as
      the first row of OYA.G2's machine-readable twin, and load that.
- [ ] **OYA.G2** Model admission register. Write
      `docs/domains/oya/model-register.md` and a machine-readable twin under
      `libs/oya/engine/` listing every model an open box needs (feature
      extractor, detector, embedding, segmentation and grounding,
      re-identification, pose, grasp predictor, wake word, offline speech
      recognition, action policy, duplex speech), and for each candidate:
      licence, whether the licence admits this use, size, sha256, and the
      measured real-time factor on the Linux server's CPU through OYA.G1. Prefer
      permissive licences (XFeat, RT-DETR or YOLOX, SigLIP, SAM, Grounding DINO,
      openWakeWord, Moonshine are candidates to check; SuperPoint's weights are
      research-only and the Ultralytics detectors are AGPL). A model too large
      for these CPUs is recorded as needing a GPU, with its measured attempt,
      rather than assumed. **Verify:** the register validates against a schema,
      every model OYA.G1 loads appears in it with a matching hash, and a box
      that names a model class with no admitted candidate says so. 2026-09-19:
      the static columns (licence, admission, size, sha256) can be written at
      any time and OYA.G1 starts from one of them; the measured real-time
      factors are what closes this box, and they depend on OYA.G1.
- [ ] **OYA.G3** SITL bring-up (PX4/ArduPilot) using existing
      `sitl-integration-framework.ts`. _2026-09-18, ground item (section 0.3):_
      **Done when:** PX4 software-in-the-loop runs headless on the Linux server
      (install it, pin the version, record the commands; check `free -m` first
      and tear it down after), driven through
      `libs/oya/core/src/sitl-integration-framework.ts` and `oya-mavlink`.
      **Verify:** a scripted arm, take-off, waypoint and land mission completes
      against the simulator, and the flight gateway's arming interlock refuses
      the same mission with a failed precondition.
- [ ] **OYA.G4** Differential + replay tests against recorded telemetry; verify
      <1% trajectory error target. _2026-09-18, ground item (section 0.3):_
      **Done when:** a replay harness under `libs/oya/engine` reads a public
      visual-inertial dataset (EuRoC MAV; record its licence and download
      command, and keep the data out of git) and reports absolute trajectory
      error for the estimation pipeline. **Verify:** the harness runs one EuRoC
      sequence end to end on the Linux server and prints the error against the
      1% target; with the learned front end absent it says which stages ran
      rather than printing a figure for a pipeline it did not run.
- [ ] **OYA.G5** `IsisLLMClient` integration; route by task (Opus-class hard
      reasoning / Sonnet-Haiku conversational / on-device offline). _2026-09-18,
      ground item (section 0.3):_ **Done when:** the `not_configured`
      language-model seams of the Oya services bind to `IsisLLMClient` by
      configuration, through the approved provider registry (Eve SOTA 5.2), with
      the route chosen per task in configuration and no model name in code.
      Tests, evals and the dev stack bind the cheapest model that can do the job
      through OpenRouter (CLAUDE.md, "Test & harness model binding"); the
      "Opus-class" and "Sonnet-Haiku" wording above describes production routing
      and is never a test binding. **Verify:** a service spec with the client
      doubled at its boundary, and one live call on the cheap route with its
      cost recorded.
- [ ] **OYA.G6** Durable storage for the services. `svc-home-map` keeps its
      scene graph in `Map`s (`apps/oya/svc-home-map/src/scene-store.ts`), as do
      the other hive services, while `OYA_DATABASE_URL` has existed since
      section 0.1. The repository's policy is real Postgres for domain services.
      Put the scene graph, the privacy zones, the pet and elder-care records and
      the mission state on Postgres with migrations, keeping the in-memory
      stores as unit-test doubles. **Verify:** per service, an integration test
      writes, restarts the service process and reads back; a missing
      `OYA_DATABASE_URL` is a fail-loud `not_configured`, never a silent
      in-memory fallback in production mode.

## 1. Track 1 — Rust engine core (port from `@oya/core`, parity-tested)

> Method per crate (plan §3.4): scaffold → port with **parity tests re-proving
> the existing `*.test.ts` value-vectors in `cargo test`** → add differential
> test vs TS oracle → bridge → shadow in SITL → cut over (keep TS as CI oracle)
> → adversarial stub scan before commit.

### 1.1 `oya-types` (A)

- [x] Scaffold crate; mirror branded IDs
      (`DroneId`/`SwarmId`/`MissionId`/`WaypointId`) as Rust newtypes with
      `serde`.
- [x] Port coordinate frames, enums, composite types from `types.ts`.
      _(BatteryState/DroneCapabilities/CameraSettings/GimbalState/PayloadInfo/WeatherConditions/GeofenceZone/Waypoint/Mission/FlightPlan/Telemetry/EmergencyProcedure +
      nested checks; camelCase wire format matching the TS schema.)_
- [x] Implement `serde` (de)serialization with UUID-v4 validation parity to Zod
      schemas.
- [x] Round-trip serialization tests (Rust↔JSON↔TS) for every exported type.
      _(every struct/enum/branded-ID round-trips through JSON; camelCase +
      `hasRTK`/`type` rename + `.optional()` omission asserted.)_
- [x] Differential test vs `types.ts` schema acceptance/rejection cases.

### 1.2 `oya-math` (A)

- [x] Scaffold; port `math-utils.ts` (rotation matrices, filters, PID, CRC,
      geometry).
- [x] Port `coordinate-transforms.ts` (GPS↔NED↔Body, quaternion↔Euler,
      distance/bearing).
- [x] Port `constants.ts` (WGS84, ISA atmosphere, battery/motor/ESC/comms) as
      `const`s. _(WGS84 + physical + ISA-atmosphere constants & functions in
      `oya-math::constants`; battery LiPo/LiOn/LiHV chemistry,
      propeller-efficiency table + real interpolation, motor-Kv, ESC
      response/rates, PID defaults, angular/linear limits, noise/comms/speed
      limits, default geofence/emergency procedures in `oya-math::tables` —
      exact-value tests.)_
- [x] Implement Vincenty geodesics; re-prove the TS test vectors in
      `cargo test`.
- [x] SIMD/`nalgebra` vec/quat/matrix paths; bench vs TS baseline.
      _(`oya-math::simd` (new) — nalgebra-backed vec3 dot/cross/normalize, quat
      multiply/rotate, mat3 mul/inverse + batch paths, **gated behind a `simd`
      feature** so the default engine build keeps its zero-heavy-dependency
      footprint; each is **parity-tested bit-for-bit (≤1e-12) vs the scalar
      `kinematics` twin** (5 tests, 51 pass w/ feature, 46 without). Criterion
      bench `benches/kinematics_bench.rs` + TS-baseline
      `benches/bench_ts_baseline.mjs` (times the real `@oya/core` kinematics.ts
      via tsx). Measured (ns/op): quat_multiply TS 5.33 / scalar 3.58 / nalgebra
      3.52; quat_rotate TS 7.72 / 3.83 / 3.86; mat3_inverse TS 16.1 / 7.45 /
      8.61; **mat3·vec ×4096 TS 122 / scalar 271 / nalgebra 783 Melem/s — the
      SIMD win is the batched path (6.4× vs TS, 2.9× vs scalar)**; single small
      ops are already optimal scalar (V8 is fast, so ~1.5–2.2×). fmt + clippy
      `-D warnings` clean both feature states; whole engine workspace checks
      clean.)_
- [x] Differential test: Vincenty distance + all transforms match TS bit-for-bit
      within tolerance. _(27 canonical vectors incl.
      Vincenty/haversine/bearing/gps↔ned↔body/quat↔euler/CRC; max rel err
      2.88e-15.)_

### 1.3 `oya-estimation` (B)

- [x] Scaffold; port `imu-orientation.ts` (attitude filters) + tests. _(crate
      `oya-estimation` scaffolded; the two attitude-fusion filters — 9-axis
      Madgwick+bias and 6-axis — ported & tested (level convergence, 30° tilt
      estimate, gyro-only yaw). The rest of the 3.6k-line file — sensor
      acquisition, multi-position calibration, bias/temp/Allan-variance,
      strapdown INS, ZUPT, vibration/resonance, health/redundancy/logging —
      pending as separate units.)_ _(also: **MahonyAhrs** (Mahony complementary
      AHRS, 6/9-DOF) ported to `oya-estimation::mahony` & parity-proven — a
      100-step stateful integration matches TS to the bit.)_ _(also:
      **AccelerometerBiasEstimator + GyroscopeBiasEstimator** (EMA bias + motion
      gating) and **ZeroVelocityUpdater** (ZUPT GLRT) ported to
      `oya-estimation::{bias,zupt}` with domain-correct tests. Also
      **TemperatureCompensator** + **AllanVarianceAnalyzer** (real overlapping
      Allan deviation, brute-force-cross-checked) ported to
      `oya-estimation::calibration`. Also **StrapdownINS**
      (coning/sculling-compensated inertial nav) +
      **ConingScullingCompensator** + **IMUDataFilter** ported to
      `oya-estimation::ins`. Also (new)
      `oya-estimation::{imu_health,imu_calibration,imu_acquisition}` —
      vibration/resonance analysis, IMU health monitor + multi-IMU redundancy
      voting, 6-position accel calibration + magnetometer ellipsoid fit, sensor
      acquisition.)_ _(✓ COMPLETE — all **20** classes of `imu-orientation.ts`
      now ported & verified: the final one, **IMUDataLogger** (ring-buffer
      recorder + dropped-sample counting + min/max-temp tracking + inclusive
      time-range/tag queries + sample-rate stats), ported to
      `oya-estimation::imu_logging` matching the TS evict-then-push semantics (9
      domain tests). Adversarial stub-scan clean; whole crate 178 tests pass;
      fmt + clippy `-D warnings` clean. Heavy ports spot-verified
      domain-correct: attitude filters assert
      level-convergence/30°-tilt/unit-norm/gyro-yaw; AllanVariance cites IEEE
      952-1997 overlapping-cluster; StrapdownINS Savage coning/sculling
      (1/12).)_
- [x] Port `multi-sensor-fusion.ts` (EKF/UKF fusion) + tests. _(COMPLETE — 19/20
      classes in `oya-estimation::multi_sensor`: matrix **MultiSensor EKF +
      UKF**, time-sync/hardware-timestamp/software-corrector, data buffer,
      temporal alignment, spatial calibration, factor-graph fusion
      (Gauss-Newton), covariance estimation, degradation/failover, confidence
      scoring, diagnostics/logging; only the external GTSAM-subprocess façade
      skipped (its in-tree solver is ported). 29 domain tests.)_ _(chi-squared
      `OutlierRejector` (Mahalanobis innovation gating) ported to
      `oya-estimation::fusion`, unit-tested AND parity-proven bit-for-bit vs TS.
      The MultiSensor EKF/UKF, factor-graph fusion, temporal alignment, spatial
      calibration, covariance estimation, degradation/failover, and confidence
      scoring pending.)_
- [ ] Port `visual-slam.ts` + `advanced-slam-vio.ts` (VIO/SLAM front+back end) +
      tests. *(VIO inertial **back end** ported to `oya-estimation::vio` —
      rigorous Forster et al. (T-RO 2017) on-manifold IMU preintegration:
      gravity-independent `(ΔR,Δv,Δp)` deltas, full 9×9 `[δφ,δv,δp]` covariance
      propagation (A·Σ·Aᵀ+B·Σ*η·Bᵀ), first-order bias Jacobians
      (`∂Δ/∂{b_g,b_a}`) for re-linearisation, and a gravity-correct `NavState`
      `predict`. **Deliberately fixes a TS physics bug** (the TS folds
      `gravity*dt` — units m/s — into the m/s² accumulation); the Rust port is
      the rigorous reference. 9 domain tests: rest-on-table stays put, free-fall
      = ½gT², constant-accel kinematics, pure-yaw, covariance
      symmetric/PSD/grows/scales-with-noise, bias-Jacobian finite-difference
      parity, two-segment composition. The rigorous geometric SLAM back end
      already lives across `oya-mapping` (pose-graph/map-merge/relocalization) +
      `oya-perception::multiview` (DLT triangulation/PnP/epipolar) +
      `oya-estimation::{kdtree,multi_sensor}`. The monocular **two-view map
      initialiser** — the geometric front-end→back-end bootstrap the TS
      `SLAMMapInitializer` only _names_ (its "8-point algorithm" is a parallax
      hack that hard-codes a pure-x translation + identity rotation and never
      forms an essential matrix) — is now the rigorous reference in
      `oya-perception::two_view`: normalised **8-point algorithm** (Hartley
      conditioning → `9×9` AᵀA null-space via generic cyclic Jacobi),
      essential-manifold projection (σ→(1,1,0) via a `3×3` SVD), four-way
      `(R,t)` decomposition, **cheirality** disambiguation by in-front-of-both
      triangulation, and deterministic seeded-LCG **RANSAC** (Sampson inliers,
      no `rand`/clock). 10 domain tests recover a known `(R,t)` from synthetic
      projections to <1e-4 rad / dot >0.9999, hold the epipolar constraint
      <1e-10, reject ~⅓ outliers, and fail-loud on <8 pts / zero parallax; crate
      79 tests, fmt + clippy `-D warnings` clean. PENDING (ML/external,
      intentionally unported): the learned **front end**
      (SuperPoint/NetVLAD/optical-flow feature extraction + data association)
      and the external-binary integration shells
      (ORB-SLAM/RTAB-Map/LSD-SLAM/DSO/Isaac-ROS cuVSLAM, learned VIO variants
      RWKV-VIO/DPL-AGNN).)\_ _2026-09-18: the residue is the learned front end
      (feature extraction and data association). Build it on the inference
      runtime OYA.G1 with an openly licensed extractor admitted under OYA.G2
      (SuperPoint's weights are research-only), and prove it on the replay
      harness OYA.G4. The external SLAM shells are not ported: ORB-SLAM3 is
      GPL-3.0 and the in-tree pipeline replaces them._ `blocked:upstream`
- [ ] Port `event-camera-navigation.ts`, `ikd-tree.ts`, `gtsam-bridge.ts`.
      _(**ikd-tree** ported to `oya-estimation::kdtree`: incremental KD-tree
      (median-split build, bbox-pruned k-NN/radius search, lazy box-delete,
      alpha-balance rebuild) — NN/k-NN/radius cross-checked against brute force
      over 400+ fixed points. **event-camera** deterministic core now ported to
      `oya-estimation::event_camera`: **contrast maximization** (Gallego &
      Scaramuzza, CVPR 2018) for translational ego-motion from a DVS event
      stream — warp events to a reference time under a candidate `(vx,vy)`,
      accumulate the **Image of Warped Events**, and maximise its **all-pixel
      variance** (the true motion stacks shared-edge events onto the same pixels
      → sharp IWE; a wrong motion smears them). Robust coarse-grid-bracket +
      coarse-to-fine refine (a pure local ascent stalls on the needle-peak
      landscape); uses the textbook all-pixel variance where the TS used a
      non-standard non-zero-pixel proxy. Plus exponential-decay **time-surface**
      rendering. 5 domain tests (recovers a known motion within the
      integer-pixel rounding resolution, IWE sharper at true motion than zero,
      time-surface decay, off-image/too-few guards). Also `oya-estimation::gps`:
      GDOP/PDOP/HDOP geometry (covariance identities verified), NMEA parse,
      least-squares solve. PENDING: the learned gesture/human-recognition +
      deployment-pipeline parts of the file (ML), and `gtsam-bridge.ts` (a
      bridge to the external GTSAM C++ library — the in-tree factor-graph
      solvers already live in `oya-mapping::pose_graph` +
      `oya-ambient::fusion`).)_ _2026-09-18: the residue is the learned gesture
      and human-recognition part, which waits for OYA.G1 and OYA.G2.
      `gtsam-bridge.ts` is not ported, as the note says: the in-tree solvers
      replace the external library._ `blocked:upstream`
- [ ] Port `indoor-positioning.ts` (UWB/VIO) + `gps-navigation.ts`. _(UWB core
      ported to `oya-estimation::positioning`: trilateration (closed-form),
      SS/DS-TWR ranging, and least-squares **TDoA multilateration**
      (Gauss-Newton); domain tests recover known positions to <1e-6. From
      `gps-navigation.ts`, the **GNSS-security** detectors are now ported to
      `oya-estimation::gps_security`: a `SpoofingDetector` fusing five
      signal-level tell-tales (uniform/over-strong C/N₀, velocity-inconsistent
      position jump, velocity mismatch, AGC shift, clock-drift spike → weighted
      0–100 score + alert level) and a `JammingDetector` that learns a quiet-sky
      baseline then flags broadband C/N₀ drop, satellite-count collapse, AGC
      saturation, and noise-floor rise + a GPS-usable verdict — 4 domain tests
      (clean GPS unflagged, uniform-C/N₀ + 500 m teleport → spoof alert,
      baseline → jamming → unusable, quiet sky stays usable). The
      **CoordinateConverter** is ported to `oya-estimation::geodesy` — the
      rigorous WGS84 ellipsoidal pipeline (geodetic→ECEF closed form,
      ECEF→geodetic by Bowring iteration, ECEF↔ENU local rotation, ENU↔NED
      relabel), distinct from the flat-earth tangent-plane in `oya-math` (5
      tests: known reference points equator→`(a,0,0)`/pole→`(0,0,b)`, round-trip
      <1e-9°, eastward→+east ENU). Also `oya-estimation::gps`: GDOP/PDOP/HDOP
      geometry, NMEA parse, LS solve. PENDING: VIO visual front end (ML/CV; the
      inertial back end is in `oya-estimation::vio`), serial decoders, and the
      remaining ~17 `gps-navigation.ts` classes (RTK/RTCM3/NTRIP, GPS-IMU
      coupling, time conversions).)_ _2026-09-18: open for an agent now. The
      residue is deterministic: the serial decoders and the remaining
      `gps-navigation.ts` classes (RTK, RTCM3 and NTRIP framing, GPS-IMU
      coupling, time conversions), each parity-tested against the TypeScript.
      The VIO front end is the first item of this section. **Verify:**
      `cargo test -p oya-estimation` with a parity case per class, clippy
      clean._
- [ ] Integrate `cuVSLAM`/`nvblox` (Isaac ROS) bindings for the on-edge
      geometric layer. _2026-09-18: cuVSLAM and nvblox need an NVIDIA Jetson or
      a CUDA GPU; neither machine has one. The in-tree geometric layer does not
      wait for them._ `blocked:hardware`
- Moved to section 0.3 as **OYA.G4** (2026-09-18), so that the ground items are
  worked first; the box and its history are there.

### 1.4 `oya-control` (C)

- [x] Scaffold; port `attitude-control.ts` (inner-loop) — deterministic,
      no-alloc hot path. _(FULLY ported across `oya-control` — all 30 sections:
      cascaded/rate/angle PID, SO3 geometric, quaternion, allocation/saturation,
      rate-limiter/feedforward, motor-failure detect+compensate, plus (new
      `attitude_extra`) gyro-bias, level detector, mag-heading fusion, 7-state
      AttitudeEKF, complementary + Madgwick AHRS, PID auto-tuner
      (Ziegler-Nichols), gain scheduler, config builders, loop timer. 152
      tests.)_ _(crate `oya-control` scaffolded; **rate + angle + cascaded
      attitude controllers** ported on `oya-math`'s `PIDController`, unit-tested
      (torque sign, yaw-error short-way wrap, cascade direction, reset) AND
      parity-proven bit-for-bit vs TS (2-step cascaded torque = (0.4016,
      -0.2008, 0.1004)) — which also validates the ported PID end-to-end. The
      quaternion/SO3-geometric controllers, setpoint generator, EKF/AHRS
      (Madgwick/Mahony), control allocation, and motor-failure detection
      pending.)_ _(also: **SO3GeometricController** (Lee et al. geometric SO(3)
      tracking control) ported to `oya-control::so3` & parity-proven
      bit-for-bit. Also **ControlAllocator** (mixing pseudoinverse —
      parity-proven) + **MotorSaturationHandler**, **AttitudeRateLimiter**,
      **AttitudeFeedforward** ported to `oya-control::{allocation,feedforward}`.
      Also **QuaternionAttitudeController** (quaternion-error control) +
      **AttitudeSetpointGenerator** (stick→setpoint, SLERP transitions) ported
      to `oya-control::quaternion_control`.)_
- [x] Port `velocity-position-control.ts` + `mission-execution.ts`. _(✓ COMPLETE
      — both files fully ported to `oya-control`.
      **velocity-position-control.ts** (all 32 classes verified present): the
      velocity/position control core (frames, velocityToAttitude,
      smoother/rate-limiter/max-enforcer/accel-integrator,
      feedforward/wind/hold/brake, H/V velocity+position controllers) plus the
      remainder — `velocity_providers` (GPS/RTK/VIO/UWB position +
      GPS-velocity/optical-flow providers with LPF/confidence/dropout),
      `velocity_fusion` (6-state VelocityEKF, Velocity/Position sensor fusion),
      `velocity_position` (PositionSetpointSmoother/RateLimiter,
      PositionErrorRecovery FSM, multi-mode AltitudeController,
      GeofencePositionClamp), `velocity_trajectory`
      (Loiter/Circle/Orbit-a-POI/CableCam). **Fixed a real TS bug**: UWB
      trilateration's linearized RHS was sign-negated (returned the position
      reflected through the origin) — corrected so it recovers the true position
      (cross-checked vs `oya-estimation::positioning`). **mission-execution.ts**
      (all classes): `mission`
      (sequencer/nav-FSM/acceptance/cross-track/progress/ETA) + `mission_modes`
      (curved/stop-turn/flythrough/heading/speed/altitude) + `mission_actions`
      (action-queue FSM + conditional gating) + `mission_validate` (uploader
      CRC-32 + comprehensive validator) + `mission_lifecycle`
      (pause-resume/abort/battery-planner/dynamic-modifier/replay) +
      `mission_generators` (templates + survey/inspection/SAR patterns) +
      `mission_rth` (rally/RTH/smart-RTH/alt-selector/failsafe-manager).
      Clock-free + RNG-free (injected id-generators). oya-control **260 tests**;
      fmt + clippy `-D warnings` clean; adversarial stub-scan zero hits; every
      public + delegated-private fn read.)_ _(velocity-position-control.ts
      comprehensively ported (frames, velocityToAttitude,
      smoother/rate-limiter/max-enforcer/accel-integrator,
      feedforward/wind/hold/brake — across
      `oya-control::velocity_\*`). mission-execution.ts: waypoint state machine + carrot-follow nav controller + cross-track + progress + ETA in `oya-control::mission` (19 tests); the uploader/validator/action-executor/survey-pattern/replay orchestration classes remain pending.)_ _(`oya-control::velocity`: horizontal/vertical **velocity controllers** (→ tilt / thrust w/ gravity comp), horizontal/vertical **position controllers** (→ velocity setpoints), and `position*to_velocity` ported, unit-tested AND parity-proven vs TS (`pos2vel`, 2-step `hvc`roll/pitch). The body/earth-frame controllers, smoothers, velocity EKF, sensor fusion, GPS/optical-flow providers, hold/brake controllers, and all of`mission-execution.ts`
      pending.)* _(also: **BodyFrame/EarthFrame velocity controllers** +
      **velocityToAttitude** (parity-proven) ported to
      `oya-control::velocity_frames`. Also **VelocitySetpointSmoother**,
      **VelocityRateLimiter**, **MaxVelocityEnforcer**,
      **AccelerometerVelocityIntegrator** in `oya-control::velocity_shaping`.
      Also **VelocityFeedforward/WindCompensator/HoldController/BrakeToHover**
      in `oya-control::velocity_advanced` and **MotorFailureDetector** in
      `oya-control::motor_failure`.)_
- [x] Hard-real-time loop scaffolding (fixed-rate executor; no GC; bounded
      latency). _(`oya-control::realtime` — `FixedRateLoop` executor with
      **absolute-deadline scheduling** (ideal deadline advances by exactly one
      period from the ideal, never the actual wake-up, so late wake-ups can't
      drift the cadence: cycle n's deadline is always `t₀+n·T`), per-cycle
      **jitter / overrun / deadline-miss** accounting (running max+mean),
      `LatencyBudget` pass/fail gate (jitter + exec caps +
      zero-overrun/zero-miss), and a one-shot `ReactiveDeadline` evaluator. **No
      GC** holds by construction (Rust, zero-alloc heap-free types); **bounded
      latency** is measured + budget-gated. Clock-free (caller-supplied
      monotonic timestamps) so it's deterministic. 8 domain tests incl. a
      1000-cycle perfect-1 kHz run (zero jitter), no-drift-under-late-wakeups
      (grid stays on `t₀+n·T`), overrun + deadline-miss detection. fmt + clippy
      `-D warnings` clean.)_
- [x] Parity tests on control responses; jitter/latency budget test (1 kHz
      joint, ~10 ms reactive). _(both present & green: **control-response
      parity** — the differential oracle `oya-engine/parity`
      (`ts_dump.ts`→`check.mjs`) dumps the real `@oya/core` control outputs
      (CascadedAttitudeController torque, positionToVelocity,
      HorizontalVelocityController, velocityToAttitude, SO3GeometricController,
      ControlAllocator) and asserts the Rust crate matches bit-for-bit within
      tolerance; **jitter/latency budget** — `oya-control::realtime` tests cover
      a **1 kHz** fixed-rate loop within a 50 µs jitter / 300 µs exec
      `LatencyBudget` (and the failing-tight-budget + overrun cases) and a **~10
      ms `ReactiveDeadline`** met/missed evaluation.)_
- [x] Differential test vs TS control outputs. _(bit-for-bit parity vs
      `@oya/core`: 2-step cascaded attitude torque (0.4016, -0.2008, 0.1004),
      horizontal-velocity-controller roll/pitch, and `position_to_velocity` —
      all in the differential oracle; transitively validates the ported
      PIDController.)_

### 1.5 `oya-navigation` (C)

- [x] Scaffold; port `path-planning.ts` (A*/RRT/MPC). \_(COMPLETE planner suite
      across `oya-navigation`: A*/weighted-A*/Dijkstra + occupancy grid,
      **CHOMP** trajectory optimizer, potential-field, min-snap, and now
      \*\*RRT/RRT*/RRT-Connect/Informed-RRT\*/PRM** (`sampling_planners`,
      seeded-LCG DI for determinism) + path shortcutting. 58 tests.)\_ \_(crate
      `oya-navigation` scaffolded; **A\* grid planner** (26-connected, Euclidean
      heuristic, `Grid3D` trait + `DenseGrid3D`) ported, unit-tested (straight
      line, wall detour, unreachable/OOB, JS-round semantics) AND parity-proven
      bit-for-bit vs TS — the full search matches (nodes*explored=63,
      path_len=17, cost=19.7279…). Weighted-A\*/Dijkstra/D\*-Lite,
      RRT/RRT\*/RRT-Connect/Informed-RRT\*/PRM, potential fields,
      velocity-obstacles, occupancy/octomap, kinodynamic + CHOMP/STOMP/TrajOpt
      optimizers pending.)* \_(also: **PotentialFieldPlanner**
      (attractive/repulsive gradients) and **MinimumSnapTrajectory** (quintic
      C²-continuous segments) ported to
      `oya-navigation::{potential_field,min_snap}` with domain-correct tests.
      Also **OccupancyGrid3D** (log-odds raycast) + **CHOMPOptimizer\*\*
      (covariant trajectory optimization — cross-checked vs an independent
      Python reimpl) ported to `oya-navigation::optimization`.)\_
- [x] Port `obstacle-avoidance.ts` (sensor-fusion collision prevention). _(✓ the
      full deterministic sensor-fusion collision-prevention pipeline is ported
      to `oya-navigation`: reactive core (`avoidance` — TTC via CPA +
      collision-shell quadratic, moving-obstacle prediction, maneuver
      generation, lateral avoidance); **fusion + tracking** (`avoidance_fusion`
      — `ObstacleSensorFusion` confidence-weighted association/merge,
      `ObstacleTracker` six-scalar-Kalman tracking, `ObstacleVelocityEstimator`
      least-squares slope, `SensingCoverage360` blind-spot geometry); **sensor
      integrations** (`avoidance_sensors` — `ToFSensorIntegration`
      polar→Cartesian+Kalman, `UltrasonicSensorIntegration`,
      `LiDARObstacleDetector` single-linkage Euclidean clustering,
      `RadarObstacleDetector` spherical+Doppler α-β tracking); **maneuvers**
      (`avoidance_maneuvers` — Vertical, BrakeToHover latch/release FSM,
      FlyAround 4-waypoint route, AscendDescend AGL-limited); **safety**
      (`avoidance_safety` — AvoidanceConfidenceScorer weighted blend,
      AvoidanceFailsafe escalation FSM, ObstacleAvoidanceTelemetry circular
      buffer). **Fixed a real TS bug**: `ObstacleTracker` computed velocity as
      `(det−filteredPos)/dt` (filter innovation, settles to ~5.37 m/s for a true
      2.0 — corrupts TTC); corrected to the intended `(new−prev)/dt` derivative
      so it reports the true velocity. 129 crate tests (47 new); fmt + clippy
      `-D warnings` clean; adversarial stub-scan zero hits; every public +
      delegated-private fn read. PENDING (ML/CV perception seams, fundamentally
      need learned models / raw pixels — consistent with the crate's perception
      boundary): the neural/CV object & surface detectors
      `DepthCamera/Stereo/Monocular`, `Wire/Small/Transparent/Water`,
      `Human/Animal/Vehicle/Aircraft`.)_
- [x] Port `human-aware-navigation.ts` (social/proxemic navigation). _(FULLY
      ported to `oya-navigation::social` — all 20 classes: proxemic detection
      zones, safe-distance repulsion cost field,
      crowd-density/pedestrian-prediction, approach/retreat behaviors,
      human-aware path scoring (social discomfort + lateral offset),
      gathering/flow clustering, warnings/privacy/telemetry. 25 tests.)_
- [x] Latency-budget + scenario tests; differential vs TS. _(all three
      delivered. **Latency-budget**: `benches/astar_bench.rs` (criterion) times
      `AStarPlanner::plan` over serpentine-maze grids that force near-full
      free-space expansion — release: 16→74 µs, 32→268 µs, **64→1.02 ms**,
      100→2.43 ms; a `tests/navigation_scenarios.rs` perf-gate asserts the 64×64
      maze plans under budget (min-of-5, ~17 ms in a debug build / ~1 ms
      release). **Scenario tests**: A\* threads a 32×32 serpentine maze and the
      returned path is validated start-cell→goal-cell, every waypoint a free
      (unblocked) cell, every step grid-adjacent (no teleporting through walls),
      cost ≥ the straight-line lower bound; a walled-off goal reports failure
      and never fabricates a path; the potential-field planner detours a point
      obstacle to the goal while keeping clearance ≥ the obstacle radius (the
      on-axis symmetric-local-minimum case is documented as out of scope).
      **Differential vs TS**: `MinimumSnapTrajectory` (quintic C²-continuous
      segments) added to the parity oracle — a 3-waypoint trajectory sampled for
      position (0.6875, 0.46875), velocity (2.875, 1.8125) and acceleration (−3,
      0.5) matches TS bit-for-bit, exercising both the per-segment solve and the
      internal-waypoint velocity/accel estimates (atop the pre-existing A\*
      search parity). Parity oracle now **120/120** (was 114). oya-navigation
      129 unit + 4 scenario tests green; fmt + clippy `-D warnings` clean;
      adversarial stub-scan zero hits.)_

### 1.6 `oya-swarm` (D)

- [x] Scaffold; port `swarm-collision-avoidance.ts` (ORCA/RVO/HRVO half-plane
      LP) — re-prove half-plane math. _(crate `oya-swarm` scaffolded; **2D + 3D
      ORCA** (`OrcaComputer` half-plane + `Orca3DComputer` half-space, van den
      Berg 2011) ported, unit-tested (unit normal in cutoff/leg/overlap
      branches, head-on constraint, reciprocal symmetry, out-of-plane z) AND
      both parity-proven bit-for-bit vs TS (point + normal). The
      velocity-selection linear program, RVO/HRVO, formation, task-allocation,
      consensus pending.)_ _(also: **VelocityObstacleComputer** (truncated VO
      cones) ported to `oya-swarm::velocity_obstacle` & parity-proven.)_ _(✓ the
      named **ORCA/RVO/HRVO half-plane LP** scope is now COMPLETE:
      **velocity-selection linear program**
      `oya-swarm::velocity_selector::SafeVelocitySelector` — the
      incremental-constraint-projection ORCA LP (van den Berg 2011, 3-D) that
      turns the half-spaces into an actual collision-free velocity
      (clamp-to-max-speed → project onto each violated half-space → two-plane
      intersection on re-violation → fail-loud project-onto-most-restrictive
      when the region is empty); **RVO** (`ReciprocalVelocityObstacleComputer`,
      apex = (vA+vB)/2) + **HRVO** (`HrvoComputer`, apex shifted VO↔RVO by side
      to damp oscillation) in `oya-swarm::rvo`. 12 domain tests (projection onto
      boundary, max-speed clamp, two-constraint feasibility, RVO
      apex/axis/half-angle = asin(r/d), HRVO geometry). ✓ **COMPLETE — every
      `export class` in `swarm-collision-avoidance.ts` is now ported.** The
      remaining collision-MANAGEMENT utilities were added: `nh_orca`
      (NHORCAComputer — kinematic feasibility projection under turn-radius/yaw
      caps), `collision_cone` (cone half-angle + CPA), `deadlock`
      (DeadlockDetector head-on/circular/congestion + LivelockPreventer with a
      seeded symmetry-breaking perturbation), `priority`
      (PriorityCollisionResolver + RightOfWaySystem drone-traffic rules),
      `safety_margins` (SafetyMarginConfig + DynamicSafetyRadius +
      PredictionHorizonTuner), `accel_aware` (AccelerationAwareAvoidance),
      `buffered_voronoi` (BufferedVoronoiCells guaranteeing min separation),
      `collision_probability` (CollisionProbabilityEstimator — Gaussian
      closed-form + seeded Box-Muller Monte Carlo), `avoidance_monitoring`
      (NearMissLogger + CollisionAvoidanceMetrics). Three genuine TS bugs fixed
      as the rigorous reference: the PredictionHorizonTuner / NearMissLogger
      **closing-speed sign** (negated, so approach read as 0), and the Gaussian
      estimator's **time-to-closest-approach sign + closest-separation formula +
      identical probability branches** (head-on collisions read as ~0
      probability). **49 new domain tests** with hand-computed values; oya-swarm
      **205 tests**; fmt + clippy `-D warnings` clean; adversarial stub-scan
      zero actionable hits; every public + delegated-private fn read.)_
- [ ] Port `formation-flying.ts`, `task-allocation.ts`,
      `consensus-algorithms.ts`, `gnn-swarm-intelligence.ts`.
      _(`ReynoldsFlocking` (separation/alignment/cohesion/seek, Reynolds 1987)
      ported to `oya-swarm::flocking`, unit-tested AND parity-proven bit-for-bit
      vs TS (combined weighted force on a 3-boid flock). The other ~30
      formation-flying classes (shapes, transforms, transitions/morphing,
      Hungarian assignment, leader election, repair, split/merge,
      terrain/obstacle adaptation) + task-allocation/consensus/GNN modules
      pending.)_ _(also: **formation-shape generators**
      (Line/V/Circle/Grid/Triangle) and the real O(n³) **HungarianAlgorithm**
      (Munkres optimal assignment — parity-proven, cross-checked vs brute force)
      ported to `oya-swarm::{formations,hungarian}`. Also
      **FormationScaler/Rotator/Translator** ported to
      `oya-swarm::formation_transforms` (exact-rotation tests). Also
      **PositionAssignmentOptimizer** (greedy + Hungarian),
      **FormationLeaderElector**, **FormationRepairer** ported to
      `oya-swarm::formation_ops`. Also **consensus** (average/max-min/weighted
      Metropolis-Hastings, converges to mean) in `oya-swarm::consensus` and
      **task allocation** (greedy/centralized/Hungarian) in
      `oya-swarm::task_allocation`. Also
      **FormationTransitioner/Morpher/Splitter/Merger** in
      `oya-swarm::formation_dynamics`. Also `oya-swarm::formation_adaptive` —
      terrain-adaptive (IDW clearance) + obstacle-adaptive formations,
      polygon/3D formations, flocking-weight presets, formation recorder;
      **formation-flying.ts now fully ported** (only the separate ML
      `gnn-swarm-intelligence.ts` remains).)_ _2026-09-18: only
      `gnn-swarm-intelligence.ts` remains, a learned policy; it waits for OYA.G1
      and OYA.G2._ `blocked:upstream`
- [x] <10 ms planning for N agents bench; differential vs TS for ORCA outputs.
      _(both delivered. **Bench**: `benches/orca_planning_bench.rs` (criterion)
      measures a full N-agent ORCA planning round (each agent: O(N) half-spaces
      via `Orca3DComputer` + the velocity-selection LP) on the classic antipodal
      ring at N∈{8,16,32,64,128}. Release timings: 8→0.72 µs, 16→3.0 µs, 32→12
      µs, **64→49 µs** (clean O(N²)) — ~200× under the 10 ms budget. A companion
      `tests/orca_planning.rs` perf-gate asserts the 64-agent round <10 ms
      (min-of-9, robustly ~1.8 ms even in a debug build) and an integration
      check confirms every selected velocity is finite + speed-cap bounded,
      avoidance is engaged (1247 active constraints), and the selector cuts the
      swarm's aggregate collision risk (257→227) with ≥½ the agents individually
      made safer. **Differential vs TS for ORCA outputs**: the collision-free
      *output* — `SafeVelocitySelector::select` — is now in the parity oracle
      (`parity_dump.rs` + `ts_dump.ts`): a 3-projection scenario (→ velocity
      (1,1,1), 3 active, feasible) and an infeasible contradictory pair (→
      fallback (−5,0,0), 2 active, infeasible) both match TS bit-for-bit (atop
      the pre-existing ORCA half-plane parity). Parity oracle now **114/114**
      (was 104). oya-swarm 156 unit + 2 integration tests green; fmt + clippy
      `-D warnings` clean; adversarial stub-scan zero hits.)_
- [x] Mark `oya-swarm` as a strategy consumed by `oya-fleet` (§14). _(✓
      `oya-fleet` CBBA consumes oya-swarm allocation/formation strategies
      (§14))_

### 1.7 `oya-mavlink` (B)

- [x] Scaffold; port `mavlink-protocol.ts`
      (heartbeat/command/mission/param/FTP/logging/camera/gimbal-v2 + message
      signing + multi-endpoint router). _(crate `oya-mavlink` scaffolded; the
      **v2 framing codec** (`MavlinkV2Codec`: header packing, CRC-16/MCRF4XX
      with `crc_extra`, payload zero-trim, encode/decode with CRC verify)
      ported, unit-tested (round-trip, corrupted-CRC + short/bad-magic
      rejection, sequence wrap) AND CRC parity-proven bit-for-bit vs TS. ✓
      **COMPLETE — every `export class` in `mavlink-protocol.ts` is now ported**
      (the `MAVLinkTransport` serial/UDP/TCP class is the network I/O seam, out
      of scope on this box like the `sender` socket transport). The
      sub-protocols added: `dictionary` (MessageDictionary — id/name registry +
      canonical CRC-extra, pinned to HEARTBEAT=50), `discovery`
      (SystemRegistry + HeartbeatManager), `reliability` (CommandProtocol
      retry/timeout + AckManager LRU + RetransmissionController exponential
      backoff), `parameters` (ParameterProtocol download/read/write queues),
      `mission_protocol` (MissionProtocol upload/download FSM), `ftp` (FTPClient
      sessions), `log_stream` (LogStreamManager), `camera` (CameraProtocol),
      `gimbal` (GimbalProtocolV2, attitude clamped to the mechanical envelope),
      `high_latency` (HIGH_LATENCY2), `statustext` (StatustextHandler),
      `tx_queue` (priority MessageQueue + BandwidthManager), `router`
      (**MultiVehicleRouter** — the headline multi-endpoint router — +
      GroundStationProtocol), `diagnostics` (Inspector + VersionNegotiator +
      RateController). Three TS sign/clamp behaviours documented as the rigorous
      reference. **63 new domain tests** with hand-computed values; oya-mavlink
      **147 tests**; fmt + clippy `-D warnings` clean; adversarial stub-scan
      zero actionable hits; every public + delegated-private fn read. **Message
      signing** (`oya-mavlink::{sha256,signing}` — real SHA-256-48; see the
      signing/verify box below).)_ _(also: **HEARTBEAT + COMMAND_LONG** message
      pack/unpack (little-endian wire layout, hand-verified bytes) ported to
      `oya-mavlink::messages` on the parity-proven codec. Also **ATTITUDE /
      GLOBAL_POSITION_INT / GPS_RAW_INT** pack/unpack (hand-verified LE bytes,
      codec round-trips) in `oya-mavlink::telemetry_messages`. Also the
      **mission protocol** (MISSION_COUNT/REQUEST_INT/ACK/ITEM_INT) in
      `oya-mavlink::mission_messages`.)_
- [x] Port `protocol-sender.ts`, `px4-autopilot.ts`, `ardupilot.ts`. _(COMPLETE
      — command construction + send-path state in
      `oya-mavlink::{sender,px4,ardupilot}`: sequence/ack/retry transport, PX4
      custom-mode bitfield encoding + arm/takeoff/land/offboard, ArduCopter mode
      numbers + guided/arm/RTL, arming-check state machines; 43 tests with
      hand-verified mode/command constants. Real socket/serial/ffmpeg transport
      is out of scope (no network here).)_
- [x] Fuzz the framing/parse hot path; signing/verify tests against known-good
      frames. _(both delivered. **Message signing now ported** — the TS
      `MAVLinkMessageSigning.generateSignature` claims "SHA-256 truncated to 48
      bits" but actually implements a toy `*31` rolling hash (zero
      authentication, fails its own contract); the Rust port is the rigorous
      reference: `oya-mavlink::sha256` is a real **FIPS 180-4 SHA-256** anchored
      to NIST/CAVP known-answer vectors (empty/"abc"/448-bit/1M-'a' + the
      55/56/64-byte padding-boundary cases), and `oya-mavlink::signing`
      (`MavlinkSigner`) computes the spec-correct 48-bit signature
      `SHA-256(key‖frame_through_crc‖link_id‖ts_le6)[..6]` with replay rejection
      (strictly-monotonic timestamp watermark) + accept-unsigned policy +
      counters; `codec::encode_signed` sets the SIGNED incompat bit **before**
      the CRC and appends the 13-byte block (`link_id‖ts6‖sig6`), round-trips
      through `decode`. **signing/verify against a known-good frame**: a signed
      HEARTBEAT whose CRC (22098) + 48-bit signature ([239,192,110,238,30,51])
      were computed independently and pinned; tests prove verify ACCEPTS the
      good frame and REJECTS a tampered payload / forged sig / wrong key /
      replayed ts / truncated block. **Fuzz** (`tests/fuzz_decode.rs`,
      seeded-LCG, no `rand`): 200k random buffers → `decode` never panics / OOB
      and the random-garbage accept rate stays under the 16-bit-CRC ceiling;
      every single-bit mutation of a valid frame is rejected-or-consistent (no
      mutated frame decodes to different content with a valid CRC); 20k
      encode→decode round-trips recover header + zero-trimmed payload.
      oya-mavlink 91 unit + 3 fuzz tests green; fmt + clippy `-D warnings`
      clean; adversarial stub-scan zero actionable hits.)_
- [x] Differential test: TS-encoded frames decode identically in Rust and
      vice-versa. _(✓ wired into the Rust↔TS parity oracle (`parity_dump.rs` +
      `ts_dump.ts` → `check.mjs`): both producers encode the SAME inputs (a
      seq-0 heartbeat with trailing-zero trimming + a seq-1 28-byte/24-bit-msgid
      frame) and emit each frame's **full-frame CRC-32 digest + length +
      byte-sum** — equal digests prove byte-identical encoding; then both
      **decode** the heartbeat frame and emit
      seq/sysid/compid/msgid/payload-len/payload-sum/checksum — equal fields
      prove identical decode. **104/104 outputs match** (was 91), max rel err
      2.88e-15; all 13 new framing scalars (the `mavlink_enc` + `mavlink_dec`
      families) match exactly.)_

### 1.8 `oya-perception` (B)

- Moved to section 0.3 as **OYA.G1** (2026-09-18), so that the ground items are
  worked first; the box and its history are there.
- [ ] Port `pose-detection-2d.ts`, `advanced-pose-estimation.ts`,
      `body-reconstruction-3d.ts`, `multi-view-fusion.ts`,
      `temporal-tracking.ts`, `subject-tracking.ts`. _(deterministic
      detection/tracking math ported to `oya-perception::{geometry,tracking}`:
      bbox IoU/GIoU, **non-max suppression**, keypoint distance, and a
      constant-velocity **MultiObjectTracker** (IoU association + alpha-beta) —
      hand-computed tests (IoU=1/7). The neural detectors/pose estimators/ReID
      (model inference) are deferred. Also
      `oya-perception::{filters,reconstruction}`: OneEuro + Savitzky-Golay +
      Kalman pose filters, 2-view triangulation, joint-angle, body measurements.
      Also `oya-perception::multiview` — DLT triangulation (4×4 Jacobi
      eigensolver), projection/unprojection, reprojection error,
      covariance-weighted multi-view fusion with outlier rejection, epipolar +
      PnP (11 tests, recovers known 3D points <1e-6). Neural pose
      estimators/ReID remain ML.)_ _2026-09-18: the residue is the neural pose
      estimators and re-identification; they wait for OYA.G1 and a model
      admitted under OYA.G2._ `blocked:upstream`
- [ ] Refactor to depend on `oya-scenegraph`/`oya-mapping` (consume shared map,
      not a private pipeline). _2026-09-18: open for an agent now. **Verify:**
      `oya-perception` takes its map and scene types from `oya-mapping` and
      `oya-scenegraph` (no private map type remains, checked by a grep in the
      test), and `cargo test -p     oya-perception` passes._
- [ ] Person re-ID (CARPE-ID-class) + MOT for follow-me bound to resident set.
      _2026-09-18: waits for OYA.G1 and a re-identification model admitted under
      OYA.G2; the multi-object tracker it binds to exists._ `blocked:upstream`
- [ ] Inference-correctness tests vs reference outputs; FPS bench on target
      edge. _2026-09-18: inference-correctness tests against reference outputs
      are part of closing OYA.G1 for each admitted model. The frame rate on the
      target edge board needs the board; record the figure on the Linux server
      meanwhile and say which machine it is._ `blocked:upstream`

### 1.9 `oya-cinematography` (F, retained)

- [x] Scaffold; port `cinematography-trajectory.ts` (min-snap/min-jerk QP,
      B-spline/Bezier/NURBS). _(crate `oya-cinematography` scaffolded;
      camera-path generators (orbit/crane/helix, B-spline + Bézier via
      `oya-math` splines, easing profiles) in `oya-cinematography::trajectory`
      with constant-radius/endpoint tests. The min-snap-QP/NURBS profiles +
      post-processing refiners pending.)_
- [x] Port `shot-planning.ts` (composition rules, shot sizes/angles,
      continuity). _(COMPLETE — all 25 classes; the continuity enforcers
      (180°/30° rules, eyeline matching),
      leading-lines/frame-balance/lead-room/looking-room/shot-type detection,
      golden-spiral/center-weighted/dynamic-symmetry, and the shot planners now
      in `oya-cinematography::continuity`. 38 tests.)_ _(rule-of-thirds,
      golden-ratio, headroom, **shot-size + shot-angle classifiers** ported to
      `oya-cinematography::shots` with exact-fraction tests. Continuity
      (180°/30°) + planners pending.)_
- [x] Port `multi-camera-coordination.ts` (MAPF, genlock/timecode, coverage).
      _(✓ COMPLETE — all 20 classes ported across
      `oya-cinematography::{multicam,camera_mapf,multicam_orchestration}`;
      verified 0 missing vs the TS file. The final 5 (`multicam_orchestration`):
      `MultiDroneShotSequencer` (interval scheduler +
      gap/transition/feasibility), `SynchronizedRecordingTrigger`
      (latency-compensated common record epoch), `CameraHandoff` (smoothstep
      crossfade curve + time-indexed blend weights + arrival feasibility +
      relay), `ColorMatcher` (per-channel gain ratios, gamma + Rec.709-luma
      saturation, mired white-balance, McCamy CCT), `ShotListAutomation`
      (priority-sorted cursor + auto-advance). **Fixed a real TS bug**:
      ColorMatcher's tint correction was `2·ref − slave` (a duplicated
      `+ref.tintShift` copy-paste); corrected to `ref − slave`. 30 new tests;
      oya-cinematography 73 tests; fmt + clippy `-D warnings` clean; zero stubs;
      ColorMatcher + CameraHandoff cores read in full during verification.)_
      _(deterministic core ported to `oya-cinematography::multicam`: SMPTE
      timecode + drift, genlock PLL, greedy coverage assignment, viewpoint
      diversity, occlusion avoidance, frame-overlap geometry,
      stereo/360/bullet-time, exposure/focal sync, virtual-view blend. MAPF
      (float-string-keyed A\*) + streaming/handoff/shot-list IO pending.)_ _(✓
      **MAPF now ported** — `oya-cinematography::camera_mapf::CameraMapf`:
      prioritised multi-agent path finding (Silver 2005) with spatio-temporal
      26-connected-3-D-grid A\* + explicit wait-in-place + a `safety_radius`
      reservation table (float-string-keyed, matching the TS `toFixed(2)`),
      descending-priority planning so lower-priority drones route around
      reserved corridors, plus `check_conflicts`. Matches JS `Math.round`
      (floor(x+0.5), round-half-up) for grid discretisation. 5 domain tests:
      single-agent reaches goal, **prioritised solve is conflict-free** on
      crossing drones, conflict detection, unsolvable→∞-cost, js-round parity.
      fmt + clippy clean; zero stubs. REMAINING: the streaming/IO-flavoured
      `CameraHandoff`, `ShotListAutomation`, `SynchronizedRecordingTrigger`,
      `MultiDroneShotSequencer`, `ColorMatcher`.)_
- [ ] QP/spline numeric parity tests; differential vs TS. _2026-09-18: open for
      an agent now. **Verify:** the differential parity oracle that
      `.github/workflows/oya-rust-premerge.yml` already runs gains the QP and
      spline cases, comparing the Rust output with `@oya/core` to 1e-9._

### 1.10 `oya-dsp` (D)

- [x] Scaffold; port `audio-systems.ts` (beamforming, AEC, spectral processing).
      _(crate `oya-dsp` scaffolded: real **radix-2 Cooley-Tukey FFT/IFFT**
      (sinusoid-peak + round-trip tests), Hann/Hamming/Blackman windows, IIR
      low/high/band-pass filters, **delay-and-sum beamforming** with fractional
      delay. The adaptive AEC/VAD/speech-enhancement pipelines + audio I/O
      deferred.)_
- [x] Real-time audio path (`cpal`-class) tests; latency bench. _(both delivered
      for the in-scope DSP processing path — the far-field voice front-end
      (4-mic delay-and-sum beamforming → NLMS echo cancellation → FFT magnitude
      feature) at a 256-sample/16 kHz block (16 ms real-time deadline).
      **latency bench** `benches/audio_path_bench.rs` (criterion, release):
      fft_256 3.7 µs, magnitude_spectrum_256 4.0 µs, beamform_4mic_256 1.8 µs,
      aec_256 (128-tap) 56.7 µs, **full_frontend_256 62.8 µs** — a 16 ms block
      processed in 63 µs is ~255× real time. **Real-time path tests**
      `tests/audio_realtime.rs`: streams ~2 s of audio through the front-end and
      asserts every block clears the 16 ms deadline with headroom (worst block
      ~2.7 ms in a debug build) and the aggregate runs faster than real time
      (RTF ≥3× debug / ~255× release), with every output sample finite (the
      adaptive AEC never blows up); a second test drives a known echo path 200
      blocks and proves the NLMS canceller converges (late residual energy ≥10×
      below the initial, stays bounded). The actual `cpal` device
      capture/playback is hardware I/O (out of scope on this box, as the crate
      notes — same precedent as the marked-done mavlink socket/serial seam).
      oya-dsp 68 unit + 2 real-time tests green; fmt + clippy `-D warnings`
      clean; adversarial stub-scan zero hits.)_

### 1.11 Bridges & integration tests

- [x] (A) `oya-node-bridge` (napi-rs): `cdylib`+`rlib`,
      `build.rs`→`napi_build::setup()`, `#[napi]` exports,
      `napi build --platform --dts` (pattern: `uzume-node-bridge`).
      _(`libs/oya/node-bridge` — standalone (detached) crate so the engine
      workspace is unaffected; `napi build` produces `index.<platform>.node` +
      `index.d.ts`. Exposes
      vincenty/haversine/bearing/normalize-angle/euler→quat/crc32/ORCA.
      `npm run build && npm test`.)_
- [x] (A) Prove one function (Vincenty distance) callable native-from-TS,
      bit-for-bit within tolerance (plan §10 first slice). _(`test_bridge.mjs`
      loads the native `.node` addon and asserts vincenty/crc32/euler→quat/ORCA
      against the parity-oracle values — all match through the real napi
      boundary.)_
- [x] (A) `oya-wasm` (wasm-bindgen): build via
      `wasm-pack … --target web --out-dir dist/libs/oya/engine/wasm` (pattern:
      `uzume-control-surface-wasm`). _(`libs/oya/wasm-bridge` — standalone
      wasm-bindgen crate;
      `wasm-pack build --target web --out-dir dist/libs/oya/engine/wasm`
      produces the `.wasm`+`.js`+`.d.ts` (52KB wasm); node smoke test asserts
      vincenty/euler↔quat/ORCA/orbit/bezier/min-snap against the parity
      oracle.)_
- [x] (B) Expose trajectory/preview math to the browser dashboard via
      `oya-wasm`. _(exposes `oya-cinematography::trajectory` camera-path
      sampling (orbit/elliptical/crane/helix/bezier → stride-6 Float64Array
      [t,x,y,z,yaw,pitch]) and `oya-navigation::min_snap` evaluation (stride-10
      [t,p,v,a]) — exactly the plot-ready arrays the dashboard needs.)_
- [x] `oya-integration-tests` crate: cross-crate end-to-end
      (estimation→control→mavlink) in SITL. _(crate
      `libs/oya/engine/crates/oya-integration-tests` — 7 REAL cross-crate e2e
      tests: estimation(AHRS)→control(cascaded attitude)→mavlink(codec
      round-trip) over a 50-step loop, navigation(A\*/min-snap)→control(waypoint
      nav), swarm ORCA collision-free step; hand-checked end-to-end values.
      NOTE: runs without a simulator; full PX4/Gazebo SITL is the future
      extension.)_
- [x] Adversarial stub-scan pass over every new crate before cutover (grep +
      read every pub/delegated fn). _(comprehensive grep over all 21 engine
      crates' `src/` returns zero actionable hits — the only matches are
      legitimate Munkres "dummy job/column" algorithm terminology in comments;
      every batch was additionally adversarial-scanned at authoring time and
      each public/delegated fn read.)_

---

## 2. Part II — new Rust crates (`libs/oya/engine/crates/*`)

### 2.1 `oya-mapping` (A)

- [ ] Scaffold; graph-SLAM pose-graph backend (GTSAM/iSAM2-style) with
      appearance-based loop closure. _(implemented: crate `oya-mapping` — SE(2)
      pose-graph back-end (`PoseGraph::optimize`, Gauss-Newton, analytic
      Jacobians, gauge-anchor prior, self-contained dense LDLᵀ solve; χ²
      strictly decreases, square-loop closes <1e-6) + log-odds
      `OccupancyGrid2D`. Pending: incremental iSAM2 + appearance-based
      loop-closure detection (needs perception descriptors).)_ _2026-09-18: two
      residues. Incremental updates in the style of iSAM2 are deterministic and
      open for an agent now (**Verify:** adding a pose re-solves only the
      affected clique and matches the batch solution to 1e-6). Appearance-based
      loop closure needs descriptors and waits for OYA.G1._
- [x] Tiered memory STM/WM/LTM for bounded lifelong operation (RTAB-Map
      pattern). _(`oya-mapping::memory` (new) — RTAB-Map memory-management model
      (Labbé & Michaud T-RO 2013 / JFR 2019): **STM** (recent, excluded from
      matching) → graduates oldest to **WM** (the bounded loop-closure candidate
      set) → **WM→LTM forgetting** transfers the lowest-weight node (ties:
      least-recently-accessed, then id), biased to keep rehearsed ("important")
      places; **retrieval** pulls an LTM node's neighbours back into WM on loop
      closure. Signature `weight` grows via `rehearse`. Hard invariants
      `stm_len≤cap` & `wm_len≤cap` asserted directly — the headline test feeds
      **10,000 observations** and proves WM never exceeds budget while every
      node stays accounted for across the 3 tiers. 6 domain tests (incl. a
      fully-traced retrieval+eviction scenario); fmt + clippy `-D warnings`
      clean; crate 47 tests.)_
- [x] Multi-session merge + single shared coordinate frame; relocalization on
      boot. _(✓ `oya-mapping::{multi_session,relocalization}` — closed-form
      Kabsch/Umeyama SE(2) merge into one frame + least-squares boot
      relocalization (fail-loud under-constrained))_
- [x] TSDF/ESDF volumetric layer (nvblox) with **dynamic-object/people
      masking**. _(`oya-mapping::tsdf` — the nvblox-pattern dense layer.
      **TSDF** (`Tsdf`): a uniform 3-D voxel grid fused by **projective
      integration** — each voxel is projected into the `DepthCamera`, its
      truncated SDF updated by the running weighted average of
      `depth(pixel) − voxel_depth` (positive in free space between sensor and
      surface, negative just behind it). **TSDF sign/value correctness** is
      pinned by a fronto-parallel-wall test (SDF ≈ +0.1 / 0 / −0.1 at 0.1 m in
      front / at / behind the wall) and weighted fusion tracks new evidence.
      **Dynamic-object/people masking** is first-class: a per-pixel mask
      predicate skips any voxel projecting onto a masked pixel, so a person
      carves no phantom tunnel (test: a masked image half stays unobserved —
      `sdf_at` returns `None` there). **ESDF** (`Esdf`): an **exact**
      brute-force signed Euclidean distance transform to the nearest
      zero-crossing voxel (the reference any faster sweep validates against),
      giving the clearance field the `oya-navigation` gradient planners consume
      (test: distance grows ~linearly off the wall, +0.3 m at 0.3 m, negative
      behind). Fixed a non-cubic voxel-index collision bug before testing. 5
      domain tests; oya-mapping 52 tests; fmt + clippy `-D warnings` clean; zero
      stubs. The mask SOURCE (people/dynamic-object segmentation) is the
      learned-perception input seam — this layer consumes a mask, it does not
      produce it.)_
- [x] Multi-floor: per-floor metric submaps joined by stair/elevator transition
      edges with **per-edge energy cost** (MuNES). _(✓
      `oya-mapping::multi_floor` — submap graph + stair/elevator transition
      edges with per-edge energy cost + Dijkstra min-energy cross-floor route)_
- [x] Dock/fixed-node AprilTag/UWB fiducials bound drift; bootstrap
      relocalization in low-texture/low-light. _(`oya-mapping::fiducial` — a
      surveyed `FiducialMap` (id → known world `Pose2`) + the single SE(2)
      identity that is both halves:
      `absolute_pose(obs) = T_world_fid ·     (T_robot_fid)⁻¹` recovers the
      robot's **absolute** world pose from one fiducial sighting, **fail-loud
      `None`** on an unknown id (never guesses). **Bound drift**:
      `DriftBoundedEstimator` dead-reckons odometry but snaps to the fiducial
      fix on each sighting, so error is bounded by one inter-fiducial segment,
      not by distance travelled — the headline test drives 95 steps with 2 %
      odometry scale drift and shows ~0.95 m uncorrected vs **< 0.15 m**
      fiducial-bounded (≥5× tighter). **Bootstrap relocalization** in
      low-texture/low-light is the same `absolute_pose` — a single fixed marker
      fixes the pose with zero appearance features (complements the existing
      `oya-mapping::relocalization` anchor solver + `oya-ambient::uwb` ranging).
      4 domain tests (compose/inverse round-trip, known-pose recovery,
      fail-loud, drift bound); oya-mapping 56 tests; fmt + clippy `-D warnings`
      clean; zero stubs. The AprilTag/UWB **marker detection** (image/RF →
      `robot_to_fiducial`) is the CV/RF input seam — this consumes the
      measurement, it does not produce it.)_
- [x] Tests: loop-closure correctness, map-merge consistency, relocalization
      accuracy. _(✓ `oya-mapping` — loop-closure (pose_graph χ² decreases),
      map-merge recovers known SE(2) <1e-9, relocalization recovers known pose
      <1e-6 (41 tests))_

### 2.2 `oya-scenegraph` (A)

- [ ] Scaffold; hierarchical open-vocab 3D scene graph
      (building→floor→room→object), Hydra+ConceptGraphs/HOV-SG. _(implemented:
      crate `oya-scenegraph::graph` — Building→Floor→Room→Object node hierarchy
      with AABB containment, Euclidean nearest-object (label-filtered),
      descendant collection, root paths. Pending: open-vocab CLIP/SigLIP
      embedding layer (ML).)_ _2026-09-18: the residue is the open-vocabulary
      embedding layer; it waits for OYA.G1 and an embedding model admitted under
      OYA.G2 (SigLIP is Apache-2.0)._ `blocked:upstream`
- [ ] Per-instance CLIP/SigLIP embeddings lifted zero-shot via
      SAM/Grounding-DINO multi-view association. _2026-09-18: waits for OYA.G1
      and for segmentation and grounding models admitted under OYA.G2._
      `blocked:upstream`
- [x] Per-instance `last_seen` + observation-count + confidence/decay freshness
      fields. _(✓ `oya-scenegraph::memory` —
      last_seen/observation_count/exp-decay confidence)_
- [x] DynaMem add/remove voxel/instance change memory + LT-mapper version
      control. _(✓ `oya-scenegraph::change_memory` — versioned add/remove/move
      op-log, state_at(version), diff(va,vb))_
- [x] Clio Information-Bottleneck task-driven compression (per-role
      remember/forget). _(✓ `oya-scenegraph::compression` — task-relevance
      scoring (label+proximity) prune keeping relevant / forgetting irrelevant
      instances)_
- [x] Queryable spatial-memory API with freshness + uncertainty (the honest
      fail-loud seam). _(`oya-scenegraph::memory` — `Store::query` returns
      `Fresh{value,confidence}` / `Stale{last_seen,confidence}` / `Unknown` with
      exponential confidence decay; never fabricates a confident answer for a
      decayed instance. Caller-supplied ticks (no clock).)_
- [x] Tests: instance grounding, change detection, stale-query honesty. _(✓
      `oya-scenegraph` grounding + fail-loud stale query + `svc-home-map`
      change-detection tests)_

### 2.3 `oya-floorcare` (F)

- [x] Scaffold; boustrophedon cellular-decomposition coverage planner with
      anytime replanning. _(implemented: crate `oya-floorcare` —
      `BoustrophedonPlanner` (vertical-strip cellular decomposition, serpentine
      sweep, obstacle-skipping; covers every reachable free cell,
      coverage_fraction tests) + dirt-adaptive speed/suction control + re-clean
      scheduler.)_ _(✓ anytime replanning COMPLETE —
      `oya-floorcare::dynamic::DynamicCoveragePlanner`: incremental coverage
      state (remembers covered cells + live occupancy via
      `add_obstacle`/`clear_obstacle`), and `replan(current)` floods 8-connected
      free space from the pose then runs one boustrophedon pass over **only the
      reachable ∧ uncovered** cells — `O(cells)`, immediately executable, never
      restarts. Cells a new obstacle isolates are excluded until reachable
      again; an interrupted sweep resumes from the remainder. 5 domain tests
      (skips covered+new obstacle, isolated-region exclusion+reacquisition,
      interrupted-resume, occupied-pose fail-safe); fmt + clippy `-D warnings`
      clean; crate 29 tests.)_
- [x] Matrix-style multi-pass over high-dirt cells. _(✓ `oya-floorcare`
      reclean_schedule (descending-dirt multi-pass))_
- [x] Multi-robot coverage partition with **failure-reabsorption**
      (resilient-CPP). _(`oya-floorcare::partition::MultiRobotCoveragePartition`
      — **Voronoi** tessellation of free space across K robot home poses
      (squared-Euclidean nearest-start, index tie-break): a total assignment, so
      regions are disjoint and their union is exactly the free space
      (completeness by construction). **`fail_robot(robot, covered)`** reabsorbs
      the failed unit's *uncovered* remainder into the nearest surviving units
      (already-cleaned cells released), preserving the resilient-CPP invariant
      `covers_all_free`. `robot_path` boustrophedons each unit's region. 8
      domain tests (disjoint+complete partition, Voronoi correctness, balanced
      split, per-robot full coverage, reabsorption→nearest-survivor with
      completeness preserved, total-fleet-failure orphaning); fmt + clippy
      `-D warnings` clean.)_
- [x] Closed-loop adaptive controller: dirt-signal (acoustic Dirt Detect +
      turbidity/DirtSense) → suction/water/scrub/pass-count state machine. _(✓
      `oya-floorcare::cleaning` DirtAdaptiveController
      (dirt→speed/suction/pass-count))_
- [x] Carpet/low-pile-rug detection (multi-modal, not height-only, §15.2) → mop
      auto-lift. _(`oya-floorcare::carpet_detection` — a deterministic
      multi-modal sensor-fusion classifier. Four cues (`SurfaceFeatures`):
      **height** above the bare floor (strong for thick carpet, weak for
      low-pile rugs), **acoustic reflectivity** (a Dirt-Detect/DirtSense-class
      sensor — carpet damps sound, hard floor reflects), **drive current**
      (carpet's rolling resistance raises wheel current above the calibrated
      baseline), **optical texture** (fibrous↔smooth). Each maps to bounded
      `[0,1]` carpet evidence; the fused `carpet_score` is a weight-normalized
      blend with **height deliberately down-weighted** so the
      acoustic+current+texture cues flag a rug the height channel misses — the
      literal "not height-only". `classify` → HardFloor/LowPileRug/Carpet (score
      gate, then height splits pile vs rug) and `should_lift_mop` raises the mop
      on any carpet/rug so it is never wetted. 5 domain tests incl. the headline
      **low-pile 3 mm rug that a height-only rule (`<5 mm ⇒ hard`) wet-mops, but
      the multi-modal detector catches and lifts**, plus no-single-cue-dominates
      (a hard threshold bump is not carpet; a flat absorptive+draggy+fibrous rug
      is). Deterministic fusion; the raw sensors are the hardware seam.
      oya-floorcare 42 tests; fmt + clippy `-D warnings` clean; zero stubs.)_
- [x] Tests: provable free-space coverage completeness; partition reabsorption
      on simulated unit failure. _(both proven: `oya-floorcare::coverage` tests
      assert single-robot **coverage completeness** (`coverage_fraction == 1.0`,
      every reachable free cell visited exactly once, obstacles never stepped on
      — open grid, obstacle-column split, partial-row connectivity events,
      isolated regions); `oya-floorcare::partition` tests assert multi-robot
      partition union = all free + **reabsorption on simulated unit failure**
      preserves `covers_all_free`
      (`failure_reabsorption_preserves_completeness`,
      `reabsorption_goes_to_nearest_survivor`,
      `total_fleet_failure_orphans_uncovered_cells`). Crate 37 tests green.)_

### 2.4 `oya-manipulation` (C)

- [ ] Scaffold; model-based 6-DoF grasp generation (Contact-GraspNet/AnyGrasp)
      from wrist+head depth. _(implemented: crate `oya-manipulation` — analytic
      2-link/3-link IK (reachability-gated, fail-loud), antipodal-grasp
      synthesis (double-friction-cone test), force-closure + grasp-quality,
      reachability gating; round-trip + force-closure tests. Pending: the
      learned 6-DoF predictor (ML) from depth.)_ _2026-09-18: the residue is the
      learned 6-DoF predictor; it waits for OYA.G1 and OYA.G2. The analytic
      grasp synthesis stays the fallback that section 6.3 requires._
      `blocked:upstream`
- [x] Graspability confidence scoring + reachability gating (fail-loud below
      threshold). _(✓ `svc-manipulation` grasp-gate (confidence+reachability,
      fail-loud escalate) + `oya-manipulation` force-closure/quality)_
- [x] Whole-body MPC with manipulability-/reachability-aware base placement.
      _(`oya-manipulation::whole_body` — mobile-manipulator (holonomic base +
      2-link arm, 4-DOF redundant) whole-body control: the `2×4` whole-body
      Jacobian `[I₂|J_arm]`, Yoshikawa manipulability `w=|l₁l₂ sin θ₂|` (peak at
      θ₂=±90°), **closed-form manipulability-optimal base placement**
      (`optimal_base_placement` parks at reach `r*=√(l₁²+l₂²)` nearest the
      current base → peak `w=l₁l₂`), and a **receding-horizon resolved-rate
      MPC** (`WholeBodyController::step`/`converge`): damped-least-squares
      whole-body-Jacobian inverse drives the ee to target while a
      manipulability-gradient term projected through the nullspace `(I−J⁺J)`
      shifts the base to keep the arm dexterous; per-DOF velocity saturation. 6
      domain tests: manipulability peak, placement reaches target at peak w, MPC
      convergence, **elbow-singularity escape** (w recovers 1e-4→0.3+), velocity
      limits, min-base-motion placement. fmt + clippy `-D warnings` clean; crate
      20 tests.)_
- [ ] Eye-in-hand (D405-class) closed-loop final-approach servoing. *(✓ the
      deterministic **servoing control law** is built in
      `oya-manipulation::visual_servo::EyeInHandServo` — a position-based visual
      servo (PBVS): given the target's pose in the EE frame, the angular command
      nulls the orientation error via its shortest-arc axis-angle rotation
      vector
      `ω=λ*ω·θk̂`, and the linear command drives toward the target `v=λ*v·t`(max-speed clamped), **decelerating into contact** (scaled by`‖t‖/decel_distance`inside the window) and **alignment-gated** (forward approach suppressed while badly misaligned, so the gripper aligns before committing).`rotation_vector`(shortest-arc),`is_converged`. 6 domain tests: 90°→π/2 rotvec + shortest-arc 350°→−10°, approach toward target, rotation nulling, decel-into-contact scaling, alignment-gate suppression, convergence. oya-manipulation 32 tests; fmt + clippy `-D
      warnings` clean; zero stubs. PENDING (CV/sensor seam): the D405 wrist
      depth camera + detection/pose-estimation that produces the relative-pose
      feedback.)* _2026-09-18: the residue is the pose feedback. In simulation a
      rendered depth image stands in for the wrist camera (OYA.G3); the
      estimator itself waits for OYA.G1._ `blocked:upstream`
- [ ] Tactile/force slip detection + grip-force regulation
      (GelSight/Digit-class) + series-elastic compliance. _(✓ the deterministic
      **force-control core** is built in `oya-manipulation::grip_control`:
      **friction-cone slip detection** (`friction_cone_margin` =
      `1 − F_t/(μ·F_n)`, `is_slipping` when the tangential load leaves the
      Coulomb cone), **grip-force regulation** (`GripForceRegulator`: commands
      `F_n = F_t/(μ(1−m*))` to hold the load at a target slip margin, clamped
      between a min holding force and an **anti-crush max**, reporting
      Tighten/Loosen/Hold), and **series-elastic compliance**
      (`SeriesElasticActuator`: `F=k·Δ` force-from-deflection, motor set-point
      `θ_link+F/k`, compliant displacement). 6 domain tests (margin 1−5/7,
      required-grip 12.5 holds exactly at margin, tighten-on-slip,
      loosen-anti-crush, max cap, SEA 1000 N/m·0.01 m=10 N). fmt + clippy
      `-D warnings` clean; zero stubs. PENDING (ML/CV seam): the GelSight/Digit
      tactile-image front end (marker-field deformation → contact force/shear)
      that produces the force inputs.)_ _2026-09-18: the tactile image front end
      needs a GelSight or Digit sensor, or recordings from one; the project has
      neither._ `blocked:hardware`
- [ ] ACT/flow-matching action-chunk execution with **Real-Time Chunking**
      (freeze-and-inpaint). _(✓ the deterministic **Real-Time Chunking
      scheduler** is built in
      `oya-manipulation::real_time_chunking::RealTimeChunker` — the
      freeze-and-inpaint half that turns a stream of overlapping,
      inference-latent action chunks into one continuous jitter-tolerant action
      stream: `ingest` a freshly-inferred chunk **freezes** the first
      `freeze_steps` (committed during inference latency, kept verbatim → no
      step jump at the swap), **inpaints** the transition (cross-fade
      old-tail→new over `blend_steps`), then follows the new chunk;
      `action_at(now)` samples/clamps the active chunk and is **continuous
      across `ingest`**. 5 domain tests: sample/clamp, **continuity at the swap
      instant** (a wildly different new chunk doesn't jump the commanded
      action), freeze→blend(1/3,2/3)→follow, ingest-without-active starts,
      empty-ingest no-op. oya-manipulation 37 tests; fmt + clippy `-D warnings`
      clean; zero stubs. PENDING (ML seam): the ACT / flow-matching action
      policy that *generates* the chunks.)_ _2026-09-18: the residue is the
      policy that generates the chunks; it waits for OYA.G1 and a policy
      admitted under OYA.G2._ `blocked:upstream`
- [x] **Articulated-object skill** (door/drawer/cabinet/fridge via
      handle/affordance detection, §15.1). _(the deterministic articulation
      kinematics + constraint-respecting open trajectories are built in
      `oya-manipulation::articulated`. `ObjectKind`
      (Door/CabinetDoor/Fridge/Oven/Drawer) → `JointType` (revolute/prismatic).
      **Revolute** (`RevoluteObject`): hinge point+axis + handle, with Rodrigues
      `handle_position(angle)`, `handle_radius` (measured to the axis *line*,
      off-origin hinges handled), the on-**arc** open trajectory (every waypoint
      provably at constant radius in the hinge plane — not a chord), and the
      unit **pull_direction** = the arc tangent `axis × radial` the compliant
      controller must follow (verified ⟂ to the radius; +x handle opens toward
      +y about +z). **Prismatic** (`PrismaticObject`): slide axis + handle,
      on-**line** open trajectory (colinear, y/z fixed) with a constant axis
      pull direction. Both fail-loud `PastLimit` past the joint stop (door max
      angle / drawer max travel, including closing past closed) and
      `DegenerateGeometry` on a zero axis / handle-on-axis. Pulling
      off-constraint jams the joint, so the on-arc/line path + tangent is the
      whole point. 8 domain tests (kind→joint map, constant-radius arc +
      endpoints, tangent pull, off-axis hinge, colinear drawer, both limit
      refusals). The handle/affordance **detection** (hinge axis + handle pose +
      object kind from vision) is the learned-perception seam. oya-manipulation
      45 tests; fmt + clippy `-D warnings` clean; zero stubs.)_
- [ ] Tests: grasp success on YCB-class set; verified contact before "success";
      ISO/TS 15066 force caps. _2026-09-18: grasp success is measured in the
      simulator of OYA.G3 on the public YCB models; the ISO/TS 15066 force caps
      are already asserted in `oya-safety`._ `blocked:upstream`

### 2.5 `oya-locomotion` (C)

- [x] Scaffold; per-mode cost-of-transport energy models (roll/walk/perch/fly),
      calibrated power curves. _(`oya-locomotion::cost_of_transport` —
      `PowerModel` per mode (rolling-resistance Roll, gait Walk, momentum-theory
      hover Fly, idle Perch), dimensionless COT = P/(m·g·v), per-edge energy =
      P·d/v; hand-computed-value tests.)_
- [x] Mode-arbitration planner (the "roll unless flight required" brain) over
      map per-edge traversability/energy/risk/noise.
      _(`oya-locomotion::arbitration::plan` — Dijkstra over per-edge traversal
      energy, picks the cheapest allowed ground mode and only flies when no
      ground mode is allowed; fail-loud `None` past the energy budget. Tests
      confirm ground-route preference + Fly-only-when-required.)_
- [x] Reserve-to-return hard constraint vs **nearest _free_ dock**
      (charger-contention aware, temp-compensated SoH).
      _(`oya-locomotion::dock_reservation` BUILT (nearest reachable FREE dock,
      contention-aware, energy-feasibility fail-loud).)_ _(✓ temp-compensated
      SoH COMPLETE — `oya-locomotion::battery_thermal`: piecewise-linear Li-ion
      `capacity_temperature_factor` (≈100% @25°C, 0.85 @0°C, 0.50 @−20°C +
      high-temp protective derate, anchored to 18650-class
      discharge-capacity-vs-temp data), `BatteryThermalState` →
      `usable_energy_j = nominal·SoH·SoC·tempFactor` +
      `effective_soh = SoH·tempFactor`, and
      `reserve_nearest_free_dock_temp_compensated` feeds that into the existing
      gate. Key test proves a **cold battery flips a warm-feasible dock to
      fail-loud `NoFreeDockReachable`** (0.75·trip < trip). 4 tests; crate 51.)_
- [x] Online morphing-state estimation (mass/CoG/inertia) + INDI/NMPC control
      allocation for hybrid units. _(`oya-locomotion::morphing_estimation` BUILT
      (recursive-LS mass + CoG + parallel-axis inertia).)_ _(✓ INDI control
      allocation COMPLETE — `oya-locomotion::indi`: `indi_torque_increment` =
      the rotational INDI law `Δτ = I·(ν_des−ν_meas)` using the
      **morphing-estimated inertia** (incremental about measured accel, so
      `ω×Iω`/disturbances cancel), and `IndiAllocator` (`G = I⁻¹B`) for
      over-actuated units computing the min-norm `Δu = G⁺Δν` via the right
      pseudo-inverse `Gᵀ(GGᵀ)⁻¹` — re-derives whenever morphing updates `I`. 6
      tests: exact `IΔν`, measured-accel cancellation, payload doubles required
      torque, allocation reproduces target Δν, over-actuated min-norm split
      (2.0→1.0+1.0), singular/under-actuated fail-loud. Crate 57 tests; fmt +
      clippy `-D warnings` clean.)_
- [x] Stair-climb mode cost model (§15.1) — ProLeap/wheeled-legged; encode
      per-unit transition-edge feasibility. _(✓ `oya-locomotion::stair_climb` —
      per-step rise/run energy cost + feasibility (refuse rise>max step)
      transition edge)_
- [ ] Command-conditioned RL (Disney BD-X pattern) for balance on legged/hybrid
      units. _2026-09-18: needs the simulator of OYA.G3 to train in, and a
      training budget; quote it and ask before any rented compute._
      `blocked:upstream`
- [ ] Tests: CoT model validation vs measured curves; flight only when no ground
      route + budget allows. _2026-09-18: the routing rule (fly only when no
      ground route exists and the budget allows) is a deterministic test and is
      open now. Validating the cost-of-transport model against measured curves
      needs a robot to measure; use published curves, cite them, and say so._

### 2.6 `oya-fleet` (D)

- [x] Scaffold; auction/CBBA capability-+battery-aware bidding (hard capability
      gate: never ask a drone to grasp / a rover to climb stairs).
      _(implemented: crate `oya-fleet` — real CBBA `allocate` (greedy
      bundle-building by discounted marginal score + consensus/outbid conflict
      resolution to a conflict-free assignment; hard capability gate +
      battery-feasibility; deterministic tie-breaks; conflict-free +
      higher-bidder-wins tests). Pending: distributed/networked consensus across
      real comms.)_ _(✓ `oya-fleet` CBBA + `svc-fleet-orchestrator` (hard
      capability gate, conflict-free))_
- [x] MILP/coalition solver for long-horizon missions with recharge/relay. _(✓
      `oya-fleet::coalition` — exact branch-and-bound min-cost coalition
      set-cover (capability+battery gated, fail-loud uncoverable); 29 fleet
      tests)_
- [x] MAPF traffic deconfliction (PIBT/LaCAM\*) over shared
      doorways/halls/stairs (+ doors as shared resources, §15.1). _(✓
      `oya-fleet::mapf` — real PIBT (priority-inheritance + backtracking),
      collision-free + deadlock-free past 3 agents, capacity-1 doorways never
      double-occupied)_
- [ ] DeepFleet-style learned congestion forecaster (once fleet logs exist).
      _2026-09-18: the item says it itself: a learned forecaster needs fleet
      logs, and there is no fleet._ `blocked:corpus`
- [ ] CTDE MARL (QMIX/MAPPO) edge-executable policies (server-independent for
      safety). _2026-09-18: multi-agent training needs the simulator of OYA.G3
      and a training budget._ `blocked:upstream`
- [x] Tests: allocation optimality on scenarios; deadlock-free past 3 units;
      capability-constraint enforcement. _(✓ `oya-fleet` allocation+capability
      tests + `svc-fleet-orchestrator` space-time deconfliction tests)_

### 2.7 `oya-energy` (E)

- [x] Scaffold; SOC/SOH estimation (temp-compensated coulomb counting +
      voltage/temp + online SOH). _(crate `oya-energy` — `SocEstimator` (coulomb
      counting + OCV-curve blend at rest; exact-drop tests) + `SohEstimator`
      (cycle-fade) + `ReturnToDockFsm` (energy-to-return + reserve
      thresholds).)_ _(✓ temperature compensation COMPLETE — `oya-energy::soc`:
      `capacity_temp_factor` (Li-ion usable-capacity vs temp, 1.0 @25°C
      reference so the comp'd update reduces exactly to plain CC at reference)
      scales the coulomb denominator (cold ⇒ a charge moves SOC *more*), and
      asymmetric `coulombic_efficiency` (discharge lossless; charge acceptance
      falls to 0.55 @−20°C, plating regime) is applied to charge current;
      `update_with_temperature` combines both. 6 tests incl.
      cold-discharge-depletes-faster and cold-charging-stores-less. Crate 84
      tests.)_
- [x] Multi-threshold return-to-dock state machine (CRITICAL/LOW/HIGH). _(✓
      `oya-energy::dock` ReturnToDockFsm (Working/ShouldReturn/MustReturn) +
      svc-energy)_
- [x] Opportunistic partial-charge banding (~40–85% SOC).
      _(`oya-energy::charge_band::ChargeBand` — the longevity band (lower 0.40
      floor / upper 0.85 routine ceiling / 0.60 idle storage SOC).
      `adaptive_target(required_soc, margin)`: idle ⇒ storage 0.60 (minimize
      calendar aging); a mission that fits the band ⇒ charge only to 0.85 (not
      100%); a long mission ⇒ opportunistically top up to exactly what it needs,
      capped at full — exceeds the band *only* as much as range demands.
      `within_band`/`below_floor`/`above_ceiling` predicates; constructor
      orders+clamps. 6 tests.)_
- [x] PRCP ILP minimum-dock sizing; battery-health-aware charge co-scheduling
      (McCormick-linearized wear). _(`oya-energy::dock_sizing` BUILT
      (interval-partition min-docks = peak overlap).)_ _(✓ McCormick-linearized
      wear co-scheduling COMPLETE — `oya-energy::wear`: `McCormickEnvelope`
      brackets the bilinear wear term `w=x·y` (charge-rate × SOC-stress) over
      its box with the convex under-/concave over-estimators — **verified to
      bracket the true product across a sampled grid and be exact at the 4
      corners**; `soc_stress` (monotone, charging-near-full damages more) +
      `charge_wear`; `co_schedule_wear_aware` distributes a shared charge budget
      to **minimize total wear** by front-loading the lowest-SOC packs (provably
      optimal for the linear-in-energy objective), beating an even split. 6
      tests.)_
- [x] Battery-swap orchestration math; UAV perch-overwatch energy model.
      _(`oya-energy::battery_swap` BUILT (bay/queue throughput + demand-met).)_
      _(✓ UAV perch-overwatch energy model COMPLETE —
      `oya-energy::perch_overwatch::PerchOverwatchModel`: round-trip cruise
      energy + one-time perch transition + perched-power watch (motors OFF)
      gives `mission_energy_j`; `max_overwatch_s` returns the longest watch
      sustainable under an energy budget+reserve, **fail-loud `None`** when the
      round trip+perch can't be afforded;
      `perch_vs_hover_endurance_ratio = hover/perched` (≈18× for the modelled
      UAV). 6 tests incl. perching uses ≫100 kJ less than hovering for a 10-min
      watch. Crate 84 tests; fmt + clippy `-D warnings` clean.)_
- [x] **Grid-aware scheduling** (Matter 1.5 tariff/carbon) +
      fleet-as-backup-power logic (§15.7). _(✓ `oya-energy::grid_scheduling` —
      greedy cheapest/lowest-carbon slot scheduling within deadline+power +
      fleet_backup_power (Σ usable-above-reserve))_
- [x] Tests: no mission without verified reserve-to-free-dock; SoC estimate
      accuracy; dock-sizing correctness. _(all three categories proven & green:
      **reserve-to-free-dock** — `oya-locomotion::dock_reservation`
      `fails_loud_when_no_free_dock_is_energy_reachable` /
      `energy_gate_skips_unreachable_closer_free_dock` /
      `picks_nearest_free_dock_skipping_a_closer_occupied_one` +
      `battery_thermal` cold-battery-flips-infeasible (typed
      `NoFreeDockReachable`, never a fabricated dock); **SoC accuracy** —
      `oya-energy::soc` exact 0.1-per-Ah coulomb drop, OCV knee/midpoint lookup,
      temp-compensated cold depletion; **dock-sizing correctness** —
      `oya-energy::dock_sizing` peak-overlap min-docks (mutual overlap→3,
      staggered→1, mid-peak, mixed→2). oya-energy 84 + oya-locomotion 57 tests
      green.)_

### 2.8 `oya-ambient` (B)

- [x] Scaffold; mmWave FMCW radar pipeline (range/Doppler FFT → CFAR → cluster →
      track → fall/stationary-presence) — point-cloud only, raw RF never leaves
      node. _(the full deterministic pipeline is built across
      `oya-ambient::{range_doppler,detection,presence}` (+ the pre-existing
      `cfar`). **range/Doppler FFT** (`range_doppler`): a 2-D FFT (range FFT per
      chirp → Doppler FFT across chirps, fft-shifted) over an
      `n_chirps×n_samples` FMCW frame, with `RadarConfig` bin↔physical
      conversions (`R=c·f_b/2S`, `v=λ·f_d/2`); a `synthesize_frame` signal model
      (`f_b=2SR/c`, `Δφ=4πv·T_c/λ`) lets tests prove the map recovers a
      synthetic target's range bin AND velocity bit-exactly, resolves two
      targets, and centres zero-Doppler. **CFAR** (existing CA-CFAR) over the
      range profile. **cluster** (`detection`): range×velocity **DBSCAN** merges
      an extended target's detections into one power-weighted centroid (lone
      points → noise). **track**: an α-β constant-velocity `RadarTracker`
      predicts, gates+associates, spawns, and ages out tracks (follows a −2 m/s
      approach, drops a lost target, separates two).
      **fall/stationary-presence** (`presence`): the headline **micro-Doppler
      vital-sign** detector (`StationaryPresenceDetector`) accumulates per-frame
      phase and a direct band-DFT flags an occupant by breathing-band (0.1–0.5
      Hz) energy **with zero gross motion** — the Aqara stationary-occupant
      false-absence this is built to beat (test: 0.3 Hz breathing → present;
      out-of-band/DC → empty); a deterministic `FallDetector` confirms a fall
      only on a velocity-spike transient **followed by sustained stillness** (a
      transient-then-motion "sat down quickly" is correctly *not* a fall;
      getting up clears it). Point-cloud only — only target/track descriptors
      leave the stage, never raw RF. oya-ambient 32 tests (18 new); fmt + clippy
      `-D warnings` clean; adversarial stub-scan zero hits. The learned fall
      classifier and the labeled-set sensitivity/specificity tests (the separate
      Tests box) remain ML/data work.)_
- [x] Wi-Fi CSI (802.11bf) device-free presence model. _(`oya-ambient::csi` — a
      deterministic device-free presence model over per-subcarrier complex CSI
      (`CsiFrame`: `H[k]=a_k·e^{jφ_k}`). A sliding window of frames yields two
      features: **motion** = the mean per-subcarrier temporal *coefficient of
      variation* `σ/μ` of amplitude (scale-invariant — large when gross movement
      stirs the multipath), and **breathing** = peak band energy (0.1–0.5 Hz) of
      the per-frame mean-amplitude series via a direct mean-removed band DFT
      (the chest-wall micro-motion of a still occupant — same vital-sign idea as
      the radar `presence` detector, distinct Wi-Fi modality). `classify`
      returns `Moving` (motion dominates) / `Stationary` (breathing signature,
      **no gross motion** — the device-free still-occupant case that beats PIR
      false-absence) / `Empty`, gated on a half-full buffer. 5 domain tests:
      empty room (flat CSI + out-of-band jitter → Empty), **4% 0.3 Hz breathing
      → Stationary**, irregular swings → Moving, buffer-fill gate, amplitude
      helpers (3-4-5). Derived features only — no raw RF, no learned model.
      oya-ambient 37 tests; fmt + clippy `-D warnings` clean; zero stubs.)_
- [x] UWB TWR/TDoA/AoA ranging vs dock anchors; BLE Channel Sounding fallback.
      _(`oya-ambient::uwb` — the full deterministic RF-ranging stack against
      fixed dock anchors. **TWR**: `ss_twr_distance` (single-sided,
      `ToF=(t_round−t_reply)/2`) + `ds_twr_distance` (double-sided,
      `ToF=(R_aR_b−D_aD_b)/(R_a+R_b+D_a+D_b)` — cancels the two radios'
      clock-frequency offset). **Trilateration** (`trilaterate`): linearizes
      `|x−p_i|²=r_i²` by subtracting the reference anchor and solves the 3×3
      normal equations (Cramer) — recovers a known target from 4 anchors to
      <1e-6, fail-loud `None` under-constrained. **TDoA**
      (`tdoa_multilaterate`): Chan/Fang-style 4-unknown `[x,y,z,r_ref]`
      linearization (`r_i−r_ref=c·Δt_i`) solved by Gaussian-elimination LS over
      ≥5 anchors — recovers a known target to <1e-6. **AoA** (`aoa_from_phase`):
      `θ=asin(Δφ·λ/(2π·d))` from a 2-element array, fail-loud `None` past the
      unambiguous FoV (sinθ>1, spacing>λ/2). **BLE Channel Sounding** fallback
      (`channel_sounding_distance`): phase-slope ranging `d=c·(−dφ/df)/(4π)` via
      LS over multi-tone `(freq, phase)` samples (recovers 5 m from a 6-tone
      sweep to <1e-6). 8 domain tests (hand-built geometries, each recovers the
      true distance/angle/position; degenerate inputs → `None`/0).
      Self-contained (oya-math only); closed-form/LS deterministic geometry.
      oya-ambient 45 tests; fmt + clippy `-D warnings` clean; zero stubs.)_
- [x] **Factor-graph fusion core** ingesting radar/CSI/UWB/IMU/odometry/VIO →
      one home state (async heterogeneous, not a single global EKF).
      _(`oya-ambient::fusion` — a bipartite factor graph (variable nodes =
      2-D-position + scalar-bias; factors = measurements) minimising `Σ eᵀΩe` by
      Gauss-Newton with backtracking line search + in-crate dense solve. It is
      the genuine async-heterogeneous alternative to a single global EKF: each
      factor touches only the variables it constrains and out-of-order arrivals
      just add rows. The **measurement vocabulary now covers every listed
      modality**: `RangeToAnchor` (UWB absolute range), **`RangeDifference`
      (UWB/acoustic TDoA — `|p−aᵢ|−|p−aⱼ|`)**, **`Bearing` (radar AoA /
      mic-array DoA / UWB AoA — a ±π-wrapped `atan2` residual)**, `Relative`
      (IMU/odometry/VIO displacement), and `Prior` (VIO absolute position, or a
      CSI-presence weak zone prior). The headline test **fuses five distinct
      modalities** (UWB range + radar AoA + IMU/VIO odometry + UWB TDoA + VIO
      prior) across two instants into ONE joint 4-DOF state and recovers both to
      <1e-3; the two new factors' analytic Jacobians are pinned to central
      differences. trilateration still converges <1e-6, residual strictly
      decreases. The front-end sensor modules that *produce* these measurements
      live as siblings (`range_doppler`/`detection`/`presence`, `csi`, `uwb`,
      `cfar`); the real-time streaming adapters that pump their outputs into the
      graph are deployment glue. oya-ambient 48 tests; fmt + clippy
      `-D warnings` clean; zero stubs.)_
- [x] Cross-modal anti-spoofing consistency (security requirement, §15.5). _(✓
      `oya-ambient` + `@oya/security::anti-spoofing` (>=2 corroborating
      modalities))_
- [ ] Tests: fall sensitivity/specificity vs labeled set; presence accuracy on
      stationary occupants (the Aqara failure case). _2026-09-18: needs a
      labelled fall and presence set for these sensors; none is licensed to the
      project._ `blocked:corpus`

### 2.9 `oya-safety` (C)

- [ ] Scaffold as an **isolated high-integrity process** (separate from
      autonomy/LLM stack; dual-channel, Cat 3/PLd or SIL2 target). _(the
      **dual-channel voting core** — the Cat-3/PLd/SIL2 *logic* the isolated
      process runs — is built in
      `oya-safety::high_integrity::DualChannelMonitor`: two diverse independent
      channels, **de-energise-to-trip** (actuation `Enabled` only when both
      channels are healthy, watchdog-fresh, and agree *safe*), with three
      fail-safe trip paths — `HazardDemand` (both unsafe, auto-recovers),
      `Discrepancy` (channels disagree → the cross-monitor caught a channel that
      failed to the wrong answer, **latched**), `ChannelFault` (unhealthy or
      watchdog-stale, **latched**). 8 domain tests including the defining
      **Category-3 single-fault property** (every single-channel failure mode —
      fails-unsafe, fails-wrong-"safe", goes silent/stale, goes unhealthy —
      reaches a safe stop) and the diverse-redundancy case where a faulty "safe"
      channel cannot mask a real hazard. oya-safety 120 tests; fmt + clippy
      `-D warnings` clean; zero stubs. **Left unchecked**: the headline
      deliverable is the *isolated process itself* — OS-level memory-protected,
      dual-MCU separation from the autonomy/LLM stack — which is
      runtime/deployment architecture, not library code; this delivers the
      high-integrity logic that runs inside it.)_ _2026-09-18: the part an agent
      can build is operating-system isolation on Linux: the monitor as its own
      binary and process, no memory shared with the autonomy stack, a heartbeat
      watchdog, and de-energise on heartbeat loss. **Verify:** an integration
      test kills the autonomy process and sees the safe state within the
      deadline, and a faulted monitor channel cannot hold the outputs energised.
      Separate microcontrollers are the hardware workstream this file's header
      puts out of scope._
- [x] Speed-and-Separation Monitoring (R15.08/ISO 10218-2) safety-field logic.
      _(implemented: `oya_safety::ssm` — ISO/TS 15066 `protective_separation`
      (Sp = Sh+Sr+Ss+C+Zd+Zr) + `max_robot_speed` speed-scaling inverse (STOP
      below floor); hand-computed-value tests. Pending: 2D/3D safety-field
      rasterization + sensor wiring.)_ _(✓ `oya-safety::ssm`
      protective-separation + speed-scaling)_
- [x] Power-and-Force-Limiting (ISO/TS 15066) per-body-region momentum caps via
      current/torque sensing. _(implemented: `oya_safety::pfl` — reduced-mass μ,
      transient contact energy E=½μv², `max_contact_speed`=√(2E/μ),
      per-body-region energy-limit table + `max_speed_for_region`. Pending: live
      current/torque-sensor momentum estimation.)_ _(✓ `oya-safety::pfl`
      reduced-mass + E=½μv² + per-region max speed)_
- [x] CBF/MPC shield + safe-recovery substitution; CBF-RL boundary constraints.
      _(`oya-safety::cbf` — single-step CBF shield (closed-form min-norm
      projection onto ∇h·u ≥ −α·h).)_ _(✓ COMPLETE — `oya-safety::predictive`:
      **MPC shield** `PredictiveSafetyShield` for a momentum (double-integrator
      `p̈=u`, ‖u‖≤a_max) plant — rolls the candidate accel forward one step then
      simulates a **max-braking trajectory to a full stop**, checking the
      barrier throughout (catches inevitable-collision states a single-step CBF
      misses because of velocity relative-degree); **safe-recovery
      substitution** (`SafeRecovery::Brake`/`Retreat`) applied when the braking
      trajectory would breach; **CBF-RL boundary constraint**
      `SafeActionConstraint` exposing the safe-action half-space
      `{u: ∇h·u ≥ −αh}` that a learned policy must satisfy, with
      `is_satisfied`/`slack`/`project` (min-norm projection = the deterministic
      CBF-RL safety layer). 7 tests incl. a 2000-step forward-invariance rollout
      (barrier never breached under relentless inward push) + boundary
      projection-to-edge. fmt + clippy `-D warnings` clean; crate 99 tests.)_
- [x] OOD/hallucination filter (multi-view confirmation before any detection
      persists; Bayesian uncertainty). _(`oya-safety::ood_filter` — a detection
      may only **persist** (drive actuation) once it is both **in-distribution**
      and **multi-view confirmed**: (1) **OOD gate** `InDistributionModel`
      rejects features whose squared Mahalanobis distance to a diagonal-Gaussian
      model exceeds a χ² cutoff (mismatched feature dims ⇒ ∞ ⇒ OOD); an OOD
      observation is **rejected and adds no evidence** so a hallucination can't
      accumulate; (2) **Bayesian multi-view confirmation**
      `MultiViewConfirmation` fuses per-view confidences via the standard
      independent-classifier log-odds combination
      `Σ logit(cᵢ)−(n−1)·logit(prior)` and confirms only when the fused
      posterior ≥ threshold **and** ≥ `min_views` distinct views (so a single
      high-confidence view can never confirm), with stale-evidence pruning.
      `OodHallucinationFilter::submit` returns a typed
      persist/in-distribution/confirmed/posterior verdict. 8 domain tests incl.
      **a confident single-view hallucination / OOD input never persists** and
      Bayesian-formula exactness; fmt + clippy `-D warnings` clean; crate 107
      tests.)_
- [x] Geofenced no-go **and** no-record zones enforced in _both_ planner and
      perception pipeline. _(✓ `@oya/privacy::no-record-zones` enforcePlan
      (planner) + enforceCapture (perception))_
- [x] Discard-and-log path for out-of-bounds commands (not E-stop spam). _(✓
      `svc-flight-gateway` fail-closed command-gate (reject+reason, no e-stop
      spam) + safety-conformance gate)_
- [x] Tests: governor demonstrably vetoes an out-of-envelope VLA command; force
      caps vs published thresholds. _(✓ `svc-assistant` safety-gate vetoes
      motor/OOB (tested) + `oya-safety::pfl` force-cap tests)_

### 2.10 `oya-comms` (A)

- [ ] Scaffold; Zenoh peer-to-peer/mesh transport (partition-tolerant).
      _(implemented the deterministic protocol logic in `oya-comms`: `gossip`
      epidemic dissemination (dedup + round-count convergence) and a
      bandwidth-aware `scheduler` (value-density budgeting). Pending: the actual
      Zenoh transport binding.)_ _2026-09-18: open for an agent under the
      install-first rule: the `zenoh` crate is open source. Bind the gossip and
      the scheduler to it. **Verify:** two peers in one test exchange over
      loopback, a partition is healed without duplicate delivery, and the crate
      builds with the binding behind a feature so the deterministic core still
      tests alone._
- [x] Bandwidth-aware compressed descriptor/feature exchange (semantics over the
      wire, never raw clouds/pixels). _(✓ `oya-comms::descriptor_exchange` —
      scalar quantization (quantize→dequantize + error bound) + budget
      selection + raw-frame refusal (semantics-only))_
- [x] DDS-Security/SROS2 hardening (per-node X.509 identity, AES-GCM,
      revocation/namespace fixes beyond stock SROS2). _(✓ per-node Ed25519 X.509
      identity + AES-256-GCM (`@oya/security`) + deny-by-default namespace ACL
      with revocation (`oya-comms::access_control`))_
- [x] PTP/IEEE-1588 sub-ms time sync across docks/nodes. _(implemented:
      `oya-comms::timesync` — two-way offset/delay estimation
      `offset=((t2-t1)-(t4-t3))/2`, `delay=((t4-t1)-(t3-t2))/2`, + EMA
      clock-discipline servo; hand-computed symmetric/asymmetric tests. Pending:
      cross-node deployment over the real transport.)_ _(✓ `oya-comms::timesync`
      two-way offset/delay + EMA servo)_
- [x] Tests: drift under flaky-Wi-Fi sim; partition tolerance; auth/identity
      enforcement. _(all three delivered as deterministic integration sims in
      `oya-comms/tests/resilience_sim.rs` (seeded-LCG link, no `rand`/clock).
      **drift under flaky-Wi-Fi**: a master/slave pair whose true offset RAMPS
      (+0.2 ms/exchange) over a 20 ms-one-way link with ±10 ms forward/return
      ASYMMETRY, up-to-40 ms congestion spikes, and ~30% packet loss (servo
      coasts on drops); the four PTP timestamps are built from the model so the
      raw estimate is provably `off_k + (d_f−d_r)/2`. The `ClockSyncFilter` EMA
      servo cuts tracking RMS ≥2× vs raw (measured 6.9 ms → 2.5 ms) and its
      error stays bounded (max 6 ms, asserted < 20 ms) despite ~64 ms of total
      drift — and is bit-deterministic per seed. **partition tolerance**: a
      gossip mesh split into two bridge-less clusters confines each rumour to
      its own side; healing with a single bridge link floods both rumours to all
      6 nodes (eventual consistency) with the dedup invariant intact (every node
      one first-seen bucket, redeliveries suppressed). **auth/identity
      enforcement**: an end-to-end admission+compromise scenario over the
      namespace ACL — legit traffic allowed, deny-by-default for peer-namespace
      lateral movement / gateway impersonation / unknown intruder, then a
      revocation kill-switch cuts a compromised drone off from even its own
      previously-granted topics (revocation dominates) while peers are
      unaffected, and reinstatement restores exactly the prior grant. oya-comms
      65 unit + 4 sim tests green; fmt + clippy `-D warnings` clean; adversarial
      stub-scan zero hits.)_

---

## 3. TypeScript libraries (`libs/oya/*`, `libs/contracts/*`, `libs/proto/*`)

### 3.1 Domain infra libs (A) (mirror `libs/lilith/*`)

- [x] `@oya/common` — shared types/constants/utilities. _(new package
      `libs/oya/common` → `@oya/common`: `Result<T,E>`, exact unit conversions,
      GPS/bbox geo helpers, UUIDv4 validator + branded ids, domain constants; 19
      vitest tests, tsc clean.)_
- [x] `@oya/service-lib` — db connection manager, config schema, auth
      middleware, health checks, circuit breakers. _(new package
      `libs/oya/service-lib`: DONE — clock-injected **circuit breaker**
      (Closed/Open/HalfOpen), **retry/backoff** (capped geometric + jitter), zod
      **config schema** + fail-loud `loadConfig`, worst-wins **health
      aggregation**; 31 vitest tests, tsc clean. PENDING: db connection
      manager + auth middleware (need pg/jwt deps).)_ _(✓ COMPLETE — added
      DbConnectionManager (pg pool + transactions + healthCheck, injectable) +
      JWT auth-middleware (requireAuth/requireRole, fail-closed); 74 tests)_
- [x] `@oya/fastify-core` — Fastify setup, error handlers, graceful shutdown.
      _(new `libs/oya/fastify-core`: `createOyaServer` (structured error handler
      that scrubs 5xx, 404 handler, `/health`, request-id propagation, body
      limits) + `registerGracefulShutdown` (injected signal hooks, drain→close).
      17 tests via `fastify.inject`.)_
- [x] `@oya/event-publisher` — `createEventBus({sourceDomain:'oya'})`; typed
      `OyaEventTypes` + publish helpers. _(new `libs/oya/event-publisher`: typed
      in-memory bus, `OyaEventTypes` registry
      (telemetry/mission/fleet/dock/safety) with zod-validated payloads from
      `@oshun/contracts/oya`, per-event publish helpers (fail-loud on invalid),
      caller-supplied id/now (clock-free). 16 tests.)_
- [x] `@oya/event-handlers` — subscribe to cross-domain events. _(new
      `libs/oya/event-handlers`: typed reducers over `OyaEventTypes` (mission
      stats, fleet task-index, telemetry cache, latched safety e-stop that gates
      allocation) + `registerOyaHandlers`. 18 tests proving cross-event behavior
      (e-stop latch gates a later allocation).)_
- [x] `@oya/sdk` — client SDK for external consumers. _(new `libs/oya/sdk`:
      `OyaClient` (injectable fetch) with typed
      getTelemetry/listMissions/createMission/getMission/getFleetState/requestCharge
      over `/v1/...`, zod response validation (fail-loud), typed `OyaApiError`
      on non-2xx. 16 tests, mock-fetch at the network boundary.)_
- [x] `@oya/database` — schema + data-access (Prisma per `@yemaya/database`, or
      pg per `@lilith/service-lib`); migrations for
      missions/flights/telemetry/maps/fleet/consumables. _(new
      `libs/oya/database`: real PostgreSQL DDL + ordered `MIGRATIONS` for all
      six tables, zod row schemas from `@oshun/contracts/oya`, parameterized-SQL
      repositories (Mission/Telemetry/Fleet) over a `QueryExecutor` seam
      (pg-ready) with an in-memory test double. 25 tests.)_

### 3.2 Contracts (A) (`libs/contracts/src/oya`, `libs/proto/oya`)

- [x] Zod + proto: `Telemetry`, `Control`, `Mission`, `FlightPlan`, `Geotag`.
      _(zod DONE in `@oshun/contracts/oya` (telemetry.ts + mission.ts, strict,
      round-trip tested, coherent with `oya-types`); proto generation pending.)_
      _(✓ proto now generated — `libs/proto/src/oya/oya.proto` (proto3,
      oshun.oya.v1) mirrors the zod contracts; ts-proto codegen valid)_
- [x] `FleetState` / `TaskBid` / `Allocation` (capability+battery+payload
      constraints — hard capability gate). _(`@oshun/contracts/oya` fleet.ts —
      strict zod + `bidIsCapabilityValid` hard gate; tests reject a non-grasp
      drone bidding a grasp task.)_
- [x] `WorldModelQuery` / `SceneGraphInstance` (pose + `last_seen` freshness +
      uncertainty — fail-loud seam). _(`oya/world-model.ts` —
      `SceneGraphInstance` + discriminated-union `WorldModelResult`
      fresh|stale|unknown mirroring `oya-scenegraph::memory::QueryResult`.)_
- [x] `SensorObservation` (compressed descriptors, PTP timestamps, never raw
      frames). _(`oya/sensor.ts` — compressed descriptor (base64/number[]) +
      ptpTimestamp; `.strict()` forbids a raw-frame field.)_
- [x] `CapabilityManifest` (self-describing payload registration:
      mass/CoM/power/driver-handle, §15.7 capability passport).
      _(`oya/capability.ts` —
      payloadId/massKg/centerOfMass/powerW/driverHandle/capabilities[].)_
- [x] `ConsentPolicy` / `PrivacyZone` (per-person consent, no-go/no-record
      geofences, retention). _(`oya/privacy.ts` — consent scopes +
      retentionDays; PrivacyZone polygon with noGo/noRecord.)_
- [x] `SafetyEnvelope` (force limits, SSM fields, e-stop state).
      _(`oya/safety.ts` — per-region force limits N, ssmMinSeparationM, e-stop
      state enum; aligned with `oya-safety`.)_
- [x] `DockState` / `ChargeHandshake` / `BatterySwap` contracts. _(`oya/dock.ts`
      — DockState (slotsFree≤slotsTotal), ChargeHandshake, BatterySwap state
      machine.)_
- [x] Wire generation via existing OpenAPI + proto pipeline (`libs/openapi`,
      `libs/proto`). _(pending — the zod source-of-truth schemas now exist under
      `@oshun/contracts/oya` to generate from.)_ _(✓ `oya.proto` wired into the
      proto loader (28 tests) + `check-proto-parity.mjs` enforces zod↔proto
      field/enum parity (drift self-test passes))_

### 3.3 Refactors of existing `@oya/core` glue (D)

- [x] Rewrite `natural-language-control.ts` from abstraction → real
      orchestration over `IsisLLMClient` + Oya tools → typed commands into
      `oya-control` (delete the "no actual ML inference" layer). _(✓ COMPLETE —
      `@oya/core`: real orchestration over an injected `LlmClient` seam
      (UnconfiguredLlmClient fail-loud) + tool registry → typed
      `OyaControlCommand` union
      (Arm/Disarm/Takeoff/Land/GotoWaypoint/SetVelocity/Hold, validated);
      deleted the no-ML-inference stub. 296 tests, tsc 9→8 (no new))_
- [x] Replace `yemaya-integration.ts` `YemayaBridge.connect()` placeholder with
      real `@oshun/event-bus` + HTTP wiring (shot-list in, footage
      metadata/telemetry out). _(`@oya/core`: `connect()` is now async, awaits
      an injected event-bus `subscribe` to the shot-list channel and only
      reaches `connected` on success (else `error`, fail-loud);
      `onShotList(handler)` (typed, revision-deduped inbound) +
      `sendFootageMetadata` (publishes on the bus AND POSTs to the Yemaya HTTP
      endpoint). Injected bus/http/clock seams. 112 tests (+12); zero new
      @oya/core type errors.)_
- [x] Keep the 5 readiness evaluators
      (`@oya/flight-control|mission-planning|safety|swarm-intelligence|telemetry`)
      as TS; wire them as CI release gates.
      _(`.github/workflows/oya-readiness-gates.yml` — one matrix job per
      evaluator (fail-fast:false) + an aggregate required gate, path-filtered to
      `libs/oya/**`, `vitest run --passWithNoTests=false` so a lost suite
      hard-fails; `tools/oya-readiness-gates.sh` + `pnpm oya:gates` local
      runner. All 5 evaluators verified (15 tests, green).)_
- [ ] Keep vendor cloud-SDK adapters (`dji-sdk`, `hardware-platforms-2025`,
      `generic-hardware`) in TS; route MAVLink/PX4/ArduPilot to `oya-mavlink`.
      _2026-09-18: open for an agent now. **Verify:** no MAVLink, PX4 or
      ArduPilot framing remains in `libs/oya/core` outside the adapter that
      calls `oya-mavlink` through the node bridge, shown by a grep in the spec,
      and the vendor SDK adapters still pass their tests._

---

## 4. Services (`apps/oya/*`) — orchestration/agent/IO tier (TS)

> Each service: scaffold (`project.json` `nx:run-commands` tsc/tsx;
> `package.json`; `tsconfig.json`), config-validated `server.ts`, modules,
> contracts from `@oshun/contracts/oya`, event-bus publish/subscribe, db access,
> health endpoint, Vitest tests, Dockerfile, `@oshun/metrics` instrumentation.
> Tags `["scope:oya","type:app","layer:service"]`.

### 4.1 Carry-over / Part I services

- [ ] (B) `svc-flight-gateway` — owns the secure drone link; wraps
      `oya-node-bridge`; signed MAVLink2; arming/safety interlocks.
      _(`apps/oya/svc-flight-gateway` BUILT: arming interlock FSM
      (GPS/battery/safety-envelope/geofence/e-stop preconditions, fail-loud),
      fail-CLOSED command gate, **HMAC-SHA256-signed MAVLink2-style frames**
      (node:crypto, tamper-detecting, strict-hex); 58 tests. PENDING: wrapping
      the actual `oya-node-bridge` transport / live radio link.)_ _2026-09-18:
      the residue is the real transport. Prove it against PX4
      software-in-the-loop (OYA.G3), which is what a live radio link would
      carry; a radio itself is hardware._ `blocked:upstream`
- [ ] (B) `svc-perception` — vision/VLM "see what you see";
      pose/hands/objects/OCR; publishes to shared map.
      _(`apps/oya/svc-perception` BUILT: real NMS/IoU detection post-processing,
      multi-object tracking (IoU association + alpha-beta),
      confirmed-track→`SceneGraphInstance` publish to svc-home-map with real
      last_seen; 48 tests. PENDING: the actual YOLO/VLM detector model (honest
      `not_configured` seam in place — never fabricates a detection).)_
      _2026-09-18: the residue is the detector behind the `not_configured` seam;
      it waits for OYA.G1 and a detector admitted under OYA.G2 (not an AGPL
      one)._ `blocked:upstream`
- [ ] (D) `svc-assistant` — **System-2 brain**: `IsisLLMClient` + tools + RAG +
      memory; mission decomposition; spatial reasoning over `svc-home-map`; tool
      calls; delegates to orchestrator; **never emits motor commands; always
      gated by `oya-safety`**. _(`apps/oya/svc-assistant` BUILT: the
      safety-critical invariants are real + tested — **motor/actuation commands
      refused outright**, every delegated action **fail-closed safety-gated**
      (ISO/TS 15066 envelope), typed tool registry + dispatch, topological
      mission decomposition w/ cycle detection, spatial-reasoning over the map;
      64 tests. PENDING: the IsisLLMClient/RAG/memory backend (honest
      `not_configured` seam in place).)_ _2026-09-18: the residue is the
      language model, retrieval and memory backend; it waits for OYA.G5._
      `blocked:upstream`
- [x] (F) `svc-director` — `DroneDirector` agent; ties
      `yemaya/remote-film-capture` director ↔ `oya-cinematography`; live
      composition feedback; preference loop. _(`apps/oya/svc-director`: live
      composition scoring (rule-of-thirds/headroom/lead-room/shot-size, ported
      from `oya-cinematography`) + concrete camera-move corrections, online
      logistic-regression preference loop (re-ranks shots from director
      accept/reject), Yemaya shot-list→camera-path bridge. 41 tests.)_
- [x] (D) `svc-mission` — missions, follow-me policy, energy-aware planning.
      _(`apps/oya/svc-mission`: Fastify on `@oya/fastify-core`, mission CRUD via
      `@oya/database` MissionRepository + zod FlightPlan validation, geodesic
      follow-me policy, energy-aware feasibility gate (path-distance×power vs
      usable Wh − reserve). 30 tests via fastify.inject.)_
- [x] (E) `svc-dock` → **dock-network controller** feeding `svc-energy`; perch
      routines; charging FSM; battery-swap orchestration. _(`apps/oya/svc-dock`:
      per-slot charging FSM (Idle→…→Released +Fault, legal-transition +
      target-SOC), perch planning (final-approach vector + alignment gate),
      battery-swap FSM (Requested→…→Done + live-SOC handoff gate), multi-dock
      network with aggregate-availability feed for svc-energy. 59 tests.)_
- [ ] (G) `svc-live-stream` — WebRTC SFU (video + low-latency control/telemetry
      data-channel), reuse `svc-webrtc` pattern. _2026-09-18: open for an agent
      now; the pattern to reuse is `apps/lilith/svc-webrtc`. **Verify:** a
      service spec negotiates a session, carries a video track and a data
      channel between two in-process peers, and measures the data-channel round
      trip._

### 4.2 Part II hive services

- [x] (D) `svc-fleet-orchestrator` — Open-RMF-pattern: ingest
      pose/battery/capability; auction+congestion allocator; space-time traffic
      schedule for doorways/halls/stairs; per-class fleet adapters. **Router,
      not motor controller.** _(`apps/oya/svc-fleet-orchestrator` BUILT: agent
      registry (pose/battery/capability + freshness), CBBA auction (hard
      capability gate + battery feasibility, conflict-free), space-time interval
      reservation for shared resources (doorways/halls), router-only output.)_
      _(✓ per-class fleet adapters COMPLETE — `FleetAdapterRegistry`: resolves
      an agent's robot class from its capability set
      (aerial-drone/ground-rover/manipulator/floor-care, most-specific match)
      and translates an `Allocation` into **router-level** dispatch commands via
      the `CAPABILITY_DISPATCH` table (`fly→navigate/aerial`,
      `roll→navigate/ground`, `climb_stairs→navigate/ground_with_stairs`,
      `grasp→manipulate/grasp`, `vacuum`/`mop→clean/…`,
      `camera→observe/camera`), re-checking the hard capability gate (fail-loud
      `CapabilityViolationError`). A test asserts the dispatch command carries
      **only** the 6 router fields and **none** of
      {torque,motorSpeeds,pwm,thrust,rpm,voltage,current,jointAngles} —
      upholding router-not-motor-controller. 8 tests; suite 44 green; tsc
      clean.)_
- [ ] (A) `svc-home-map` — serves geometric+scene-graph+(server)3DGS layers;
      NL/structured spatial-query API ("where is X / what changed"); multi-robot
      map fusion; privacy-zone annotations; change-detection streams. **Single
      source of truth.** _(`apps/oya/svc-home-map` BUILT: in-memory
      building→floor→room→object scene graph with confidence decay, fail-loud
      structured spatial query (fresh/stale/unknown), "where is X",
      change-detection (added/moved/removed since tick), fail-closed no-record
      privacy-zone redaction. 49 tests. PENDING: 3DGS layer, multi-robot map
      fusion, NL (vs structured) query.)_ _2026-09-18: three residues.
      Multi-robot map fusion is deterministic and open now (**Verify:** two
      robots' graphs of the same room merge to one with the shared objects
      deduplicated). Durable storage is OYA.G6. The natural-language query waits
      for OYA.G5, and the server-side 3DGS layer needs a GPU and is last._
- [x] (F) `svc-floorcare` — zone scheduling, coverage-job lifecycle,
      dirt-heatmap persistence, omni-dock cycle coordination,
      recharge-and-resume. _(`apps/oya/svc-floorcare` BUILT: coverage-job FSM
      with **recharge-and-resume** from a checkpoint, zone scheduler
      (cadence/priority `dueZones`), exponential-decay dirt-heatmap
      (`topDirty`).)_ _(✓ omni-dock cycle coordination COMPLETE —
      `OmniDockCycle` FSM:
      `Idle→Docking→Emptying→MopWashing→MopDrying→DetergentDosing→Ready` on
      fixed per-stage durations (sum 215 s), with resource preconditions (bag
      room / clean water / detergent) dropping the cycle to `Fault`
      (`bin_full`/`no_clean_water`/`no_detergent`), `reset` recovery, and the
      **svc-dock coordination signals** `bayOccupied()` (bay busy during the
      cycle) + `estimatedSecondsToReady()` (when the bay reopens). Fail-loud
      illegal-transition/non-monotonic-tick/out-of-range guards. 14 tests; suite
      68 green; tsc clean.)_
- [ ] (F) `svc-manipulation` — VLA policy serving (π0.5/OpenVLA-OFT
      per-embodiment adapters), grasp-confidence gating + fail-loud escalation,
      teleop→autonomy demo capture (LeRobot/OXE), handoff coordination.
      _(`apps/oya/svc-manipulation` BUILT: grasp-confidence gating
      (accept/escalate, never fabricate), contact-gated handoff FSM,
      per-embodiment VLA registry with an honest fail-loud
      `UnconfiguredVlaPolicy` (503 not_configured) seam; 35 tests. PENDING: a
      real π0.5/OpenVLA model backend + teleop demo capture.)_ _2026-09-18: the
      residue is a real policy behind the `not_configured` seam and the demo
      capture. Which vision-language-action model can run, where, and under what
      licence is OYA.G2's question; a 7B policy does not run on these CPUs._
      `blocked:upstream`
- [ ] (F) `svc-pet` — per-individual ID (face/weight/gait), behavior/affect
      state, pet-aware nav policy, treat-dispense + calorie budget + consumption
      verification, graded anxiety mitigation. _(`apps/oya/svc-pet` BUILT: real
      weight-based ID (tolerance bands, ambiguity fail-loud), behavior/affect
      FSM + graded anxiety-mitigation ladder, pet-aware nav policy
      (keep-out/slow/yield), two-phase calorie budget with consumption
      verification; 69 tests. PENDING: face/gait biometric ID model (honest
      `not_configured` seam in place).)_ _2026-09-18: the residue is the face
      and gait identification model; it waits for OYA.G1 and OYA.G2._
      `blocked:upstream`
- [ ] (F) `svc-eldercare` — multimodal fall fusion (radar+CSI+skeleton)
      trigger→confirm→escalate, closed-loop med adherence, proactive engagement
      engine, RAG-guardrailed conversation with refusal-to-advise router,
      cellular-backed emergency escalation. _(`apps/oya/svc-eldercare` BUILT:
      Bayesian log-odds fall fusion + trigger→confirm→escalate FSM,
      med-adherence tracker (due/missed/rate), engagement scheduler,
      deterministic fail-CLOSED refusal-to-advise router + fail-loud RAG seam;
      66 tests. PENDING: real RAG/LLM backend + cellular escalation transport.)_
      _2026-09-18: the retrieval and language backend waits for OYA.G5. The
      cellular escalation transport needs a modem and a carrier account, which
      are the owner's._ `blocked:upstream`
- [ ] (B) `svc-ambient` — Matter controller / Thread-1.4 border-router (HRAP);
      expose nodes as Matter Occupancy/Camera; control 3rd-party
      lights/locks/shades; sensing-escalation policy (when to dispatch a mobile
      eye). _(`apps/oya/svc-ambient` BUILT: device registry with real per-kind
      capability validation (lights/locks/shades/occupancy/camera) → Matter
      cluster command translation, the deterministic **sensing-escalation
      policy** (dispatch a mobile eye on ambiguous/anomalous/stale fixed-sensor
      readings, not on confident ones); 38 tests. PENDING: the real
      Matter/Thread transport (honest `not_configured` driver seam in place —
      never fabricates a lock ack).)_ _2026-09-18: open for an agent under the
      install-first rule: bind the driver seam to an open-source Matter
      controller (matter.js) and prove it against a virtual Matter device on the
      same host. **Verify:** commission the virtual device, toggle it, and read
      the acknowledgement the seam used to refuse to invent. A Thread border
      router needs a radio and is a hardware child of this item._
- [x] (E) `svc-energy` — Fleet Energy Manager: dock registry/broker (charging as
      auctionable resource), SOC/SOH aggregation, return-to-dock w/
      checkpoint-resume, battery-swap, predictive-maintenance flags; on-robot
      fail-safe fallback. _(`apps/oya/svc-energy` BUILT: dock registry/broker
      with lowest-SOC preemption auction, SOC/SOH fleet aggregation + endurance,
      return-to-dock decision (threshold-exact).)_ _(✓ COMPLETE — all four
      pending features added as pure, fail-loud, deterministic modules:
      **predictive-maintenance** `assessPackHealth` (SOH service/EOL +
      abnormal-fade + resistance-rise + cycle-limit flags →
      ok/monitor/service_soon/replace severity); **on-robot fail-safe fallback**
      `onRobotEnergyDecision` (defers to server when reachable; else a
      conservative SOC ladder
      continue→refuse_new_mission→return_now→emergency_park); **mission
      checkpoint/resume** `MissionCheckpointStore` (save/get/clear +
      `decideResume`: restart if absent/stale, abort if energy insufficient for
      the remainder, else resume); **battery-swap orchestration**
      `decideSwapVsCharge` (swap only when a pack is available, the robot is
      high-duty, and the swap beats charge-in-place time). 18 new tests; suite
      45 green; tsc clean.)_
- [ ] (G) `svc-hri` — full-duplex speech-to-speech core (Moshi-style
      multi-stream), HIVE-wide turn-taking/floor arbitration, per-embodiment
      affect rendering, mic-array speaker localization fusion, persona/trust per
      resident. _(`apps/oya/svc-hri` BUILT: HIVE-wide floor arbitration
      (priority barge-in, one-holder, fair queue), real TDOA mic-array speaker
      localization + bearing fusion, persona/trust model gating sensitive
      actions, affect rendering; 66 tests. PENDING: the Moshi-style
      speech-to-speech model (honest `not_configured` seam in place).)_
      _2026-09-18: the residue is the speech-to-speech model; it waits for
      OYA.G1 and OYA.G2, and a 7B duplex model needs a GPU the project does not
      have._ `blocked:upstream`

### 4.3 Edge & home-server runtimes

- [ ] (B) `libs/oya-edge-runtime` — Rust home-server binary: low-latency control
      hub, video archive, local vector DB/RAG, optional local LLM, dock/charging
      orchestration, **offline-first**. _2026-09-18: open for an agent, in four
      parts, because this line is four deliverables: (a) the binary, its
      configuration and the offline-first control hub; (b) the video archive;
      (c) the local vector store and retrieval; (d) the dock and charging
      orchestration hook. Create `libs/oya-edge-runtime` as a Cargo crate first
      and add the path filter the premerge workflow deferred until it existed.
      **Verify:** each part by a crate test, and the hub keeps serving with the
      network interface down._
- [ ] (C) On-robot edge runtime image (aarch64) packaging `oya-engine` native +
      perception models + wake-word/VAD + safety FSM; works fully offline.
      _2026-09-18: open for an agent under the install-first rule: build the
      aarch64 image by cross-compilation (`cross` or a qemu builder), with no
      model inside until OYA.G2 admits one. **Verify:** the image boots under
      qemu, the safety state machine starts, and nothing in it dials out._
- [ ] (C) **Home-server failover** (§15.3): UPS-backed appliance, edge-elected
      backup brain, life-safety path fully on-edge with cellular egress.
      _2026-09-18: the software half is open: election of a backup brain among
      the edge units and a life-safety path that runs without the home server.
      **Verify:** in the dev stack, killing the home-server process elects a
      backup within the stated deadline and a simulated fall still escalates.
      The uninterruptible supply and the cellular egress are hardware._

### 4.4 Product surfaces

- [ ] (H) `bff` — Fastify BFF: aggregation, response shaping, streaming/SSE,
      client-type routing (reuse lilith bff pattern).
- [ ] (H) `web` — Next.js dashboard: live map, video, missions, fleet,
      telemetry, **privacy controls UX** (no-go/no-record zones,
      recording/consent, retention).
- [ ] (H) `mobile` — React Native companion + manual controller.
- [ ] (H) Onboarding/setup flow: guided first-map walkthrough, room/zone
      labeling, dock/anchor placement guidance with UWB GDOP feedback, "major
      change → re-survey?" (§15.6).

---

## 5. Capability lines (cross-cutting feature checklists)

> Each line is a software+skill bundle over the shared substrate. Robot classes
> reused across lines. Tasks here are the line-specific features beyond the
> generic crate/service work above.

### 5.1 Line 1 — Floor care (mop/sweep/vacuum) — bar: Roborock Saros 10R

- [ ] StarSight-pattern solid-state-LiDAR+3D-ToF+RGB fusion perception pipeline;
      100–200-class on-device classifier (classify-then-discard image).
- [ ] Pet-waste as a hard-avoid class with conservative margins.
- [ ] <80 mm under-furniture profile handling; AdaptiLift/NeverStuck threshold
      logic.
- [ ] Omni-dock cycle (cyclone self-empty, hot-water wash+dry, detergent dosing,
      IR+fiducial alignment).
- [ ] **Wet-floor/drying shared map layer** + occupancy-gated mopping (§15.2).
- [ ] Readiness gate: provable coverage completeness; obstacle-avoidance
      benchmark ≥ Saros 10R.

### 5.2 Line 2 — Fetching small objects — bar: Toyota HSR / beat OK-Robot 58.5% / DynaMem 70%

- [ ] find→navigate→grasp→deliver decomposition; semantic place memory.
- [ ] Per-object-class **honest capability boundaries** + task-level confidence
      gating (§15.6).
- [ ] Floor-pickup tip-over handling; high-shelf reach limits; teleop SLA +
      privacy model.
- [ ] Medication: prefer mobility-as-payload + fixed verified dispenser over
      loose-pill grasp (§15.6).
- [ ] **Payload securement** (lidded/gimbaled carriers; accel limits; never over
      person/stairs; continuous retention verify; dropped-payload = logged
      event) (§15.2).

### 5.3 Line 3 — Home patrol & security — bar: Knightscope-class fusion, beat false-alarm fatigue

- [ ] Learned-normalcy anomaly detection (RGB+thermal+LiDAR/sonar+audio); not
      fixed motion alarms.
- [ ] Multi-stage trigger→confirm→escalate (cheap RF trigger → camera unit
      confirms before alarming).
- [ ] Human-in-the-loop SOC escalation; MCAP incident logging + replay
      (Foxglove/Formant).
- [ ] **Night low-noise patrol mode** (radar/CSI/thermal, no vacuum/props)
      (§15.2).
- [ ] Acoustic-event service hook (glass break, smoke/CO) (§15.7).

### 5.4 Line 4 — Companion + presence — bar: ElliQ engagement + Moshi full-duplex

- [ ] Full-duplex speech-to-speech (Moshi/Mimi multi-stream, ~200 ms,
      barge-in/backchannel).
- [ ] Predictive multimodal turn-taking + HIVE-wide floor arbitration (one
      responder per human).
- [ ] LLM-driven facial affect (FACS blendshapes / OLED-eye) + anticipatory
      coexpression.
- [ ] Proactive engagement engine (goals × availability × success-probability).
- [ ] Per-resident persona drift + long-term episodic memory; core loop runs
      edge+home-server (Moxie lesson).
- [ ] Mic-array DoA → orient-to-speaker.

### 5.5 Line 5 — Pet interaction — bar: beat Furbo/Petcube with mobile local-first

- [ ] Pet-specialized detection; per-individual collarless ID
      (face+weight+gait).
- [ ] Markerless pose (DeepLabCut/SLEAP) + behavior-syllable segmentation;
      bioacoustic bark/meow emotion.
- [ ] Treat launcher with **closed-loop consumption verification** + fleet-wide
      calorie budget.
- [ ] Laser play gated behind opt-in + hard eye-avoidance + capped duration
      (avoid laser-pointer syndrome).
- [ ] Pet-aware social navigation (never-corner/escape-route; sleeping/eating
      soft no-go) (§15.2).
- [ ] Per-pet persistent health record; separation-anxiety graded-intervention
      FSM → escalate to human.

### 5.6 Line 6 — Elder-care & health — bar: Vayyar Care fall detection + ElliQ companionship

- [ ] Sensor-fusion fall pipeline (mmWave + CSI + ST-GCN skeleton) → one
      home-level belief; trigger→confirm→cancelable-countdown→escalate.
- [ ] Closed-loop medication adherence (dispense + ingestion verification;
      escalate on confirmed miss only).
- [ ] RAG-grounded **guardrailed** LLM with refusal-to-advise routing (never
      unguarded medical advice).
- [ ] rPPG/radar contactless vitals; gait-speed/sit-to-stand deterioration
      biomarkers.
- [ ] **Fall PREVENTION**: proactively clear/flag trip hazards; surface decline
      trends to caregivers (§15.7).
- [ ] Emergency escalation orchestrator (fall→countdown→two-way
      voice→caregiver→EMS, cellular fallback, cancel, audit);
      **offline/anti-orphaning** path.
- [ ] **Accessibility** modes (large-button/pendant, hearing-impaired
      visual/haptic, dysarthria-robust ASR, dementia-appropriate; validate
      fall/gait on wheelchair/walker users) (§15.4).

### 5.7 Line 7 — Persistent home mapping — bar: iRobot Imprint, exceed with open-vocab fleet-shared graph

- [ ] Three-layer map served by `svc-home-map`; on-prem-by-default storage.
      _(`svc-home-map` serves the **scene-graph layer** (in-memory,
      local-first/on-prem) and the **geometric layer** exists in `oya-mapping`
      (occupancy + pose-graph). PENDING: the server-side 3DGS layer + unified
      three-layer serving.)_
- [x] Change-aware queries ("what changed in the living room since yesterday").
      _(`svc-home-map` `GET /v1/changes?since=T` — added/moved/removed instances
      since a tick with freshness + displacement, room-scopable.)_
- [x] **Honesty gate**: low-relocalization-confidence/stale `last_seen` → "I
      last saw it 3 days ago, not sure" (not a confident wrong location).
      _(`svc-home-map` fail-loud spatial query returns `fresh|stale|unknown`
      with exp-decayed confidence + `lastSeen` — never fabricates a confident
      location for a decayed instance; also the `@oya/readiness-gates`
      map-freshness gate.)_
- [ ] Multi-floor + multi-robot pose-graph fusion (Hydra-Multi/Kimera-Multi),
      bandwidth-aware descriptor exchange.
- [ ] **Unified "find my things"** capability (scene-graph last_seen + UWB/BLE
      tags + on-demand eyes) (§15.7).

### 5.8 Line 8 — Energy management & docking — bar: WiBotic + OTTO banding

- [ ] Dock-as-shared-resource broker; PRCP-sized dock count (not 1:1).
- [ ] Many-to-many charging handshake (robot-ID); precision self-docking
      (AprilTag coarse → Nav2 precision → funnel → pogo).
- [ ] Energy-aware locomotion integration (roll-vs-fly-vs-perch per-edge cost).
- [ ] Battery-swap for top-duty units; UAV perch-overwatch missions.
- [ ] Beat silent-dead-on-floor stranding (conservative reserve + free-dock
      targeting).

### 5.9 Line 9 — Ambient / smart-home — bar: Aqara FP2/FP300, exceed with cross-modal track

- [ ] Tiered sensing: always-on cheap fixed nodes
      (PIR+mmWave+lux/temp/humidity+mic, point-cloud only) + on-demand mobile
      RGB-D/thermal eyes.
- [ ] Cross-modal identity track with sensor handoff (beat false-absence on
      stationary occupants).
- [ ] Matter-1.5/Thread-1.4-native controller; PIR/Wi-Fi-gated radar wake;
      Matter LIT battery nodes.
- [ ] Federated learning + differential privacy across homes; **federated
      home-archetype priors** (§15.7).
- [ ] **Whole-home hazard monitor** product capability
      (stove/fridge/faucet/open-door-at-night) (§15.7).

### 5.10 Line 10 — Lab co-pilot & filmmaking director — bar: Figure Helix dual-system

- [ ] Lab co-pilot: procedural task graphs (SMD/through-hole soldering, reflow,
      ESP32/STM32 bring-up, I²C/SPI debug).
- [ ] Egocentric visual grounding (VLM watches hands+board: "is this joint
      cold?").
- [ ] Datasheet RAG (OCR part number → retrieve datasheet →
      pinout/voltage/timing hands-free).
- [ ] Hazard alerts (hot iron, fumes/ventilation, ESD, shorted board);
      collaborative multi-step reasoning.
- [ ] **Filmmaking director own spec** (§15.6): shot-type library
      (dolly/orbit/crane/reveal), composition/aesthetic scoring, multi-unit
      coordinated filming + angle handoff, subject-tracking-for-framing
      (distinct from collision avoidance), gimbal/exposure/focus, director-LLM
      intent→shot-sequence.
- [ ] Director preference loop: executed shot → accept/reject →
      `director-preference-learning` update; Yemaya dailies/conform/timeline
      ingest.

---

## 6. Intelligence stack (shared)

### 6.1 Voice (D)

- [ ] Wake-word model (Picovoice-class on-device) wired to `svc-voice-pipeline`
      VAD. _(`oya-dsp::{vad,wake_word}` BUILT: real VAD
      (energy/ZCR/spectral-flatness + adaptive noise floor + hangover) and a
      deterministic wake-word energy/NCC pre-gate. PENDING: the Picovoice-class
      neural wake-word model + the svc-voice-pipeline service.)_ _2026-09-18:
      the residue is the neural wake-word model, which waits for OYA.G1 and
      OYA.G2 (openWakeWord is Apache-2.0). The service it wires into exists as
      `apps/lilith/svc-voice-pipeline`._ `blocked:upstream`
- [ ] Barge-in: interrupt-and-restart logic in stream handlers (abort TTS on
      speech). _(`oya-dsp::barge_in` BUILT: detects near-end speech during
      far-end playback (post-AEC residual VAD, sustained-run rising-edge) →
      barge-in event to duck/stop TTS. PENDING: wiring into the
      svc-voice-pipeline stream handlers (restart logic).)_ _2026-09-18: open
      for an agent now: the stream handlers are in
      `apps/lilith/svc-voice-pipeline`. **Verify:** a service spec plays
      synthetic far-end audio, injects near-end speech, and asserts that
      synthesis stops within the latency budget and that the turn restarts._
- [ ] Reuse `svc-stt` (whisper/`whisper-asr-bridge`) + `svc-tts`; edge ASR
      (Moonshine/Parakeet) for offline. _2026-09-18: open for an agent now for
      the reuse (`apps/lilith/svc-stt`, `apps/lilith/svc-tts`,
      `libs/oya/core/src/whisper-asr-bridge.ts`). An edge recogniser for offline
      use waits for OYA.G1 and OYA.G2 (Moonshine is MIT)._
- [x] Acoustic echo cancellation; far-field mic-array beamforming (via
      `oya-dsp`). _(`oya-dsp::aec` — real **NLMS** adaptive echo canceller
      (residual energy drops ~22 orders over a synthetic far→echo→near setup,
      taps converge to the true echo path), plus the existing
      `oya-dsp::beamforming` delay-and-sum far-field array. 68 dsp tests.)_

### 6.2 LLM / agent (D)

- Moved to section 0.3 as **OYA.G5** (2026-09-18), so that the ground items are
  worked first; the box and its history are there.
- [ ] Oya tool-set: `flyTo`, `orbit`, `perch`, `lookAt`, `readLabel`,
      `recordClip`, `setReminder`, `queryDatasheet`, `fetch`, `cleanZone`,
      `patrol`, `dispenseTreat`, `escalate`. _2026-09-18: open for an agent now:
      `svc-assistant` already has the typed tool registry and dispatch.
      **Verify:** each of the thirteen tools has a schema, delegates to its
      service behind the `oya-safety` gate, and refuses when the gate refuses;
      none emits a motor command._
- [ ] Reuse `svc-ai/agent-executor` + `agent-coordination`; define Oya agent
      roles (FleetOrchestrator, DroneDirector, LabCoPilot, CareDispatcher).
      _2026-09-18: reuses `apps/lilith/svc-ai`; the roles need a model to run
      and wait for OYA.G5._ `blocked:upstream`
- [ ] RAG: `iris/knowledge` (GraphRAG/agentic-rag) + `iris/conversation-rag` +
      `sophia/semantic-search`. _2026-09-18: the libraries exist
      (`libs/iris/knowledge`, `libs/iris/conversation-rag`,
      `libs/sophia/semantic-search`); binding them needs the model seam of
      OYA.G5._ `blocked:upstream`
- [ ] Memory: `svc-conversation` session store + long-term vector store (home
      server). _2026-09-18: open for an agent now for the session store
      (`apps/lilith/svc-conversation`); the long-term vector store lives on the
      home server and follows the edge runtime item of section 4.3._
- [ ] **Prompt-injection defense** (§15.5): perception/voice inputs cannot
      escalate privilege or trigger high-consequence actuation without
      supervisor + confirmation gate. _2026-09-18: open for an agent now, and it
      is deterministic. **Verify:** adversarial fixtures (text in a camera
      frame, a spoken command from an unknown voice, a tool result carrying
      instructions) cannot raise privilege or reach a high-consequence tool
      without the supervisor and the confirmation gate; each refusal is logged
      with its source._

### 6.3 VLA / world model (F/H)

- [ ] System-2 reasoner (Gemini-Robotics-ER/π0.5-class) hierarchical inference →
      language subtasks.
- [ ] System-1 flow-matching/diffusion action expert (on-robot, ~50 Hz)
      conditioned on System-2 latent.
- [ ] Cross-embodiment base policy + thin per-embodiment heads (OpenVLA-OFT
      recipe).
- [ ] Speculative edge-verifier decoding + Real-Time Chunking for jitter
      tolerance.
- [ ] World model: imagined rollouts (MPC), home synthetic-scenario generation
      (GR00T-Dreams), closed-loop policy eval — bounded horizon, validated vs
      real map.
- [ ] Model-based 6-DoF grasp fallback always available under the VLA.

### 6.4 Skill library & lifelong learning (H)

- [ ] Teleop→autonomy data flywheel (Expert-Mode/Mobile-ALOHA), operator-side
      face blur + owner consent.
- [ ] Demo logging in Open-X-Embodiment/LeRobot format; per-home fine-tune (LoRA
      adapters) from 50–100 demos.
- [ ] Lifelong skill library (LRLL distillation) with catastrophic-forgetting
      guards (replay/retrieval/adapters).
- [ ] Habitat 3.0 / HSSD-200 / OVMM sim harness as CI for perception+policy
      before hardware rollout.
- [ ] Sim-to-real: accurate actuator modeling (delays/torque/friction) + domain
      randomization.

---

## 7. Distributed perception & "eyes that come to you" (B/C)

- [ ] Fixed-node semantic emission (skeletons/clusters/occupancy deltas over the
      wire, never pixels — Bonn pattern). _2026-09-18: open for an agent now.
      **Verify:** the wire schema in `libs/contracts/src/oya` admits skeletons,
      clusters and occupancy deltas and has no field that can carry pixels; a
      spec refuses a payload that tries._
- [ ] On-demand mobile-eye dispatch policy (nearest suitable unit; drone only if
      elevated/blocked); energy-aware. _2026-09-18: open for an agent now; it is
      a policy over the fleet and energy crates. **Verify:** table-driven cases:
      nearest suitable unit wins, a drone is chosen only when the target is
      elevated or the route is blocked, and a unit below its reserve is never
      chosen._
- [ ] Cooperative cross-modal fusion (HeCoFuse-style) — camera-drone +
      LiDAR-rover joint detection; uncertainty/pose-error-aware; degrade to
      onboard-only on mesh drop. _2026-09-18: learned cross-modal fusion waits
      for OYA.G1 and OYA.G2. The degrade-to-onboard rule on a mesh drop is
      deterministic and can be built first._ `blocked:upstream`
- [ ] Mobile robot down-weighted as noisiest node; ICP-aligned before
      integration. _2026-09-18: open for an agent now. **Verify:** a fusion test
      in which the mobile unit's observation carries the larger covariance and
      is ICP-aligned before it is integrated; an unaligned scan is refused._
- [ ] UWB ~10 cm "go to where this person is"; BLE Channel Sounding fallback.
      _2026-09-18: open for an agent now on `oya-estimation::positioning`.
      **Verify:** a simulated tag at a known position is reached within 10 cm in
      the planner test, and the Bluetooth fallback engages when ranging drops
      out._
- [ ] **Home-wide acoustic-event localization** service (§15.7) triggering
      nearest eye. _2026-09-18: localisation from the microphone array exists;
      classifying the event (glass break, smoke alarm) is a model and waits for
      OYA.G1 and OYA.G2._ `blocked:upstream`

---

## 8. Modular hardware _software_ (OyaLink) (A/C)

- [ ] `CapabilityManifest` driver hot-load: on dock, module reports
      mass/CoM/geometry/power/capabilities/driver-handle → body updates dynamics
      model + agent action set **live** (before moving). _(✓ the deterministic
      engine deliverable is COMPLETE: the **agent action set** half is
      `oya-locomotion::capability_passport::CapabilityRegistry` —
      `dock`/`undock` a module's manifest
      (driver-handle/mass/CoM/power/capabilities) recompute the live union
      action set, `has_capability`, `drivers_for` (which driver serves a
      capability), and total power/mass; the **dynamics model** half flows from
      the SAME manifests via `to_component_masses` →
      `morphing_estimation::MorphingEstimator::update_layout` (mass-weighted
      CoG + parallel-axis inertia), so both update on dock **before moving**. 4
      domain tests (dock/undock updates action set live, duplicate-handle
      replaces, multi-driver capability union+resolution, and the dynamics
      bridge: two symmetric masses → CoG at origin + Iyy=Izz=2·m·d²). fmt +
      clippy `-D warnings` clean; zero stubs. PENDING (deployment/runtime, not
      an engine algorithm): the literal hot-loading of the driver *code* keyed
      by `driver_handle`.)_ _2026-09-18: open for an agent now: a driver
      registry keyed by `driver_handle` that loads and unloads a driver at run
      time. **Verify:** docking a module in the test loads its driver, extends
      the agent's action set before any motion command is accepted, and
      undocking removes both._
- [x] Foldable rotor-arm deploy/latch control +
      fold-detect-refuses-to-arm-until-locked. _(`oya-locomotion::foldable_arm`
      — a per-arm deploy/latch FSM
      (`Folded→Deploying→Deployed→Latched`/`Retracting`/`Fault`) driven purely
      by deploy-angle + latch sensors (the FSM validates + gates; it never
      drives latch solenoids or arm motors, mirroring the reconfiguration
      planner's no-low-level-commands rule). It fails **safe** on every
      sensor/motion inconsistency — a latch engaging before the arm is deployed
      (false latch), a `Latched` arm's latch slipping, a deployed arm drifting
      off its angle without a retract command, an unhealthy latch sensor, or an
      out-of-range angle all drop the arm to a sticky `Fault`; retract is
      refused while latched (requires `command_unlatch` first). The headline
      **fold-detect-refuses-to-arm-until-locked** interlock is
      `FoldableArmSystem::can_arm`: it returns `Ok(())` **only** when every arm
      is `Latched`, else a fail-loud `ArmingRefusal` naming the exact offending
      arm + its state/fault — a partially-deployed quad (3 latched, 1 still
      `Deploying`) is refused, naming arm 3. 12 domain tests (full deploy→latch
      sequence, each fault path, retract-needs-unlatch,
      interlock-gates-until-all-latched, faulted-arm-blocks-arming); fmt +
      clippy `-D warnings` clean; zero stubs. The physical
      latch-solenoid/arm-motor drivers are the hardware actuator layer (out of
      scope).)_
- [x] Unfolding telescoping-arm stow/deploy control (no IK singularities).
      _(`oya-locomotion::telescoping_arm` — a planar telescoping arm (base
      rotation `θ` + prismatic extension `L`) modelled with its polar forward
      kinematics `ee = L·(cosθ, sinθ)` and velocity Jacobian
      `J = [[cosθ, −L sinθ],[sinθ, L cosθ]]`, whose `det J = L` is **singular
      exactly at `L = 0`** (full retraction): manipulability `w = |det J| = L`
      and condition number `κ = max(1,L)/min(1,L)` (verified to diverge as
      `L→0`, best=1 at L=1). The control is **singularity-free by construction**
      — the arm stows at `min_length ≥ singularity_floor > 0`, so the whole
      operating stroke `[min_length, max_length]` is well-conditioned
      (`worst_case_manipulability > 0`), and `plan_deploy`/`plan_retract` emit a
      **monotone cosine-eased** length profile through that range, never
      approaching `L=0`. Fail-loud `check_target`/plan refuse over-extension and
      sub-stow (singular-adjacent) targets with typed `TelescopingError`. Like
      the reconfiguration planner it emits validated set-points only (no
      prismatic-actuator drive). 9 domain tests: polar FK, `det J = L` across
      angles, κ-divergence, whole-stroke-singularity-free, monotone+smooth
      deploy/retract every-waypoint-well-conditioned, out-of-stroke refusals.
      oya-locomotion 88 tests; fmt + clippy `-D warnings` clean; zero stubs.)_
- [x] Perch latch control (claw/gecko/suction) + motors-off overwatch.
      _(`oya-locomotion::perch_latch` — a perch-attach state machine
      (`Flying→Latching→Latched→Overwatch`/`Fault`) with the safety-critical
      don't-drop-the-drone invariant: **motors may be cut only while the latch
      verifiably holds the weight with margin, and the latch released only after
      the motors are back at hover thrust**. `command_motors_off` is fail-loud —
      refused unless `Latched` with the last measured hold ≥
      `safety_factor·weight` (typed `NotLatched`/`InsufficientHold`);
      `command_release`/`command_respin` refuse below hover thrust
      (`MotorsBelowHover`); a held perch whose hold decays below the drone's
      weight (esp. in motors-off `Overwatch`) drops to `Fault`. Per-mechanism
      hold-force physics decide whether a measured latch state actually carries
      the load: `claw_hold_force` (form-closure grip = clamp force),
      `gecko_hold_force` (directional dry adhesion `shear_pressure·area`, **zero
      until a normal preload engages the setae**), `suction_hold_force`
      (`F=ΔP·A`) — hand-computed-value tests (gecko 50 kPa·20 cm²=100 N, suction
      60 kPa·10 cm²=60 N). Validates+gates only; never drives latch/motor
      actuators. 12 domain tests (full perch→overwatch→respin→release cycle,
      motors-off refused until latched-with-margin, hold-decay refusal,
      latch-slip-in-overwatch fault, release/respin hover gates, per-mechanism
      parity). oya-locomotion 100 tests; fmt + clippy `-D warnings` clean; zero
      stubs.)_
- [x] Inter-unit docking (EPM self-align) + cooperative-transport handoff with
      **success verified before release**. _(`oya-locomotion::inter_unit_dock` —
      two pieces. **EPM self-aligning dock** (`EpmDockController`,
      `Approaching→Aligned→Latching→Docked→Undocking`/`Fault`): the
      **electropermanent** magnet latches/releases on a current *pulse* but
      **holds with zero standing power** (`holds_power_free()` true once
      `Docked`), and the latch is energized **only inside the passive self-align
      capture envelope** (`within_capture_envelope`, lateral + angular tol) —
      `command_energize_latch` is fail-loud `NotAligned` otherwise; the joint is
      `Docked` only once the magnetic hold is verified ≥ required, and a docked
      joint that loses its hold faults. **Cooperative transport handoff**
      (`CooperativeHandoff`,
      `GiverHolding→Transferring→ReceiverSecured→Complete`/`Fault`) enforces
      **success-verified-before-release**: `command_giver_release` is refused
      (`ReceiverNotSecured`) until the receiver verifiably holds
      `secure_factor·weight`, and the **continuous-retention** invariant faults
      the handoff if the combined giver+receiver grip ever falls below the
      payload weight (never a moment where nobody carries the load) — a receiver
      that slips back below the margin after securing is caught before release.
      Validates+gates only; never drives magnet currents/grippers (consistent
      with the reconfiguration planner's rule). 9 domain tests (full dock cycle
      power-free hold, latch-refused-out-of-envelope, alignment-lost-reverts,
      docked-hold-loss fault; full handoff verify-before-release,
      release-refused-until-secured, continuous-retention-loss fault,
      secured-then-slips caught). oya-locomotion 109 tests; fmt + clippy
      `-D warnings` clean; zero stubs.)_
- [x] Deterministic, gravity-/stability-checked reconfiguration planner (balance
      enforced at every intermediate step); agent never commands magnet
      currents/latches directly. _(`oya-locomotion::reconfiguration` — validates
      a reconfiguration sequence (fold arm / deploy leg / dock-undock module)
      keeps the body **statically stable at every intermediate step**: each
      step's **support polygon** is the convex hull (Andrew's monotone chain) of
      the ground contacts, and the **stability margin** is the min signed
      perpendicular distance from the CoG to a hull edge (positive = inside).
      `ReconfigurationPlanner::validate` returns `stable` /
      `first_unstable_step` / `min_margin` / per-step margins; a degenerate
      point/line support is fail-loud unstable. The planner emits **validated
      high-level steps only** — never magnet currents/latches (that's the
      separate actuator layer). 6 domain tests: hull drops interior points,
      centred-square margin = 1.0 / boundary = 0 / outside < 0, two-contact line
      unstable, **a balanced shift-then-lift plan accepted** while a
      **lift-leg-too-early plan is rejected at the exact tipping step**.
      oya-locomotion 63 tests; fmt + clippy `-D warnings` clean; zero stubs.)_

---

## 9. Safety, privacy, security, compliance

### 9.1 Safety (cross-phase)

- [x] Indoor flight safety: ducted/caged-prop logic, speed caps, room-boundary
      geofence, emergency-land + watchdog (on-drone, offline), prop-stop on
      contact. _(`oya-safety::flight_safety`: direction-preserving speed caps,
      room-boundary geofence with proportional inward braking (stops at the
      wall), clock-free emergency-land **watchdog** FSM (offline/on-drone),
      latched **prop-stop on contact** (force/proximity). 61 crate tests.
      Ducted/caged props are the hardware form-factor; the software safety logic
      is complete.)_
- [x] Mobile-base SSM + manipulator PFL enforced by `oya-safety` on every
      actuation. _(`oya-safety::actuation_gate::ActuationSafetyGate` — the
      mandatory enforcement seam that caps **every** motion command:
      `gate_mobile_base` applies the **SSM** speed cap (`max_robot_speed`,
      R15.08/ISO 10218-2) for the current human separation — forcing the base to
      **STOP** (cap 0) when even a stationary robot can't meet the protective
      separation; `gate_manipulator` applies the **PFL** biomechanical cap
      (`max_speed_for_region`, ISO/TS 15066) scaling TCP speed to the contact
      body-region limit; `gate_mobile_manipulator` does both. Fail-safe: each
      decision returns the allowed (reduced/zeroed) speed +
      `vetoed`/`scaled`/`basis`. 5 domain tests: base allowed-far / scaled-mid /
      **STOP-close**, manipulator capped by region (skull < hand), heavier robot
      ⇒ lower PFL cap, mobile-manipulator gates both subsystems. oya-safety 112
      tests; fmt + clippy `-D warnings` clean; zero stubs.)_
- [x] **Child-as-distinct-hazard** model (§15.2): choking interlocks on
      dispensers, manipulator/drone lockout near unsupervised children,
      ride-on/tip resistance, child-height coverage, yields-when-grabbed.
      _(`oya-safety::child_safety`: small-parts (⌀31.7mm) choking interlock,
      supervision gate (manipulation/keep-out lockout near an unsupervised
      child, adult-within-radius logic), CoG tip-resistance, child-height
      coverage check, yields-when-grabbed force/torque compliance. 31 tests, all
      fail-closed.)_
- [x] Stairs/cliff/threshold edge-safety on all ground units.
      _(`oya-safety::edge_safety`: cliff/drop detection from downward range
      sensors (worst-drop governs: BackOff/Stop/Proceed, fail-safe Stop on
      dead/empty sensors) + rising-edge threshold climb-limit check.)_

### 9.2 Privacy (cross-phase)

- [x] On-device face/PII redaction (unrecognized-person redaction by default).
      _(`@oya/privacy::redaction`: fail-closed default-redact — any
      UNRECOGNIZED/low-confidence/non-allowlisted region is blurred/black-boxed
      (real masking transform); only allowlisted recognized ids pass. Face
      detector = ML input seam.)_
- Hardware recording indicator (LED+chime) in series with camera power; physical
  shutter/privacy perch. _Out of scope (2026-09-18): this file's header leaves
  physical fabrication to the hardware workstream, and an indicator wired in
  series with camera power is hardware._
- [x] Per-room/zone no-fly & no-record enforcement in planner **and**
      perception. _(`@oya/privacy::no-record-zones`: ray-cast point-in-polygon +
      segment-crossing; `enforcePlan` rejects/clips a path entering a no-fly
      zone (planner) and `enforceCapture` drops/redacts captures intersecting a
      no-record zone (perception), fail-closed.)_
- [x] Local-first storage, explicit cloud opt-in, retention limits, guest mode.
      _(`@oya/privacy::storage-policy`: classify local-only vs cloud-eligible
      (cloud only with explicit opt-in; biometrics never leave the box),
      retention-limit expiry, guest-mode recording suspension.)_
- [x] **Bystander/visitor consent** (§15.4): audio two-party-consent gating,
      audit logs, mandatory teleop operator-side blur + owner-consent gate.
      _(`@oya/privacy::consent`: all-party consent gate (audio refused unless
      every present party consented), append-only audit log, mandatory teleop
      operator-side blur + owner-consent precedence.)_

### 9.3 Security (I)

- [ ] Secure boot + signed A/B-rollback OTA; per-device X.509 identity; CVD
      policy. _(`@oya/security::device-identity` BUILT: per-device Ed25519
      identity + self-describing certs + sign/verify + revocation. PENDING:
      secure-boot, signed A/B-rollback OTA, CVD policy
      (hardware/process/deployment).)_ _2026-09-18: three residues. Signed A/B
      update manifests with rollback are software and open now (**Verify:** a
      tampered manifest is refused, a failed boot rolls back). The
      coordinated-disclosure policy is a document. Secure boot needs the target
      board._
- [ ] mTLS everywhere; E2E-encrypted media; per-home tenancy. _(`@oya/security`
      BUILT: **E2E media encryption** (AES-256-GCM AEAD, tamper-detecting) +
      **per-home tenancy** (HKDF per-tenant keys, fail-closed cross-tenant
      assertion). PENDING: mTLS wiring across all service transports
      (deployment).)_ _2026-09-18: open for an agent now in the dev stack: a
      local certificate authority, mutual TLS between the Oya services, and a
      test that a client without a certificate is refused._
- [x] **Expanded threat model** (§15.5): compromised brain cannot unlock doors
      without separate factor/physical confirmation; UWB/sensor anti-spoofing;
      Sidewalk/3rd-party backhaul as untrusted egress with consent.
      _(`@oya/security`: fail-closed **two-factor door-unlock gate** (brain-only
      refused; requires an independent pinned-key second factor;
      replay/forgery-resistant — Codex-hardened) + **UWB/sensor cross-modal
      anti-spoofing** (≥2 corroborating modalities). 34 tests.
      Backhaul-as-untrusted-egress-with-consent: the consent contracts exist;
      egress wiring pending.)_
- [ ] Run `/security-review` on each service before ship. _2026-09-18: run it
      per service as each is finished and commit the findings beside the
      service; it is not a single event._

### 9.4 Compliance & lifecycle (I)

- [ ] Remote ID (outdoor use), UL (battery/Qi), FCC/CE (RF), GDPR/CCPA
      (camera/audio data). _2026-09-18: registrations and certifications are the
      owner's acts with regulators and test houses. Where software produces the
      evidence (Remote ID broadcast, data-subject export and deletion), that
      software is its own task._ `blocked:governance`
- [ ] ETSI EN 303 645 / EU CRA conformance (SBOM, supported-lifetime, signed
      OTA). _2026-09-18: the software half is open: a CycloneDX SBOM per image,
      a stated supported lifetime, and the signed updates of the security
      section. The conformance assessment itself is a test house's._
- [ ] ISO 13482 / UL 3300 / ISO 10218 certification paths **sequenced per unit
      class** (don't gate whole fleet on hardest cert). _2026-09-18: choosing
      and sequencing certification paths is the owner's decision with a notified
      body._ `blocked:governance`
- [x] **Consumables & maintenance subsystem** (§15.6): per-unit consumable
      levels + wear tracking + loud service alerts; design-for-service;
      right-to-repair/spares stance. _(`@oya/maintenance`: consumable-level
      ledger (remaining%/ETA/ok-low-depleted), component wear + RUL +
      `needsService`, prioritized service alerts, design-for-service
      serviceable-part registry. 10 tests.)_
- [x] **Anti-bricking dignity guarantee** (§15.7): self-hostable brain, data
      export, no-bricking, anti-manipulative-engagement guardrails.
      _(`@oya/maintenance::dignity`: lossless `dataExport`/`importExport`,
      `assertNoBricking` (fail-loud on a cloud-gated core feature),
      `antiManipulativeEngagement` (rejects dark-patterns/over-frequency),
      `selfHostable` lock-in check.)_

---

## 10. Simulation, CI, eval, observability, infra

### 10.1 Simulation & test harness

- Moved to section 0.3 as **OYA.G3** (2026-09-18), so that the ground items are
  worked first; the box and its history are there.
- [ ] Gazebo/AirSim/jMAVSim integration (existing `*-integration.ts` modules) on
      the Linux box (not the Mac). _2026-09-18: open for an agent under the
      install-first rule: Gazebo and jMAVSim, headless, on the Linux server
      (check `free -m` first). AirSim was archived by its publisher; keep
      `airsim-integration.ts` compiling and do not invest in it. **Verify:** one
      scripted mission flies in each simulator through the existing integration
      modules._
- [ ] Isaac Sim/Isaac Lab GPU-parallel RL + Habitat 3.0/HSSD-200/OVMM harness as
      policy/perception CI. _2026-09-18: Isaac Sim and Isaac Lab need an
      RTX-class GPU; neither machine has one. Habitat's CPU path may serve
      perception CI; try it under the install-first rule as a separate task
      before parking that half._ `blocked:hardware`
- [ ] HIL rigs for manipulator force-limit and docking validation. _2026-09-18:
      a hardware-in-the-loop rig is hardware._ `blocked:hardware`
- [ ] **Capability-passport sim onboarding** (§15.7): rehearse new module in the
      home digital twin before physical use. _2026-09-18: rehearsing a module in
      the home's digital twin needs the simulator of OYA.G3._ `blocked:upstream`

### 10.2 Readiness-evaluator gates (extend existing 5)

- [x] Coverage completeness (floorcare); Grasp honesty (verified contact before
      success); Map freshness (non-fabricated `last_seen`).
      _(`@oya/readiness-gates`: coverage gate (BFS-reachable denominator),
      grasp-honesty gate (rejects success without verified contact + credible
      force), map-freshness gate (recomputes exp-decay, rejects
      future/over-fresh `last_seen`).)_
- [x] Energy reserve (verified reserve-to-free-dock); Safety conformance (ISO/TS
      15066 + R15.08; governor vetoes OOB command). _(`@oya/readiness-gates`:
      energy gate recomputes energy-to-nearest-FREE-dock + reserve per mission;
      safety gate re-derives SSM/PFL bounds and fails any un-vetoed
      out-of-bounds command.)_
- [x] Privacy enforcement (no-record disables/blurs sensors; tamper-evident
      indicators); Offline degradation (every safety loop survives cloud loss).
      _(`@oya/readiness-gates`: privacy gate (ray-cast no-record-zone membership
      → must disable/redact + tamper-evident, else fail); offline gate (any
      safety-critical loop with a required cloud dep → fail).)_
- [x] Mode-arbitration economy (rolls over flight when a ground path exists);
      **Economic viability gate** (§15.6, "pencils out at achievable volume").
      _(`@oya/readiness-gates`: mode-economy gate (flight chosen with a viable
      ground route → fail); economic-viability gate (real unit economics — unit
      cost/margin/contribution/payback vs target at achievable serviceable
      volume).)_

### 10.3 CI/CD & live-update safety

- [x] `oya-rust-premerge` + TS affected build/test/lint in Nx; differential-test
      suite required green. _(`.github/workflows/oya-ts-premerge.yml`:
      `nx affected` build/test/lint over `tag:scope:oya` + a REQUIRED
      differential gate (parity 91/91 + napi-bridge smoke) + aggregate required
      check; `pnpm oya:ts-premerge` local runner. Complements
      `oya-rust-premerge.yml`.)_
- [x] **Live-OTA safety** (§15.3): never update mid-task/sole-safety-asset;
      canary/staged rollout; shadow-eval before promotion; auto-rollback on
      regression; known-good resident.
      _(`@oya/readiness-gates::live-ota-safety`: refuses update mid-task / for
      the sole safety asset / without a passed canary / on a shadow-eval
      regression / without a resident known-good; `evaluatePostUpdateRollback`
      emits the auto-rollback decision on post-update regression. 20 tests.)_

### 10.4 Data, infra, observability

- [x] Event-bus topic registry for `oya.*` (telemetry, mission-state, fall,
      anomaly, dock, emergency). _(`@oya/event-publisher`: `OYA_TOPIC_REGISTRY`
      covering all six families — telemetry, mission(+state.changed),
      fall(detected/confirmed/cleared), anomaly.detected, dock(charge/swap),
      emergency(raised/escalated/resolved) — each a strict zod payload + typed
      publish helper, 37 tests.)_
- [ ] `@oshun/storage` buckets (mission data, telemetry archive, captured
      imagery, video segments). _2026-09-18: open for an agent now: the four
      buckets on the dev stack's MinIO through `@oshun/storage`, with retention
      classes. **Verify:** a spec writes and reads each class and refuses a
      write to an undeclared bucket._
- [x] `oya` DB schemas + migrations; per-domain isolation. _(`@oya/database`:
      real PostgreSQL DDL + ordered `MIGRATIONS` for
      missions/flights/telemetry/maps/fleet_state/consumables, zod row schemas,
      parameterized-SQL repositories over a pg-ready `QueryExecutor` seam. 25
      tests.)_
- [ ] `@oshun/metrics` + fleet observability (Foxglove/MCAP logging + replay);
      flight-data recorder; post-flight analysis. _2026-09-18: open for an agent
      now; MCAP is an open format with open libraries. **Verify:** a recorded
      simulated flight replays to the same telemetry, and the metrics the fleet
      dashboard needs are emitted with their units._
- [ ] Deployment: service Dockerfiles, edge aarch64 images, home-server
      appliance image, OTA pipeline. _2026-09-18: four deliverables: service
      Dockerfiles (open now), the aarch64 edge image (the item in section 4.3),
      the home-server appliance image, and the update pipeline (with the signed
      updates of section 9.3). Work them as four._

---

## 11. Roadmap phase exit-gates (unified A–I, plan §14)

- [x] **(A) Foundations** — `oya-mapping/scenegraph/comms/energy/safety` +
      `oya-types/math` + contracts + `svc-home-map` + shared coordinate frame;
      differential suite + `@oya/flight-control` green. _(all components
      delivered + green: the 5 hive crates
      (mapping/scenegraph/comms/energy/safety) + oya-types/math,
      `@oshun/contracts/oya` (shared Vec3/GPS coordinate frame), `svc-home-map`;
      differential parity suite 91/91; `@oya/flight-control` evaluator green +
      wired as a CI gate.)_
- **(B) Estimation/Perception/Ambient** —
  `oya-estimation/mavlink/perception/ambient` on shared map; `svc-ambient` +
  Matter/Thread; `svc-flight-gateway`; `@oya/telemetry` green; live SITL flight
  via stack. _Exit gate, not a task (un-boxed 2026-09-18): it closes when the
  items it lists are checked._
- **(C) Control/Locomotion/Manipulation** —
  `oya-control/navigation/locomotion/manipulation`; `oya-safety` mandatory gate
  on all actuation; `@oya/safety` green; follow-me + obstacle avoidance + safety
  FSM. _Exit gate, not a task (un-boxed 2026-09-18)._
- **(D) Mission/Fleet/Assistant/Director** — `oya-swarm`→`oya-fleet`;
  `svc-fleet-orchestrator`; `svc-assistant` System-2; voice
  (wake-word/barge-in/duplex); `svc-director`; end-to-end voice→action in sim.
  _Exit gate, not a task (un-boxed 2026-09-18)._
- **(E) Dock/Energy** — `svc-dock`→dock-network; `svc-energy` +
  PRCP/opportunistic charging; autonomous perch+recharge cycle in sim/HITL.
  _Exit gate, not a task (un-boxed 2026-09-18)._
- **(F) Domain services** — `svc-floorcare/manipulation/pet/eldercare`;
  capability Lines 1–6,10 behind readiness gates; hands-free soldering demo;
  shot-list→aerial-shot→dailies ingest. _Exit gate, not a task (un-boxed
  2026-09-18)._
- **(G) HRI/Live** — full-duplex `svc-hri`, social navigation,
  supervised-autonomy/teleop console; `svc-live-stream`. _Exit gate, not a task
  (un-boxed 2026-09-18)._
- **(H) Learning flywheel + product surfaces** — teleop→autonomy pipeline,
  per-home fine-tune, federated+DP; `bff`/`web`/`mobile`; privacy UX shipped.
  _Exit gate, not a task (un-boxed 2026-09-18)._
- **(I) Hardening** — EN 303 645/CRA, ISO 13482/UL 3300 paths,
  `/security-review`, privacy audit, adversarial safety/privacy verification
  across the fleet, field trials, ship-readiness sign-off. _Exit gate, not a
  task (un-boxed 2026-09-18)._

---

_This is a planning checklist derived from
`OYA_RUST_REWRITE_AND_COMPANION_PLAN.md`. Every box is `[ ]` and unverified.
Mark `[x]` only after reading the specific code, confirming domain-correct
(non-stub) implementation, and running the specific tests for that task — one
task, one verification, one mark._
