KB-4876
00 — IU CUT Điều 39 VERIFY_MARK Root Cause + Contract Fix — Summary
6 min read Revision 1
dieu44iu-cutdieu39verify-markcontract-fixv0.6pass
00 — IU CUT Điều 39 VERIFY_MARK Root Cause + Contract Fix
Pack: IU_CUT_DIEU39_VERIFY_MARK_ROOT_CAUSE_AND_CONTRACT_FIX_PASS
Date: 2026-05-27
Channel: local CC → ssh contabo → docker exec postgres → psql -U workflow_admin -d directus
Result: PASS
Headline
The Điều 39 VERIFY_MARK failure had two compounding root causes, both now fixed at the contract level:
- MARK contract gap (primary) —
fn_cut_mark_staged_filevalidated onlycontent_textandcanonical_address, allowing manifests missingsource_position,piece_role,local_piece_idto reachstatus='marked'. The Codex caller for Điều 39 produced pieces with onlypiece_index,section_type,content_text,canonical_address— and MARK accepted them silently. - VERIFY_MARK error handling bug (secondary) —
fn_iu_verify_markappended bare string literals to atext[](v_problems := v_problems || 'literal'). PostgreSQL resolved this astext[] || 'literal'::text[], requiring{...}array-literal syntax, so the function raisedmalformed array literal: "Axis B: ..."instead of returning a structured failed verdict.
Both fixes shipped together in mig 054 (single TX, COMMITTED). Re-mark of Điều 39 against the corrected contract passed all 3 axes with verdict approved.
Live state at exit
| key | value |
|---|---|
cut_request_id |
146f1520-aaa2-4bda-af2c-06a8f76cd36a |
cut_request.status |
marked (re-marked under v1 schema) |
manifest_staging_record_id |
9fa4685e-d35a-45d4-aee7-aa2836785ca5 (new) |
manifest_digest |
aded6af91fb9643fb2ea99ff024a1ede (new) |
pieces_count |
16 |
cut_manifest_piece_schema_v1 fields |
16/16 each (local_piece_id, content_text, canonical_address, source_position, piece_role, section_type) |
verify_mark preview |
verdict=approved, axis_a_ok=true, axis_b_ok=true, axis_c_ok=true |
| Prior bad staging record | 3d9d0388-… → lifecycle_status=rejected (orphaned, kept for audit) |
cut_run_id |
NULL (no CUT was run — strict non-goal honored) |
| Backups | pre 84,084,948 B / post 84,104,933 B (Δ +19,985 B) |
| 5/5 MARK/CUT alias body md5 | UNCHANGED |
| Gates at exit | job_substrate=false, composer=false, heartbeat=true (untouched) |
What changed (durable)
public.fn_iu_verify_mark—text[] || ARRAY['...']everywhere; added Axis A pre-check that surfaces non-integersource_positionas a structured problem rather than raising in the int cast. New md504e5191c430a142712bfe63089863d44.public.fn_cut_mark_staged_file— addedcut_manifest_piece_schema_v1validation block before status transitions; relaxed status precondition to accept{copied, mark_rejected}for the re-mark path; idempotency key now includesmd5(p_pieces::text)so a re-mark with different pieces yields a new manifest record. New md567721b42a55315858e3c377654c2a5b1.
Strict non-goals honored
- No CUT, no VERIFY_CUT, no COMPLETE.
- No Qdrant touch, no
production_documentstouch (table absent). - No
pg_cron. - No worker started.
- No manual
jsonb_setpatch oniu_staging_payload.payload_json.pieces(the prior recovery pattern from [[feedback-mark-pieces-live-in-iu-staging-payload-cut-manifest-not-in-iu-staging-record-metadata]] was explicitly avoided — root cause fixed instead). - No MARK/CUT alias contract rewrite (5/5 alias body md5 unchanged).
- Điều 45 untouched.
Report index
| # | File |
|---|---|
| 00 | 00-summary.md (this file) |
| 01 | 01-hard-gate-baseline-backup.md |
| 02 | 02-root-cause-classification.md |
| 03 | 03-cut-manifest-piece-schema-v1.md |
| 04 | 04-function-fixes.md |
| 05 | 05-dieu39-rerun-mark-result.md |
| 06 | 06-dieu39-rerun-verify-mark-result.md |
| 07 | 07-regression-safety.md |
| 08 | 08-next-step-cut-approval.md |
Carry-forward
- CF-1 — Approval gate: operator should call
fn_cut_verify_mark(cut_request_id, p_approve:=true, p_approval_doc_id:='<doc>', p_approver:='<user>')to transitionmarked → mark_verified. Until then, the manifest stays at preview level. - CF-2 —
fn_cut_verify_markquirk: whenp_approve=falseis passed, the operator wrapper transitions the cut_request without first reading the structured verdict fromfn_iu_op_verify_mark. Consider tying transition to actual verdict (or document the manual-override semantics). - CF-3 — Orphaned staging records: the rejected staging record
3d9d0388-…remains atlifecycle_status=rejected. Cleanup executor (already CF in [[project-iu-cut-operational-pipeline-runtime-hardening-pass-2026-05-27]]) should sweep these. - CF-4 —
piece_rolevocab: novocab.piece_role.*entries exist indot_config. Today only non-empty is enforced; consider defining the vocabulary (e.g.,section_body,definition,clause). - CF-5 — Caller convergence: operational MARK callers (Codex/Agent) should adopt
cut_manifest_piece_schema_v1documented in03-cut-manifest-piece-schema-v1.mdto avoid silent rejects.
Cross-links
- [[project-iu-cut-operational-pipeline-runtime-hardening-pass-2026-05-27]] — direct parent (added
content_text/canonical_addressvalidation in mig 053; this pack extends to full schema v1). - [[project-iu-cut-operational-pipeline-copy-mark-verify-cut-pass-2026-05-26]] — operational pipeline parent.
- [[feedback-fn-cut-mark-staged-file-must-validate-piece-schema-upstream]] — now fully generalized from
content_textto all schema-v1 fields. - [[feedback-mark-pieces-live-in-iu-staging-payload-cut-manifest-not-in-iu-staging-record-metadata]] — recovery pattern explicitly avoided; root cause fixed at MARK boundary instead.