IU CUT VERIFY/APPROVE/CUT Gate Consistency Fix — Summary (PASS 2026-05-27)
IU_CUT_VERIFY_APPROVE_CUT_GATE_CONSISTENCY_FIX_PASS — 2026-05-27
Outcome
IU_CUT_VERIFY_APPROVE_CUT_GATE_CONSISTENCY_FIX_PASS
Mission
Fix the gate design problem surfaced during the Điều 39 flight-test where VERIFY_MARK dry-run reported cut_readiness_ok=true, APPROVE rubber-stamped, and CUT then failed with composer_gate_closed, AND fn_cut_apply falsely wrote cut_run_id + cut_done_at despite zero IUs being created.
Migration
056_iu_cut_verify_approve_cut_gate_consistency_fix.sql — single TX. Four surgical fixes:
-
fn_iu_cut_preflight_validate— adds Axis E8composer_gate_open(runtime gate).cut_readiness_okis nowmark_valid AND e8_composer_okand exposes a new fieldmark_validso callers can distinguish manifest correctness from runtime readiness. md5914e26d6→25600094. -
fn_iu_verify_mark— APPROVE (p_apply=true) is now stronger than dry-run VERIFY. Re-runs preflight at approval time (E8 included) AND addscut_requestcross-binding: must find acut_requestwithmanifest_staging_record_id=...whosestatus='marked'andcut_run_id/cut_done_at/cut_verified_at/completed_atare all NULL. md5c9c0553f→c740ff7c. -
fn_cut_verify_mark(wrapper) — now respects the inner verdict. Previously transitionedmarked→mark_verifiedpurely onp_approve=true, ignoring inner refusal. Now: ifinner_ok=falseorinner_verdict<>'approved', transitionsmarked→mark_rejectedand threads inner reason into transition metadata. md5 changed to2b77a680. -
fn_cut_apply— re-runs preflight IMMEDIATELY before CUT (E8 included). If preflight refuses, returns refusal WITHOUT transitioning tocut_in_progress. If the aliasfn_iu_op_cutreturnsinner_result.ok=falseorapplied<>true, transitionscut_in_progress→cut_failedand never writescut_run_id/cut_done_at. md5ce488b88→007e2097.
Preserved (md5 UNCHANGED) — 7/7 alias contracts intact
| function | md5 |
|---|---|
fn_cut_mark_staged_file |
e85065acb9996623e0ef1f654d991df6 |
fn_cut_request_transition |
c392d3e156159852a97889df1af04165 |
fn_iu_create |
dcade99af1ef096892748c9f14082e11 |
fn_iu_cut_from_manifest |
c5d556bc22cc2d255c0484b5a969ebc5 |
fn_iu_op_cut |
66b813e50205448eb01170aebec614df |
fn_iu_op_mark_file |
ffaa47fff7a906d93060141661080cd4 |
fn_iu_op_verify_mark |
bf20bd1929998073865808d17b1dd648 |
Backups
- pre:
/tmp/pre_verify_approve_cut_gate_fix.dump84,391,933 B md50578a9f58820b29c50f8c3d1c089c9f3 - post:
/tmp/post_verify_approve_cut_gate_fix.dump84,397,741 B md52a454fda2be1f54528aa76991c48d74e - delta: +5,808 B (fn bodies only)
Regression — 7/7 PASS (BEGIN/ROLLBACK on Điều 39 manifest 5ef349ac…)
| # | scenario | expected | actual |
|---|---|---|---|
| T1 | preflight live composer=false | cut_readiness_ok=f, mark_valid=t, E8 problem present |
✓ |
| T2 | VERIFY_MARK dry-run composer=false | verdict=rejected, mark_valid=t, cut_readiness_ok=f, E8 |
✓ |
| T3 | fn_cut_verify_mark(p_approve=true) composer=false |
verdict=rejected, status=mark_rejected, inner_ok=f; manifest→rejected |
✓ |
| T4 | fn_cut_apply composer=false |
verdict=refused, refusal_code=preflight_refused; cut_request unchanged (status=mark_verified, cut_run_id=NULL, cut_done_at=NULL); IU count 200 |
✓ |
| T5 | composer=true flip (BEGIN/ROLLBACK) | preflight verdict=approved, e8_composer_ok=t |
✓ |
| T6 | bogus unit_kind still refused | Axis D axis_d_ok=f, problem present |
✓ |
| T7 | forbiddens snapshot | iu=200, vsp=152, eo=139900, prod_docs=absent, composer=false, queue.job=false, hb=true | ✓ |
Forbiddens — 15/15
no live CUT · no IU created · no Qdrant · no production_documents · no pg_cron · no broad worker · no vocab widening · no silent value mapping · no MARK/CUT alias body change · no new cut_request · no runtime.phase flip · no event_outbox mutation (delta 0) · no manual jsonb_set on Điều 39 manifest in live state · no destructive --force / git reset --hard · no Điều 39 re-MARK in this macro
Current Điều 39 state (Phase G — untouched)
cut_request_id=c7133284-a579-4266-aed8-c91f75a22283status=cut_failed(preserved as audit)cut_run_id=c42ebc50-20eb-459e-9099-eb46638f63b1(LEGACY fake state from pre-fix bug — kept for audit)cut_done_at=2026-05-27 05:45:54.928814+00(LEGACY fake state — kept for audit)cut_verified_at= NULL ·completed_at= NULL- manifest
5ef349ac…lifecycle_status=approved(LEGACY — not consumed) ready_for_cut_now= false (E8 closed under live composer=false)iu_dieu39_count= 0
The pre-fix fake cut_run_id + cut_done_at on a cut_failed request are LEFT IN PLACE deliberately: this macro is read-only on the existing audit record and the new code prevents the same pathology going forward.
Carry-forwards
- CF-1 (MEDIUM) — Backfill cleanup for the legacy fake
cut_run_id/cut_done_atoncut_request c7133284…: best done as part ofIU_CUT_DIEU39_RETRYplan (Phase H 07). Schema option:fn_cut_request_clear_phantom_cut_state(p_cut_request_id)gated onstatus='cut_failed' AND completed_at IS NULL. - CF-2 (LOW) —
iu_staging_record_approved_consistency_chkrequiresapproved_at/by/doc_idto be all-NULL or all-non-NULL withlifecycle_statusin {approved, consumed}. Document this constraint in the runbook (currently surprises BEGIN/ROLLBACK reverts). - CF-3 (HIGH) — Composer gate is now the only runtime gate in E8. Add additional runtime gates (
iu.composer.write_enabled,runtime.phase ∈ {…}, etc.) once their semantics stabilise. - CF-4 (HIGH) —
IU_CUT_DIEU39_RETRYplan (see 07-next-dieu39-retry-plan.md) once composer gate is opened intentionally.
KB reports — 8/8 in this folder
00-summary.md(this file)01-hard-gate-baseline-backup.md02-root-cause-classification.md03-preflight-runtime-gate-fix.md04-approve-stronger-gate-fix.md05-cut-apply-failure-state-fix.md06-regression-results.md07-next-dieu39-retry-plan.md
Cross-links
- Extends [[project_iu_cut_verify_mark_cut_readiness_gate_pass_2026_05_27]] (mig 055 — preflight + verify_mark delegation)
- Extends [[project_iu_cut_dieu39_unit_kind_root_cause_and_contract_fix_partial_2026_05_27]] (mig 054 — Axis D unit_kind)
- Generalises [[feedback_verify_mark_must_delegate_to_fn_iu_cut_preflight_validate]] — VERIFY/APPROVE/CUT all delegate to preflight; E8 added as runtime gate.