- 0063: partial unique index on (app_id, materialized_from) + ON CONFLICT DO NOTHING in the reconciler, so a materialized copy is idempotent under concurrent reconciles (two parallel app-creates no longer duplicate a copy). - stateful_templates journey: a group cron template + a descendant app → a materialized cron copy in `pic triggers ls --app`; re-apply is a NoOp. - docs (§4.5 + CLAUDE.md): cron+queue materialization implemented; group EMAIL templates deferred (per-descendant inbound-secret reseal needs the master key threaded through the hooks + a secret-ref schema) and cleanly rejected at apply for now, alongside a `materialized` ls column. Completes the v1.2 near-term batch (M1-M5, cron+queue); group email is the one scoped follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
13 lines
741 B
SQL
13 lines
741 B
SQL
-- §4.5 M5: exactly one materialized copy per (descendant app, source template).
|
|
--
|
|
-- The materialization reconciler (materialize::rematerialize_stateful_templates)
|
|
-- runs on every tree/apply mutation, and two of them can run concurrently (e.g.
|
|
-- two apps created in parallel each trigger a full reconcile). Both compute the
|
|
-- same "missing" (app, template) pair and both try to insert the copy — without
|
|
-- a constraint that races into a DUPLICATE. This partial unique index makes the
|
|
-- second insert a no-op (the reconciler uses `ON CONFLICT DO NOTHING`), so a
|
|
-- copy is idempotent under concurrency.
|
|
|
|
CREATE UNIQUE INDEX triggers_materialized_uidx
|
|
ON triggers (app_id, materialized_from) WHERE materialized_from IS NOT NULL;
|