KB-3BBB

One-Roof Clone Axis/Topic — 05 Scanner/Candidate Integration (Obj D, PASS, idempotent)

3 min read Revision 1
one-roofnonprod-cloneaxis-substratescannercandidate-findingidempotentboundedno-daemon

05 — Objective D: Scanner / Candidate Integration

Verdict: PASS — committed. SQL: sql/D_scanner_candidate_integration_clone_COMMIT.sql. 9/9 checks PASS; idempotency and bounded growth proven.

What was added

  1. v_axis_coverage_summary — one row per active axis with required_cells / gap_cells / ok / low_conf / stale / quarantine. This is the scanner's summary metric surface (read contract in doc 07).
  2. An idempotent axis-finding scan (pg_temp.run_axis_scan()) that UPSERTs into the persistent clone-local axis_candidate_finding table from the quality/gap/missing views.

The integrated read path (what the scanner now sees)

axis required gap ok low_conf stale quarantine
responsibility 210 0 0* 0 0 0
topic 6 2 3 1 1 1

*responsibility coverage is owner-based, not assignment-based, so it has no assignment-quality rows — its health is the gap (0).

Findings written (clone-local, bounded)

finding_type severity count
missing_owner warning 2
low_confidence warning 1
stale_topic warning 1
quarantine warning 1
missing_assignment info 29
total 34

Idempotency + bounded growth (the load-bearing property)

The scan was run twice in one transaction:

  • pass 1 row count = 34; pass 2 row count = 34identical, no growth.
  • max(scan_count) = 2 → re-seen findings are updated (last_seen, scan_count+1), not duplicated — the PK (finding_type,object_type,object_ref,axis_code,value_code) + ON CONFLICT DO UPDATE makes the scan convergent.
  • The finding set is bounded by inventory × finding_types (a fixed ceiling), and missing_assignment is info-severity and capped at the unclassified-inventory count (29 ≤ 35). No object-grain explosion, no unbounded scan.

No daemon, no worker loop

The scan is a single deterministic pass invoked explicitly — there is no background worker, no pg_notify, no polling. Running it again is safe and converges. This matches the project's standing "no uncontrolled worker loop" rule: the cursor/worker machinery exists (gov_worker_cursor) but nothing here starts a loop; a future governed scheduler would call this same convergent function.

Relation to the existing candidate path

axis_candidate_finding is the axis analogue of governance_candidate_state/governance_candidate_object — a no-checked-forever, re-derivable finding store. It is kept separate (clone-local, additive) rather than overloading the existing candidate tables, so the existing scanner state is untouched and the axis findings can be dropped independently.

Back to Knowledge Hub knowledge/dev/reports/architecture/one-roof-nonprod-clone-axis-topic-substrate-pipeline-2026-06-02/05-scanner-candidate-integration.md