70000x · 07 — Lessons (6 new + 4 refreshed)</title> <parameter name="tags">["iu-core","70000x","lessons","feedback"]
70000x · 07 — Lessons (new + refreshed)
NEW
L1 — local all all trust is the workflow_admin unblock channel
50000x packaged mig 036 as authored-only because no workflow_admin auth path was known. 70000x live discovery: inside the postgres container, pg_hba.conf has local all all trust. Any role (including superusers) can connect over the Unix socket via docker exec -i postgres psql -U <role> -d directus with no password. With workflow_admin connection available, mig 036 applies as the owner — no GRANT needed.
Rule: before declaring a privilege block, probe with -U <role-name> via the trust channel.
→ [[feedback-pg-hba-local-trust-unblocks-role-channel]]
L2 — information_unit.status does not exist; it's lifecycle_status
T01 first iteration failed with column "status" does not exist. The actual column is lifecycle_status.
Rule: for IU lifecycle filtering, always use lifecycle_status.
→ [[feedback-information-unit-status-column-is-lifecycle-status]]
L3 — event_outbox.payload does not exist; it's safe_payload
T05 first iteration filtered WHERE payload->>'phase'=... and failed. The actual JSONB column is safe_payload (the redacted-safe view, paired with payload_classification).
Rule: for event_outbox JSONB extraction, always use safe_payload.
→ [[feedback-event-outbox-jsonb-column-is-safe-payload]]
L4 — fn_iu_create unit_kind vocab is {design_doc_section, law_unit}
T04 first iteration passed "unit_kind":"document" and got Not in vocab. Available: design_doc_section, law_unit. Cross-link with [[feedback-section-type-and-piece-role-vocab-enforced]] — section_type vocab is broader, unit_kind is just those two.
Rule: mint with unit_kind ∈ {design_doc_section, law_unit}. For composed test docs, design_doc_section is the default.
→ [[feedback-fn-iu-create-unit-kind-vocab-is-two]]
L5 — iu_sql_link.link_role CHECK vocab is 11 roles, NOT reference
T06 first iteration failed iu_sql_link_link_role_chk. Allowed: represents, describes, governs, source_of_truth_for, derived_from, emits_event_for, consumes_event_from, validates, renders, indexes, reports_on.
Rule: for iu_sql_link.link_role, pick from the 11-role vocab.
→ [[feedback-iu-sql-link-link-role-vocab-eleven]]
L6 — fn_iu_sql_link_inbound_capture is a trigger function
T06 first tried SELECT fn_iu_sql_link_inbound_capture() and got trigger functions can only be called as triggers. The function returns trigger and must be invoked via a trigger.
Rule: before calling a fn_iu_* directly, check its return type. If trigger, it must be invoked via a trigger. Identify with pg_proc.prorettype = 'trigger'::regtype.
→ [[feedback-trigger-functions-only-callable-via-triggers]]
REFRESHED
R1 — [[feedback-pinning-tests-bump-per-macro]]
At 70000x, 15 pinning sites + DDL SSOT + 1 total assertion needed coordinated bumps from 65/6/181 to 66/7/183 for a single trigger+fn DDL change. Sites grow linearly with every SSOT macro. Future opportunity: a pinning_constants module — cuts bump radius from 15 to 1.
R2 — [[feedback-in-tx-gate-toggle-reversibility]]
Proven AGAIN at 70000x T04 (composer_enabled) + T05 (piece_event_runtime.emit_enabled). Durable across 7 macros. Defensive pre-ROLLBACK gate-flip-back is the right default.
R3 — [[feedback-honest-channel-block-beats-partial-trigger]]
50000x's honest channel-block was vindicated at 70000x: when the channel was discovered (local trust line), unblock was one-step with NO state recovery needed. A half-application at 50000x would have left a corrupt fn+no-trigger mix.
R4 — [[feedback-no-truncated-uuids-for-apply-sql]]
Refreshed at T06: used (SELECT id FROM ... LIMIT 1) inline subqueries to fetch full uuids in-place for FK and jsonb row_pk fields.