KB-136F

18000x · 05 — Template Mutation Invariants Proof (shared-graph, copy-on-instance=NO, supersede does NOT propagate)

5 min read Revision 1
iu-corev0.618000xmutation-invariantshared-graphsupersedemanifest-digest

18000x · 05 — Template Mutation Invariants Proof

Question

If an operator supersedes a piece that is shared between a template and N instances, does the mutation silently propagate?

Bounded BEGIN/ROLLBACK proof

ops/.../iu_core_18000x_mutation_invariant_proof.sql

  1. Capture pre-supersede digests of tpl:wf:onboarding/v1 and tpl-inst:wf:onboarding/2026-05-25-demo.
  2. Call fn_iu_supersede('iu_core/template/15000x/wf-onboarding-v1/step-0-d0-account', actor, NULL, NULL, reason, tool, NULL, dry_run=true).
  3. Force fn_iu_collection_manifest_refresh on BOTH collections (composer gate flipped on inside TX, off before COMMIT — but here ROLLBACK reverts both anyway).
  4. Compare digests.
  5. ROLLBACK.
  6. Post-rollback durability assertion.

Live transcript (excerpted)

-- pre-state digests
             collection_key             |         manifest_digest
----------------------------------------+----------------------------------
 tpl-inst:wf:onboarding/2026-05-25-demo | 2d3d37b60fd32963ddd7a6eef81aaad6
 tpl:wf:onboarding/v1                   | 2d3d37b60fd32963ddd7a6eef81aaad6

-- supersede dry_run probe
{"field": "review_decision_id", "status": "invalid_input",
 "guidance": "Required. Supersede must reference a cutter_governance.review_decision row.",
 "next_action": "record_review_decision_first"}

-- manifest refresh round-trip (both collections)
{"ok": true, "piece_count": 5, "manifest_digest": "2d3d37b60fd32963ddd7a6eef81aaad6"}  v1
{"ok": true, "piece_count": 5, "manifest_digest": "2d3d37b60fd32963ddd7a6eef81aaad6"}  instance

-- post-supersede digests
             collection_key             |            pre_digest            |           post_digest            | digest_unchanged
----------------------------------------+----------------------------------+----------------------------------+------------------
 tpl-inst:wf:onboarding/2026-05-25-demo | 2d3d37b60fd32963ddd7a6eef81aaad6 | 2d3d37b60fd32963ddd7a6eef81aaad6 | t
 tpl:wf:onboarding/v1                   | 2d3d37b60fd32963ddd7a6eef81aaad6 | 2d3d37b60fd32963ddd7a6eef81aaad6 | t

ROLLBACK

Invariant policy

  • Shared-graph by defaultfn_iu_compose's iu_id branch attaches an existing IU by reference; pieces are NOT duplicated when instantiating from a template. The iu_id is the same object.
  • Copy-on-instance = NO — there is no in-place copy. An instance and its template hold the same iu_ids in their memberships.
  • Mutation does NOT propagate silentlyfn_iu_supersede (and fn_iu_piece_split / fn_iu_piece_merge by symmetry) creates a new iu_id and an iu_supersede_set (or split/merge ledger) link; it does not rewrite any existing membership row. Because fn_iu_collection_manifest_refresh hashes (piece_order, iu_id, piece_role) ordered (see [[feedback-manifest-digest-keyed-on-piece-graph]]), the digest input is byte-identical, so the digest is byte-identical.
  • Explicit propagation only — to upgrade an existing instance to "use the superseded piece," an operator must explicitly compose a v(N+1) of the template (with the new iu_id) and re-instantiate. This is exactly the version chain pattern 18000x adds in migration 032.

Implications for operators

  • You can safely supersede a template piece — existing instances will continue to render the pre-supersede content (the iu_id they reference is unchanged).
  • You should always ship a new template version when superseding content if you want the new content reflected in downstream instances. Use dot_iu_register_template_version with previous_collection_id to make the chain explicit.
  • The observability view flags driftv_iu_template_observability.digest_diverged_instances counts instances whose digest no longer matches the template (a divergence that can only happen if an operator manually added or removed pieces from an instance).

Counter-example (NOT proved here; documented as the only known way to mutate)

If an operator runs fn_iu_collection_remove_piece(instance_id, old_iu_id) + fn_iu_collection_add_piece(instance_id, new_iu_id) directly on the instance, the instance's manifest will diverge from the template — which is the intended behaviour for hand-edited instances and is surfaced by v_iu_template_observability.digest_diverged_instances.

Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.6-iu-core-18000x-template-versioning-bulk-scaleout-event-ops-open-goal/05-mutation-invariants-proof.md