KB-7D6D

11000x · 03 — Migration 030: event_outbox.event_domain CHECK += 'piece' (10000x defect fix)

4 min read Revision 1
iu-corev0.611000xmigration-030event_outboxcheck-constraint10000x-defectlive-apply

11000x · 03 — Migration 030 (10000x defect fix, LIVE applied)

Defect surfaced

During Phase B of the bounded BEGIN/ROLLBACK proof:

ERROR:  new row for relation "event_outbox" violates check constraint "event_outbox_event_domain_check"
DETAIL:  Failing row contains (... piece, superseded, update, immediate, ...)

The pre-existing CHECK constraint:

CHECK (event_domain = ANY (ARRAY[
  'iu', 'birth_registry', 'governance', 'tac',
  'kg', 'system', 'dot', 'health']))

Did not include 'piece', even though the 10000x 01-piece-event-type-seed.sql happily registered 6 piece.* event types in event_type_registry with event_domain='piece'.

This was a dormant defect: the registry insert and the outbox insert had different validation paths (registry insert had no FK to a domain enum; outbox insert is gated by both a trigger validator AND this column CHECK). Because 10000x never actually emitted a piece event, the defect lay invisible until 11000x's bounded proof.

Fix

Migration 030 drops + recreates the CHECK as a strict superset:

ALTER TABLE public.event_outbox DROP CONSTRAINT event_outbox_event_domain_check;
ALTER TABLE public.event_outbox ADD CONSTRAINT event_outbox_event_domain_check
    CHECK (event_domain = ANY (ARRAY[
        'iu','birth_registry','governance','tac',
        'kg','system','dot','health',
        'piece']));

Plus a DO $$ ... RAISE EXCEPTION post-apply that re-reads pg_get_constraintdef and aborts if 'piece' isn't present.

Live apply transcript

BEGIN
ALTER TABLE
ALTER TABLE
DO
COMMIT

Existing 121,289 event_outbox rows were re-validated against the new CHECK (PostgreSQL default behaviour without NOT VALID). All passed — the new constraint is a strict superset.

D9 inventory impact

None. ALTER CONSTRAINT changes no row in pg_class / pg_proc / pg_trigger / dot_config / event_type_registry / outbound_route / iu_sql_event_route. The D9 scan inventory is unchanged.

Rollback caveat

rollback/030_event_outbox_event_domain_add_piece.rollback.sql will fail if any event_outbox row has event_domain='piece' (PostgreSQL revalidates rows when ADD CONSTRAINT runs). Inspect first:

SELECT count(*) FROM public.event_outbox WHERE event_domain='piece';

At the time of this report: 0 piece rows persisted (the bounded proof rolled back; gates remain closed; no live emission has happened).

Lesson written to memory

[[feedback-event-outbox-check-vs-registry-drift]]:

Registering an event_domain in event_type_registry is necessary but NOT sufficient. The event_outbox.event_domain CHECK constraint must also accept the new domain. Before declaring an event-type seed "done", attempt one BEGIN/INSERT/ROLLBACK against event_outbox to confirm the column constraint allows it.

Why: 10000x ran its 04-author-mode-verification.sql against event_type_registry only and reported PASS. The CHECK on event_outbox was not in the verification set. 11000x's bounded proof attempted a real INSERT (still under BEGIN/ROLLBACK) and surfaced the defect immediately.

How to apply: any future macro that adds a new event_domain or event_stream value should include — in its verification pack — at least one BEGIN…INSERT…ROLLBACK round-trip into the actual outbox table.

Back to Knowledge Hub knowledge/dev/laws/dieu44-trien-khai/v0.6-iu-core-11000x-piece-event-runtime-product-factory-open-goal/03-migration-030-check-extension.md