KB-4485

100000x · 02 — Re-author + apply 040R (CUT from approved manifest)

6 min read Revision 1
iu-core100000x040Rcut-from-manifestlive-applied2026-05-26

100000x · 02 — Re-author + apply 040R

Phase: B · Status: APPLIED · Migration: 040R

Apply

ssh contabo 'docker exec -i postgres psql -U workflow_admin -d directus -v ON_ERROR_STOP=1' < /tmp/iu100000x/040R-cut-from-manifest.sql

Result: BEGIN / CREATE FUNCTION / INSERT 0 1 / COMMIT. One TX, atomic.

Smoke test (immediate, no separate proof TX)

G1  | {ok:false, refusal_code:not_found,   run_id:<uuid>}
G2  | {ok:false, refusal_code:wrong_kind,  staging_kind:sql_snapshot, run_id:<uuid>}
CAT | dot_iu_cut_from_manifest registered (1 row)

Substrate validates. plpgsql function body successfully resolves all SQL statement column references at runtime ([[feedback-plpgsql-check-function-bodies-defers-sql-stmt-resolution]] guard).

Contract (preserved from 90000x report 05)

Input Type Default
p_staging_record_id uuid (required)
p_apply boolean false
p_source_hash text NULL
p_actor text 'fn_iu_cut_from_manifest'

Returns jsonb:

  • Refusal: {ok:false, refusal_code:<code>, ...context, run_id:<uuid>}
  • Dry-run: {ok:true, dry_run:true, pieces_planned:<n>, guards_passed:[G1..G7], run_id}
  • Apply: {ok:true, applied:true, run_id, staging_record_id, pieces_created_count:<n>, pieces_created:[uuid,...]}

G1-G7 guards (live signatures honored)

Guard Check Refusal code
G1 SELECT FOR UPDATE of staging row finds it not_found
G2 staging_kind = 'mark_manifest' wrong_kind
G3 lifecycle_status = 'approved' (consumed/pending/etc all refuse) not_approved
G4 approved_at AND approved_by AND approval_doc_id all NOT NULL incomplete_approval
G5 manifest.manifest_digest ~ ^[0-9a-f]{32}$ digest_changed
G6 p_source_hash matches manifest.source_hash (if both provided) source_changed
G7 fn_iu_composer_enabled() returns true composer_gate_closed

SELECT FOR UPDATE provides concurrency safety: parallel cut attempts on the same staging row serialize; the second caller observes lifecycle_status='consumed' and refuses via G3.

Drift patches vs 90000x mig 040 (mechanical, contract preserved)

Original (90000x) Live 040R fix
v_unit_id := fn_iu_create(unit_kind, content_text, section_type, piece_role, canonical_address, source_position) returning uuid fn_iu_create(p_canonical_address, p_title, p_body, p_actor, p_unit_kind, p_section_type, p_owner_ref, p_publication_type, p_parent_ref) returning jsonb Call with named params; extract via (result->>'iu_id')::uuid
(none) fn_iu_create writes section_type to identity_profile only Follow with UPDATE information_unit SET sort_order, section_type, doc_code, section_code WHERE id=v_unit_id
fn_iu_collection_add_piece(parent_uuid, child_uuid, piece_role, source_position) Not appropriate for parent-child IU edges (it's for piece↔collection edges) Use p_parent_ref on fn_iu_create directly; no membership write in 040R
INSERT INTO dot_iu_command_run (command_name, payload_json, actor, status) VALUES (..., 'running') Live columns: category, run_mode, run_status, evidence. No payload_json / status / running One INSERT per call with run_mode ∈ {plan,apply}, run_status ∈ {refused,planned,applied}, evidence jsonb
DOT catalog category='cut' Vocab is {collection, piece, lifecycle, read, health} category='piece'

Algorithm

  1. SELECT FOR UPDATE staging row. G1 fail → audit refused + return.
  2. G2 / G3 / G4 sequential checks, each emits a refusal audit on miss.
  3. Load cut_manifest payload_json. G5 digest 32-hex check.
  4. G6 source_hash compare (if both sides present).
  5. G7 composer gate.
  6. If p_apply=false → audit planned + return dry-run shape.
  7. Loop pieces ordered (parent_local_id NULL FIRST, source_position ASC):
    • Resolve v_parent_uuid from local map.
    • Call fn_iu_create with all named params (title = first line of body, fallback to canonical_address; truncated to 200 chars).
    • Assert result->>'status' = 'created'; RAISE otherwise (whole TX rolls back).
    • UPDATE information_unit SET sort_order, section_type, doc_code, section_code WHERE id = v_unit_id.
    • Append (local_id, unit_id) to v_local_to_unit, append v_unit_id to v_pieces_created.
  8. Audit row run_status='applied' with evidence carrying run_id, staging_record_id, manifest_digest, pieces_created, pieces_created_count.
  9. UPDATE staging row → lifecycle_status='consumed', consumed_at=now(), consumed_by_run_id=v_run_id, referenced_iu_ids=v_pieces_created, append cut_pieces_created/cut_run_id/cut_at to metadata.
  10. Return {ok:true, applied:true, run_id, staging_record_id, pieces_created_count, pieces_created}.

Any RAISE in steps 7-9 triggers TX rollback; staging row stays approved, no IU pieces persist.

Rollback

BEGIN;
DELETE FROM public.dot_iu_command_catalog WHERE command_name='dot_iu_cut_from_manifest';
DROP FUNCTION IF EXISTS public.fn_iu_cut_from_manifest(uuid, boolean, text, text);
COMMIT;

Refuse to rollback if any staging row currently has lifecycle_status='consumed' with consumed_by_run_id populated (would orphan audit linkage). 100000x leaves no such rows (proof rolled back).

  • [[feedback-fn-iu-create-returns-jsonb-not-uuid]] (refresh: key is iu_id)
  • [[feedback-information-unit-no-piece-role-column]]
  • 90000x report 05 (original author intent).
Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.6-iu-core-100000x-reauthor-apply-cut-verify-full-proof/02-reauthor-040R.md