KB-7146

Branch A — P0 Post-Cut Axis Autowire Fix (FIXED, live) (2026-05-29)

6 min read Revision 1
iup0autowirefn_cut_completepost-cutbranch-a2026-05-29

Branch A — P0 Post-Cut Axis Autowire Fix

Doc 01 (2026-05-29) — Status: FIXED, committed live, reversible

1. Precise root cause (refined from the audit)

The audit said "post-cut axis autowire is not wired." Live investigation pinned the exact defect:

  • fn_cut_complete(p_cut_request_id uuid, p_actor text) finalizes a cut (status cut_verifiedcompletedcleanup_scheduled, signals cut.complete). It had no call to fn_iu_post_cut_axis_materialize. Confirmed: a scan of all PL/pgSQL functions found none referencing post_cut_axis_materialize except the materialize function itself, and no function calls fn_cut_complete (it is invoked externally).
  • A statement-level trigger trg_iu_three_axis_envelope_auto_refresh_iu (AFTER INS/UPD/DEL on information_unit, gated by iu_core.three_axis_auto_refresh_enabled) already refreshes the three-axis envelope on IU writes. So envelope refresh was NOT the gap.
  • The gap is specifically the axis-B tag application that only fn_iu_post_cut_axis_materialize performs: ensuring iu_metadata_tag_registry rows and applying doc:, kind:, sectype: tags to the cut's IUs. No trigger or function applied these automatically after a cut — only a manual DOT (iu.post_cut.axis_materialize) did. Hence "new cuts may not auto-refresh axis B."

Current drift: none. The two completed cuts (DIEU-39, DIEU-38) had their tags applied manually — DIEU-39: 16/16 IUs tagged; DIEU-38: 8/8 tagged. The bug is mechanism (manual, not auto), not present data corruption.

2. Deterministic doc_code resolution (the enabler)

fn_iu_post_cut_axis_materialize is keyed by doc_code; cut_request has no doc_code column. Discovery: information_unit.doc_code == cut_request.source_ref (both = e.g. knowledge/dev/laws/dieu39-knowledge-graph-law.md). Verified live:

cut_request source_ref IUs with matching doc_code already doc-tagged
e15cabe9 (DIEU-39) …dieu39…md 16 16
777b1297 (DIEU-38) …dieu38…md 8 8

fn_iu_post_cut_axis_materialize(v_req.source_ref, p_actor) is the correct, deterministic wire.

3. The fix (minimal, gated, best-effort, idempotent)

CREATE OR REPLACE FUNCTION public.fn_cut_complete(...) — identical to the original through the cut.complete signal, then inserts before RETURN:

IF (SELECT value = 'true' FROM public.dot_config
      WHERE key = 'iu_core.three_axis_auto_refresh_enabled') THEN
  BEGIN
    v_axis_autowire := public.fn_iu_post_cut_axis_materialize(v_req.source_ref, p_actor)
                       || jsonb_build_object('attempted', true);
  EXCEPTION WHEN OTHERS THEN
    v_axis_autowire := jsonb_build_object('attempted', true, 'ok', false, 'error', SQLERRM);
    RAISE WARNING 'fn_cut_complete: post-cut axis materialize failed for source_ref=% (cut %): %',
      v_req.source_ref, p_cut_request_id, SQLERRM;
  END;
END IF;

and adds 'axis_autowire', v_axis_autowire to the returned JSON. (Full DDL in Doc 07.)

Design properties:

  • Gated on the existing governable gate iu_core.three_axis_auto_refresh_enabled — no new config key. Default false ⇒ runtime behaviour unchanged. This also makes that gate meaningful for cuts (previously it governed only the envelope trigger; now it also governs post-cut tag materialization). Activation = open the gate via the normal protocol.
  • Best-effort: the materialize runs in a PL/pgSQL sub-transaction; any failure is caught, logged as a WARNING, and cannot roll back or block cut completion.
  • Idempotent: materialize uses ON CONFLICT DO NOTHING (tags) + NOT EXISTS (registry) + upsert envelope refresh — safe to re-run; no double execution.
  • No vector / no body / no law mutation: touches only iu_metadata_tag(_registry) + iu_three_axis_envelope.

4. Evidence (dress-rehearse → commit → fresh-verify)

Rehearsal (BEGIN..ROLLBACK):

  1. Standalone determinism+idempotency: fn_iu_post_cut_axis_materialize('…dieu39…md','canary')ok=true, iu_count=16, new_tags_total=0, envelope 219/219.
  2. Gate value false (deployment behaviour-preserving).
  3. Full-wire canary: gate set true in-tx, cut e15cabe9 reset to cut_verified in-tx, fn_cut_complete(e15cabe9,'canary')axis_autowire={attempted:true, ok:true, iu_count:16, new_tags_total:0}. ROLLBACK → gate back to false, all reverted.

Commit: CREATE OR REPLACE inside BEGIN/COMMIT.

Fresh-connection verify (read-only MCP): autowire_present=true, gate_guarded=true, gate_value=false, all_safe=true, never_flip_intact=true, idle-tx 0, iu 219 / iu_metadata_tag 536 unchanged.

5. Activation (for a human, later)

To turn post-cut axis-B auto-materialization ON: open the governable gate iu_core.three_axis_auto_refresh_enabled via fn_iu_gate_open(...) (bounded TTL). From then, every fn_cut_complete auto-applies axis-B tags for the cut source. To turn off: fn_iu_gate_close(...). No code change needed either way.

6. Rollback

Restore the original fn_cut_complete via CREATE OR REPLACE with the verbatim pre-fix definition (Doc 07 §1). Reversible at any time; no data migration involved.

Back to Knowledge Hub knowledge/dev/reports/architecture/iu-technical-gap-fix-p0-autowire-dot-vector-reconcile-2026-05-29/01-p0-post-cut-autowire-fix.md