KB-5E49
10000x · 04 — Migration 028: Piece MERGE primitive (fn_iu_piece_merge + iu_merge_set)
3 min read Revision 1
iu-corev0.610000xmigration-028mergefn_iu_piece_mergeiu_merge_setcontent-policy
10000x · 04 — Migration 028: Piece MERGE primitive
File
sql/iu-core/028_piece_merge_primitive.sql (transactional). ~324 lines.
Substrate
Table public.iu_merge_set
id uuid PK DEFAULT gen_random_uuid()
merged_iu_id uuid NOT NULL REFERENCES information_unit(id)
merged_canonical_address text NOT NULL
source_iu_ids uuid[] NOT NULL
source_canonical_addresses text[] NOT NULL
actor text NOT NULL
review_decision_id uuid NOT NULL
change_set_id uuid
reason text
tool_revision text
idempotency_key text NOT NULL UNIQUE
created_at timestamptz NOT NULL DEFAULT now()
rolled_back_at timestamptz
rolled_back_by text
rolled_back_reason text
CHECK (cardinality(source_iu_ids) = cardinality(source_canonical_addresses))
CHECK (cardinality(source_iu_ids) >= 2)
INDEX (merged_iu_id, created_at DESC)
Function public.fn_iu_piece_merge
(p_merged_spec jsonb,
p_source_canonical_addresses text[],
p_actor text,
p_review_decision_id uuid,
p_change_set_id uuid DEFAULT NULL,
p_reason text DEFAULT NULL,
p_tool_revision text DEFAULT NULL,
p_dry_run boolean DEFAULT false
) RETURNS jsonb
SECURITY DEFINER
SET search_path = pg_catalog, public
Refusal contract
| Refusal | Status |
|---|---|
| NULL actor | invalid_input |
| NULL review_decision_id | invalid_input |
| NULL merged_spec or non-object | invalid_input |
| missing merged_spec.canonical_address | invalid_input |
| missing / empty merged_spec.body | invalid_input (content-policy refusal) |
| <2 sources | invalid_input |
| duplicate source addresses | invalid_input |
| merged_canonical = ANY(sources) | invalid_input (forbids self-merge) |
| source IU not found | source_iu_not_found |
| review_decision_id not in FK | review_decision_not_found |
Content policy
Merged body MUST be explicit in p_merged_spec.body. The function refuses to concatenate or otherwise synthesise content from sources — preventing fake content that would not match any human authorisation. For intentionally empty merged body, pass body='__EMPTY__' (stored verbatim).
This is the strongest divergence from naive "merge = concat sources" implementations.
Why sources are NOT auto-superseded
Same reasoning as 027. After merged piece is enacted, operator invokes dot_iu_supersede_piece ONCE PER SOURCE.
Live apply
BEGIN
CREATE TABLE
CREATE INDEX
COMMENT
CREATE FUNCTION
COMMENT
COMMIT
Rollback file
sql/iu-core/rollback/028_piece_merge_primitive.rollback.sql — drops function, index, table in safe order.