KB-1765

18000x · 10 — Lessons + Carry-forward (4 new lessons: two-narrow-sidecars, fn_iu_compose=bulk-mechanism, mutation-invariant, live-tag-vs-delivery-separable)

5 min read Revision 1
iu-corev0.618000xlessonscarry-forwardsidecar-designpinning-tests19000x-roadmap

18000x · 10 — Lessons + Carry-forward

Lessons (new this macro)

L1 — Versioning is a sidecar, not a composition concern

The 15000x productization marked templates with a sidecar; 18000x extends with a SECOND sidecar (iu_collection_template_version) for version-chain identity. Both sidecars key on collection_id. They could have been one wider table, but the two-table split:

  • keeps mig 031 rollback row count separable from mig 032;
  • lets a template exist WITHOUT a version chain (legitimate — see tpl:file:status-report/v1);
  • lets a future "lightweight" entry (e.g. ad-hoc bookmark or favourite) live without dragging in versioning columns.

Rule: when adding a second productization concern to a marker table, prefer a second PK-shared sidecar over widening the existing sidecar. Two narrow sidecars = two narrow rollbacks.

L2 — fn_iu_compose iu_id-reuse branch IS the bulk-instance mechanism

12000x discovered it; 15000x proved 1 instance; 18000x proved N=3 instances in one TX. The pattern:

v_pieces := array_agg(m.iu_id ORDER BY m.piece_order)
            FROM iu_piece_membership WHERE collection_id=<template> AND membership_status='active';
v_pieces_json := jsonb_agg(jsonb_build_object('role','body','iu_id', x) FROM unnest(v_pieces));
fn_iu_compose('tpl-inst:.../bulk-i', kind, title, desc, v_pieces_json, actor);

Rule: don't author a fn_iu_create_bulk_instancesfn_iu_compose already does it. Bulk = loop over fn_iu_compose with the same pieces_json. Each call is its own atomic compose.

L3 — Mutation invariant comes from digest-keyed-on-iu_id

fn_iu_collection_manifest_refresh hashes (piece_order, iu_id, piece_role) — see [[feedback-manifest-digest-keyed-on-piece-graph]]. Supersede mints a new iu_id and leaves memberships alone, so digest is byte-identical. The invariant is automatic — no policy code needed.

Rule: when a substrate's primitive (here: manifest_digest) is computed from immutable inputs (iu_id), in-place mutations of those inputs' DOWNSTREAM state cannot propagate without an explicit operator action.

L4 — Internal LIVE event proof is just two gate flips inside a TX

15000x emitted with dry_run_only=true; 18000x flipped dry_run_only=false inside the same in-TX gate-toggle pattern. The proof shape is unchanged; only the tag in safe_payload.emit_mode changes (dry_runlive). External delivery is governed by SEPARATE gates (iu_core.delivery_enabled + iu_core.delivery_live_routes) and remains untouched.

Rule: "live tagging" and "external delivery" are separable concerns in this substrate. Future macros can durably enable live tagging without touching delivery, and vice-versa.

Memory entries proposed (4)

  1. [[feedback-two-narrow-sidecars-over-one-wide-sidecar]] (NEW) — see L1
  2. [[feedback-fn-iu-compose-is-the-bulk-instance-mechanism]] (NEW) — see L2
  3. [[feedback-pinning-tests-bump-per-macro]] (REFRESH) — 18000x bumped 13 (was 12 at 15000x)
  4. [[project-dot_iu_cutter_v0_6_iu_core_18000x_template_versioning_bulk_scaleout_event_ops_closeout]] (NEW) — full project entry

Carry-forward to 19000x+

# Item Target
1 Birth-gate PILOT noise (P-pub1/P-pub2 warnings) — silence for iu_core/template/* addresses 19000x
2 Auto-instantiate productization: 1 event_type + 1 fn + 1 route + 1 gate (per 08-…) 19000x
3 Live (durable, non-dry-run) piece event delivery — needs external worker contract ≥19500x
4 tpl:file:status-report family — opt-in to version chain when v2 ships when v2 authored
5 Template piece mutation propagation worker (NOT silent; explicit operator command) ≥19000x
6 Retention enable after 30+ day soak ≥17000x (operator)
7 PR #669 merge / Nuxt deploy external (different clone)
8 VPS Linux timer / Grafana monitoring fallback 19000x
9 Bulk-instance retire / soft-delete DOT (current state: durable, addressable, no fast-cleanup verb) 19000x
10 Cross-template piece sharing observability extension — show "this piece is used by N templates" 19000x

What 18000x intentionally did NOT do

  • Did NOT add a new healthcheck surface — 8 is the right size; observability lives in views.
  • Did NOT add a new event_type for template.published — would require event_outbox CHECK constraint update (per 10000x defect lesson); deferred until the consumer is authored.
  • Did NOT productize fn_iu_auto_instantiate_from_template — sandbox proof is the deliverable; durable function ships in 19000x.
  • Did NOT touch the 12000x untracked ops dir.
  • Did NOT enable retention, deploy Nuxt, or merge PR #669 (all external/constitution-forbidden).
  • Did NOT productize template piece mutation propagation — the invariant is "explicit v(N+1) compose", which is exactly what the version chain provides.
Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.6-iu-core-18000x-template-versioning-bulk-scaleout-event-ops-open-goal/10-lessons-and-carry-forward.md