dot-iu-cutter v0.5 — Canonical Path Survey · fn_iu_create Contract Survey (S2 PASS · rollback-only test confirms zero persistence) (doc 2 of 7)
dot-iu-cutter v0.5 — Canonical Path Survey · fn_iu_create Contract Survey
doc 2 of 7 · 2026-05-20 · read-only survey + ROLLBACK-only test
phase : S2 — survey canonical fn_iu_create + companions outcome : PASS — contract fully mapped + live ROLLBACK-only test production_mutation : NONE (test ran inside BEGIN/ROLLBACK; counts verified byte-identical post-rollback)
1. Signature
public.fn_iu_create(
p_canonical_address text,
p_title text,
p_body text,
p_actor text,
p_unit_kind text DEFAULT NULL, -- auto-resolve from dot_config
p_section_type text DEFAULT NULL, -- auto-resolve from dot_config
p_owner_ref text DEFAULT NULL, -- default = p_actor
p_publication_type text DEFAULT NULL, -- optional, explicit only
p_parent_ref uuid DEFAULT NULL -- optional parent/container
) RETURNS jsonb
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = pg_catalog, public
owner : directus
security_definer : YES (runs as directus inside the function body)
volatility : VOLATILE
md5(pg_get_functiondef): dcade99af1ef096892748c9f14082e11
body_length : 5871 chars
2. Internal behaviour (read full body)
In order :
0. validate inputs (canonical_address/title/body/actor required, non-empty)
1. fn_iu_create_preflight() → check schema columns, FK deferrability, all
three IU triggers (gateway, L1, L2) present
2. unique-canonical_address guard discovery (exactly one such constraint)
3. fn_iu_resolve_default → unit_kind, section_type (auto from
iu_create.default_unit_kind etc.); publication_type
(explicit only)
4. owner := COALESCE(p_owner_ref, p_actor)
5. pg_advisory_xact_lock(hashtext(canonical_address)) -- race-safety
6. fn_iu_classify_existing(canonical_address)
- 'not_found' → proceed
- 'exists_complete' → idempotent return (NO new row)
- 'exists_missing_*' / 'exists_anchor_invalid' / 'exists_duplicate_*' /
'exists_unknown_state' → health/remediation (NOT success ; caller
must NOT treat as success)
7. IF FK not initially deferred → SET CONSTRAINTS <fk> DEFERRED
8. PERFORM set_config('app.canonical_writer', 'fn_iu_create', true)
-- txn-local marker; gateway trigger lets the writes through
9. v_iu_id := gen_random_uuid() ; v_uv_id := gen_random_uuid()
v_hash := fn_content_hash(p_body) -- sha256 hex of body
10. INSERT INTO public.information_unit
(id, canonical_address, unit_kind, owner_ref, created_by, updated_by,
identity_profile, parent_or_container_ref)
VALUES (v_iu_id, btrim(p_canonical_address), v_unit_kind, v_owner,
btrim(p_actor), btrim(p_actor),
{"title": btrim(p_title),
"owner_lookup_ref": v_owner,
"primary_section_type_ref": v_section_type[,
"publication_type_ref": btrim(p_publication_type)]},
p_parent_ref)
-- unique_violation → re-classify_existing → idempotent return
11. INSERT INTO public.unit_version
(id, unit_id, body, content_hash, version_seq, created_by)
VALUES (v_uv_id, v_iu_id, p_body, v_hash, 1, btrim(p_actor))
12. UPDATE public.information_unit
SET version_anchor_ref = v_uv_id,
content_anchor_ref = v_uv_id::text
WHERE id = v_iu_id
13. fn_iu_verify_invariants(canonical_address) → assert all_pass=true
14. RETURN jsonb_build_object(
'status','created', 'iu_id', v_iu_id, 'uv_id', v_uv_id,
'canonical_address', btrim(p_canonical_address),
'content_hash', v_hash,
'birth_verified', true, 'invariants_verified', true,
'unit_kind', v_unit_kind, 'section_type', v_section_type,
'version_seq', 1)
3. Q3 RESOLVED — fn_iu_create writes IU + UV + anchor itself
The function is "complete-or-nothing" : IU + UV(version_seq=1) + the anchor UPDATE all happen inside the function body. There is NO separate canonical UV writer ; UV(v1) is birthed atomically with the IU.
For the constitution CUT this means one call per row is sufficient — no separate UV INSERT loop, no separate anchor UPDATE loop.
4. fn_iu_create_plan — dry-run companion
signature : identical to fn_iu_create
security_definer: YES
volatility : STABLE
md5 : 9840036c9747fc213234f111314144bc
returns : jsonb with {mode='plan', status, would_create, existing,
resolved_unit_kind, resolved_section_type, resolved_owner_ref,
body_length, content_hash_preview (first 16 hex),
preflight, fk_initially_deferred, issues}
status values : plan_ok | invalid_input | invalid_publication_type |
unresolved_vocab | preflight_failed | <existing_status>
The redesign does NOT strictly require fn_iu_create_plan (the canonical fn_iu_create runs its own preflight + classify_existing internally). Optional as a defense-in-depth pre-CUT check.
5. fn_iu_verify_invariants — invariants checked at COMMIT step 13
i1 IU row exists at canonical_address
i2 unit_version row is linked (version_anchor_ref resolves)
i3 anchors exact (version_anchor_ref::text == content_anchor_ref
AND unit_version.unit_id == iu.id)
i4 birth_registry has an 'information_unit::<iu_id>' entry
i5 uv_birth_ok (per collection_registry birth_code_strategy)
all_pass = (i1 ∧ i2 ∧ i3 ∧ i4 ∧ i5)
NOTE: invariants do NOT cover lifecycle_status, doc_code, section_code, or section_type. The function does NOT enforce 'enacted'.
6. fn_content_hash — hash equivalence with cutwrite
CREATE FUNCTION public.fn_content_hash(p_body text) RETURNS text
IMMUTABLE LANGUAGE plpgsql
AS $$ BEGIN RETURN encode(digest(p_body,'sha256'),'hex'); END; $$;
Equivalent to cutwrite's Python hashlib.sha256(s.encode("utf-8")).hexdigest()
when p_body is the same text in UTF-8. So if the same body string is
passed in, the resulting content_hash is byte-identical to cutwrite's
uv["content_hash"].
7. Rollback-only live test — fn_iu_create()
Executed 2026-05-20 03:44 UTC inside the postgres container via Unix socket
trust auth as directus (the only role with EXECUTE today).
BASELINE_iu_uv_br=98|105|526396
BEGIN
WARNING: Birth gate L1 PILOT-ONLY: P-pub1 missing — production sẽ BLOCK
WARNING: Birth gate L1 PILOT-ONLY: P-pub2 missing — production sẽ BLOCK
{ "iu_id": "0f1f4eb9-…", "uv_id": "99806163-…",
"status": "created", "unit_kind": "law_unit", "version_seq": 1,
"content_hash": "81e10831…30c",
"section_type": "principle",
"birth_verified": true,
"canonical_address": "ICX-SURVEY-ROLLBACK-ONLY/20260520T034408Z/test-1",
"invariants_verified": true }
1 -- in-txn matching row (visible
only inside the txn)
ROLLBACK
POST_iu_uv_br_surveyMatch=98|105|526396|0 -- back to baseline ; 0 survey rows
ROLLBACK_TEST=PASS counts unchanged
Findings
- The canonical path WORKS today — returns
status='created'withbirth_verified=true,invariants_verified=true. - The L1 trigger is in PILOT-ONLY mode and emits warnings about
P-pub1/P-pub2"production sẽ BLOCK". When L1 transitions to strict, fn_iu_create calls withoutp_publication_typemay start failing. Not a blocker today; relevant to future re-rerun planning. - ROLLBACK leaves zero persistent footprint: pre- and post-counts are
byte-identical (
98|105|526396), noICX-SURVEY-ROLLBACK-ONLY%rows remain.
8. Persisted row shape — direct vs canonical
| Column | direct (cutwrite shape) | canonical (fn_iu_create) | in writer_digest? |
|---|---|---|---|
| id | deterministic per cutwrite | gen_random_uuid() | NO |
| canonical_address | per cutwrite | passed through | YES |
| unit_kind | "law_unit" | passed through | YES |
| lifecycle_status | 'enacted' (OD-W8) | 'draft' (column DEFAULT) | NO |
| content_anchor_ref / version_anchor_ref | per cutwrite (then anchored) | set by fn_iu_create | NO |
| owner_ref | per cutwrite (OWNER) | passed as p_owner_ref | NO |
| parent_or_container_ref | NULL | passed as p_parent_ref | NO |
| conformance_status | per cutwrite | column DEFAULT 'open' | NO |
| identity_profile | rich (multiple keys) | {title, owner_lookup_ref, primary_section_type_ref[, publication_type_ref]} |
NO |
| created_at / updated_at | SQL_NOW sentinel | column DEFAULT now() | NO |
| created_by / updated_by | "cutter_exec/DOT-991/constitution-cut" | btrim(p_actor) | NO |
| doc_code | 'ICX-CONST' | NULL | NO |
| section_type (column) | per cutwrite | NULL (value lives in identity_profile.primary_section_type_ref) | YES (via cutwrite row, mapped to p_section_type) |
| section_code | per cutwrite | NULL | NO |
| uv.id | deterministic per cutwrite | gen_random_uuid() | NO |
| uv.body | per cutwrite | passed as p_body | NO (but its sha256 is) |
| uv.content_hash | sha256(body) | fn_content_hash(p_body) = sha256 hex of body | YES |
writer_digest input tuple = [canonical_address, unit_kind, section_type, uv.content_hash, idempotency_key].
All five components are PRESERVED end-to-end when the canonical adapter maps cutwrite's rows to fn_iu_create's args. Writer digest equivalence holds.
Semantic gaps to surface for sovereign ruling (NOT in writer_digest, but ARE in persisted row):
lifecycle_status='draft'instead of'enacted'doc_codecolumn NULL (value'ICX-CONST'lost ; ICX-CONST prefix still visible viacanonical_address LIKE 'ICX-CONST%')section_codecolumn NULL (e.g. 'NT-1' lost ; still present in the trailing path segment ofcanonical_address)section_typecolumn NULL (value still inidentity_profile.primary_section_type_ref)identity_profileJSON minimal vs richid/uv.idserver-generated (not deterministic)
9. Disposition
S2 : PASS — full contract + live ROLLBACK test
no_production_mutation : confirmed (BEGIN…ROLLBACK; counts unchanged)
next : S3 — existing code/docs review
remaining_sovereign_decisions :
- whether lifecycle_status='draft' is acceptable for constitution IUs
- whether the missing column data (doc_code/section_code/section_type/
rich identity_profile) is acceptable
- whether fn_iu_create needs to be extended (out of cutter_agent scope)
OR whether the constitution CUT proceeds with the current contract
doc 2 of 7.