Điều 39 Rerun DEFERRED (PARTIAL_WITH_EXACT_GAP — state-machine gap)
05 — Điều 39 Rerun (DEFERRED)
Decision: NOT executed in this macro
Per operator directive (Option C): rerun MARK and VERIFY_MARK for Điều 39 are deferred to a subsequent macro after the rollback-policy gap is resolved. This macro returns PARTIAL_WITH_EXACT_GAP.
Why deferred
Current state:
cut_request_id : 146f1520-aaa2-4bda-af2c-06a8f76cd36a
status : mark_verified
manifest : 9fa4685e-... (lifecycle_status=approved)
Both options for re-MARKing have unacceptable side effects:
Option A — open a new cut_request (rejected)
SELECT public.fn_cut_request_add(
p_source_ref := 'knowledge/dev/laws/dieu39-knowledge-graph-law.md',
p_source_kind := 'kb_document',
p_requested_by := 'operator-dieu39-rerun'
);
Pros: simple, no state-machine change.
Cons: orphans 146f1520-... at mark_verified indefinitely. Pollutes operational view of in-flight cut_requests with a stuck row. Operator rejected for operational hygiene.
Option B — extend state machine (rejected)
-- inside fn_cut_request_transition:
WHEN 'mark_verified' THEN ARRAY['cut_in_progress', 'mark_rejected'] -- add second target
Pros: single cut_request can be re-used; clean re-MARK path.
Cons: changes the state-machine fn body and the transition contract. This is a state-machine policy decision, not a unit_kind fix — scope creep. Operator rejected as out of scope for this macro.
Option C — STOP here (chosen)
The code-side contract fix is COMMITTED and regression-proven. The data-side rerun of Điều 39 is deferred. The mission returns PARTIAL_WITH_EXACT_GAP documenting the precise blocker.
What would the rerun look like (spec for next macro)
When the rollback-policy gap is resolved (in a separate macro), the Điều 39 rerun should:
-
Reject the current approved manifest (audit):
UPDATE iu_core.iu_staging_record SET lifecycle_status = 'rejected', metadata = metadata || jsonb_build_object( 'rejected_at', now(), 'rejected_by', 'operator-dieu39-rerun', 'rejected_reason', 'pre_unit_kind_gate_manifest', 'rejected_root_cause', '16/16 pieces had unit_kind=law_section which is not in dot_config vocab.unit_kind.* — refused at fn_iu_create during CUT attempt', 'prior_manifest_digest', 'aded6af91fb9643fb2ea99ff024a1ede' ) WHERE staging_record_id = '9fa4685e-d35a-45d4-aee7-aa2836785ca5'; -
Transition cut_request
mark_verified -> mark_rejected(legal after state-machine widening):SELECT public.fn_cut_request_transition( '146f1520-aaa2-4bda-af2c-06a8f76cd36a', 'mark_rejected', 'operator-dieu39-rerun', '{"reason":"manifest_pre_unit_kind_gate","prior_manifest_digest":"aded6af9..."}'::jsonb ); -
Re-build pieces from the same
source_copypayload (no re-COPY needed —copy_staging_record_id=365cdc10-...is intact), with every piece'sunit_kind=law_unitinstead oflaw_section. All other fields (local_piece_id, content_text, canonical_address, source_position, piece_role, section_type, parent_local_id) copied verbatim. -
Call MARK:
SELECT public.fn_cut_mark_staged_file( p_cut_request_id := '146f1520-aaa2-4bda-af2c-06a8f76cd36a', p_pieces := '<rebuilt 16-piece array>'::jsonb, p_actor := 'operator-dieu39-rerun' );Expected: status ->
mark_in_progress->marked; newmanifest_staging_record_id; newmanifest_digest(different fromaded6af9...since unit_kind values differ). -
Dry-run VERIFY_MARK:
SELECT public.fn_iu_verify_mark( p_staging_record_id := '<new_manifest_sr>', p_apply := false );Expected:
{ok:true, verdict:'approved', axis_a_ok:true, axis_b_ok:true, axis_c_ok:true, axis_d_ok:true}. -
Apply VERIFY_MARK with new
approval_doc_id:SELECT public.fn_iu_verify_mark( p_staging_record_id := '<new_manifest_sr>', p_apply := true, p_approval_doc_id := 'knowledge/current-state/reports/dieu39-rerun-after-unit-kind-fix-2026-XX-XX.md', p_approver := 'operator' );Expected: status ->
mark_verified; manifestlifecycle_status='approved'. -
STOP at
ready_for_cut=true. Do not run CUT in that macro unless explicitly authorized.
What was NOT done now (proven by section 06)
- Old manifest
9fa4685e-...lifecycle_status STILLapproved(NOT changed torejected) - cut_request
146f1520-...status STILLmark_verified(no transition recorded after this macro's start) - No new cut_request rows created
- No new staging records created
- No new IUs created (
information_unittotal = 200, unchanged)
Conclusion
This phase is DEFERRED and is the explicit PARTIAL_WITH_EXACT_GAP of this macro. The code-side gate (Phase D) is sufficient to prevent recurrence: any future MARK call with unit_kind=law_section or any unknown value is refused at the boundary. The data-side cleanup of the specific stuck Điều 39 cut_request requires the next macro's rollback-policy decision.