[AZ-348] C3.5 ConditionalRefiner Protocol + factory + PassthroughRefiner

Defines the public `ConditionalRefiner` Protocol (PEP 544
@runtime_checkable, two methods: `refine_if_needed` +
`was_invoked`), extends `MatchResult` in-place with two
default-valued refinement fields (`refinement_label`,
`refinement_added_latency_ms`), defines the `RefinerError` family
(`RefinerBackboneError`, `RefinerConfigError`), and ships the
trivial `PassthroughRefiner` reference impl.

Both refiner strategies are linked unconditionally — no
`BUILD_REFINER_*` flag (NOT ADR-002 territory). Runtime selection
only per ADR-001. `PassthroughRefiner` returns the input
`MatchResult` by reference (bit-identical correspondences per
contract INV-5) and always reports `was_invoked() is False`.

Documentation: renames `module-layout.md` `c3_5_adhop` Public API
symbol from `AdHoPRefinementStrategy` to `ConditionalRefiner`
(AC-14) so the doc agrees with `description.md` and the contract.

AC-9 (single-thread binding) deferred to AZ-270 runtime-root
composition, mirroring AZ-336 / AZ-342 / AZ-344 Risk-4 precedent.
AC-7 for the `"adhop"` strategy stops at `ModuleNotFoundError`
because the AdHoP backbone is owned by AZ-349. All other ACs +
NFRs covered by 36 new conformance tests.

Architectural note: `PassthroughRefiner.inference_runtime` is
typed as `object` because the L3→L3 import ban
(`test_az270_compose_root`) forbids c3_5_adhop from importing
c7_inference; the runtime-root factory narrows the type at
construction time.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-12 05:52:36 +03:00
parent 89c223882b
commit 9a605c8514
13 changed files with 991 additions and 28 deletions
+9 -5
View File
@@ -89,11 +89,15 @@ Bootstrap reference: `_docs/02_tasks/todo/AZ-263_initial_structure.md`. Architec
- **Epic**: AZ-258 (E-C3.5 AdHoP Refinement)
- **Directory**: `src/gps_denied_onboard/components/c3_5_adhop/`
- **Public API**:
- `__init__.py` (re-exports `AdHoPRefinementStrategy`)
- `interface.py` (`AdHoPRefinementStrategy` Protocol)
- **Internal**: `default_refiner.py`
- **Owns**: `src/gps_denied_onboard/components/c3_5_adhop/**`, `tests/unit/c3_5_adhop/**`
- **Imports from**: `_types`, `helpers.ransac_filter`, `helpers.se3_utils`, `config`, `logging`, `fdr_client`
- `__init__.py` (re-exports `ConditionalRefiner`, `C3_5RefinerConfig`)
- `interface.py` (`ConditionalRefiner` Protocol)
- `config.py` (`C3_5RefinerConfig`)
- `errors.py` (`RefinerError`, `RefinerBackboneError`, `RefinerConfigError` — held internal to the component; consumers reach them only via tests)
- **Internal**:
- `passthrough_refiner.py` (reference baseline; AZ-348)
- `adhop_refiner.py` (production-default; AZ-349 pending)
- **Owns**: `src/gps_denied_onboard/components/c3_5_adhop/**`, `tests/unit/c3_5_adhop/**`, `src/gps_denied_onboard/runtime_root/refiner_factory.py`
- **Imports from**: `_types`, `helpers.ransac_filter` (R14: SHARED with C3 and C4 — owned by helper, NOT by C3.5), `helpers.se3_utils`, `components.c7_inference`, `config`, `logging`, `fdr_client`
- **Consumed by**: `c4_pose`, `runtime_root`
### Component: c4_pose