KB-499E
04 fn_iu_verify_mark Gate Fix
5 min read Revision 1
dieu44verify-markdelegationfix2026-05-27
04 — fn_iu_verify_mark Gate Fix (delegation to preflight)
Before
fn_iu_verify_mark (md5 1db15847b1c48e3b86568b712a15cfd6) implemented Axes A–D inline. It approved any manifest that passed those axes plus coverage + digest checks. It did NOT check:
- per-piece
section_typevocab presence - canonical_address collision with existing IU
- per-piece publication_type vocab
- title derivability
- per-piece local_piece_id uniqueness (relied on MARK)
Consequently, manifests could reach lifecycle_status=approved while a known CUT-time refusal still applied.
After
fn_iu_verify_mark md5 c9c0553f3184bfaa3f2ee5488b5ff46c:
SELECT * INTO v_rec FROM iu_core.iu_staging_record WHERE staging_record_id=p_staging_record_id;
... staging_kind / lifecycle preconditions unchanged ...
-- Single source of truth: delegate.
v_preflight := public.fn_iu_cut_preflight_validate(p_staging_record_id, NULL, p_actor);
v_ok := COALESCE((v_preflight->>'cut_readiness_ok')::boolean, false);
IF NOT v_ok THEN
IF p_apply THEN
UPDATE iu_core.iu_staging_record
SET lifecycle_status='rejected',
metadata = COALESCE(metadata,'{}'::jsonb) || jsonb_build_object(
'verify_mark_problems', v_preflight->'problems',
'verify_mark_preflight', v_preflight,
'verify_mark_at', now())
WHERE staging_record_id=p_staging_record_id;
INSERT INTO public.dot_iu_command_run(...) VALUES (...);
END IF;
RETURN jsonb_build_object('ok',false,'verdict','rejected',
'axis_a_ok', v_preflight->'axis_a_ok',
'axis_b_ok', v_preflight->'axis_b_ok',
'axis_c_ok', v_preflight->'axis_c_ok',
'axis_d_ok', v_preflight->'axis_d_ok',
'cut_readiness_ok', false,
'problems', v_preflight->'problems',
'preflight', v_preflight);
END IF;
IF p_apply THEN
-- approval_doc_id + p_approver required; sets lifecycle='approved' + audit
...
END IF;
RETURN jsonb_build_object('ok',true,'verdict','approved',
'axis_a_ok',true,'axis_b_ok',true,'axis_c_ok',true,'axis_d_ok',true,
'cut_readiness_ok', true, 'preflight', v_preflight);
Behavioural diff
| scenario | before mig 055 | after mig 055 |
|---|---|---|
Axis D fails (e.g. unit_kind=law_section) |
already rejected (post mig 054) | rejected with full preflight artifact in metadata |
| section_type ∉ vocab | approved (silent gap) | rejected at verify_mark with E1 problem |
| canonical_address collision | approved → CUT raises mid-loop | rejected at verify_mark with E2 problem |
| title not derivable | approved → fn_iu_create RAISES title required |
rejected at verify_mark with E4 problem |
| publication_type ∉ vocab (when present) | approved → fn_iu_create RAISES | rejected at verify_mark with E3 problem |
| duplicate local_piece_id | MARK already refused, verify_mark passed if it got there | rejected at verify_mark with E5 problem (redundancy) |
| valid manifest | approved | approved + verify_mark_cut_readiness_ok=true + preflight artifact pinned in metadata |
Audit trail enrichments
On reject (p_apply=true), metadata now pins:
verify_mark_problems(jsonb array of indexed strings)verify_mark_preflight(full preflight verdict for forensic replay)verify_mark_at
dot_iu_command_run row now carries the full preflight in evidence.
On approve (p_apply=true), metadata pins:
verify_mark_axis_a/b/c/d(all true)verify_mark_cut_readiness_ok(true)verify_mark_preflight(full preflight)verify_mark_at
What is unchanged
- Pre-checks: staging_kind='mark_manifest', lifecycle_status='pending_review' (identical gates as before).
- Apply path:
approved_at = now(),approved_by = p_approver,approval_doc_id = p_approval_doc_id. - Reject path: lifecycle →
rejected(setsiu_staging_record_approved_consistency_chk-compatible state). - Return-shape compatibility: same top-level keys (
ok,verdict,axis_*_ok,problems) plus newcut_readiness_okandpreflight. fn_iu_op_verify_markalias body — md5bf20bd19...untouched (governance pin honored).
Why delegation, not inline expansion
- Single source of truth. Future CUT-time refusal additions go in one place.
- Inspection. Operators can call
fn_iu_cut_preflight_validatedirectly on any lifecycle (including already-approved manifests like9fa4685e) to get a verdict without mutation. - STABLE classification. Preflight is read-only and can be called from views/triggers without write-permission risk.
- Governance pinning. Alias body unaffected; only the underlying function changes — preserves the contract surface.