KB-E4EF

05 Điều 39 Invalid Manifest Now Caught at VERIFY (no mutation)

4 min read Revision 1
dieu44dieu39preflightphase-e2026-05-27

05 — Điều 39 Invalid Manifest Now Caught at VERIFY (no mutation)

Target

cut_request_id            = 146f1520-aaa2-4bda-af2c-06a8f76cd36a
manifest staging_record_id = 9fa4685e-d35a-45d4-aee7-aa2836785ca5
manifest lifecycle_status  = approved
manifest piece count       = 16
unit_kind distribution     = law_section × 16
cut_request status         = mark_verified
cut_run_id                 = NULL

Preview run (read-only, no mutation)

Command:

SELECT jsonb_pretty(public.fn_iu_cut_preflight_validate(
  '9fa4685e-d35a-45d4-aee7-aa2836785ca5'::uuid,
  NULL,
  'phase_e_test'
));

Verbatim output:

{
    "ok": false,
    "gates": {
        "e2_addr_ok": true,
        "e4_title_ok": true,
        "e6_digest_ok": true,
        "e1_section_ok": true,
        "e3_pubtype_ok": true,
        "e7_coverage_ok": true,
        "e5_local_uniq_ok": true
    },
    "counts": {
        "pieces": 16,
        "title_bad": 0,
        "unit_kind_missing": 0,
        "address_collisions": 0,
        "unit_kind_in_vocab": 0,
        "local_piece_id_total": 16,
        "publication_type_bad": 0,
        "local_piece_id_unique": 16,
        "section_type_in_vocab": 16
    },
    "verdict": "rejected",
    "problems": [
        "Axis D: unit_kind compatibility failed (total=16, missing=0, in_vocab=0) -- every piece.unit_kind must exist in dot_config vocab.unit_kind.*"
    ],
    "axis_a_ok": true,
    "axis_b_ok": true,
    "axis_c_ok": true,
    "axis_d_ok": false,
    "checked_at": "2026-05-27T04:37:52.650728+00:00",
    "checked_by": "phase_e_test",
    "cut_readiness_ok": false,
    "staging_record_id": "9fa4685e-d35a-45d4-aee7-aa2836785ca5",
    "lifecycle_status_at_check": "approved"
}

Interpretation

  • One isolated failure: Axis D only. Every other axis and gate passes — including E2 (no canonical_address collision against the 200 existing IUs), E1 (all 16 section_types resolve in vocab.section_type.*), E4 (titles derivable), E5 (local_piece_ids unique), E6 (manifest_digest 32-hex), E7 (coverage_bytes=29393 matches source_bytes).
  • This is the strict refusal behaviour mandated by the parent policy: do NOT silently map law_section → law_unit; surface the contract violation with an indexed error.
  • lifecycle_status_at_check=approved confirms the read-only nature — the manifest was NOT mutated.

Why fn_iu_verify_mark itself did not run on 9fa4685e

fn_iu_verify_mark has a precondition: lifecycle_status='pending_review'. Manifest 9fa4685e is approved (set by an older verify_mark before mig 054), so verify_mark refuses with lifecycle must be pending_review. The new preflight function deliberately accepts any lifecycle so it can be used as a forensic / inspection tool.

To bring this stuck request unstuck, see 07-next-state-machine-rollback-policy.md. The current mig does NOT mutate Điều 39 state.

What did NOT happen (forbiddens honored)

  • No CUT (fn_iu_op_cut/fn_iu_cut_from_manifest not called).
  • No new cut_request row.
  • No new staging record for Điều 39.
  • No silent vocab widening (law_section not added to vocab.unit_kind.*).
  • No silent mapping (manifest's unit_kind not rewritten).
  • Manifest 9fa4685e bytes not touched.
  • cut_run_id remains NULL.
  • IU count for Điều 39 stays at 0.

What WILL happen on the next attempt

When the operator (or rebuilt Codex/Agent) re-MARKs Điều 39 with unit_kind=law_unit for all 16 pieces, fn_cut_mark_staged_file will accept it (mig 054 Axis D check passes), fn_iu_verify_mark will call preflight which returns ok=true, the lifecycle moves to approved, and CUT can run successfully — no further migrations needed for the normal happy path.

The unresolved blocker is the state-machine rollback gap from mark_verified, addressed in 07.

Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.6-iu-cut-verify-mark-cut-readiness-gate/05-dieu39-invalid-manifest-now-caught-at-verify.md