feat(modules): author group trigger templates (event kinds) (§11 tail T2)
A [group] node may now declare EVENT trigger templates (kv/docs/files/ pubsub) that bind a group-owned handler. cron/queue/email stay app-only (rejected on a group at both the manifest and the reconcile layer). - Trigger is now owner-polymorphic (app_id: Option, group_id: Option), mirroring Script's Phase-4 reshape; TriggerRow carries group_id (#[sqlx(default)] so interactive RETURNING clauses still hydrate). - insert_trigger_tx takes a ScriptOwner (App XOR Group) and writes the group_id column; the queue advisory-lock branch is app-only. - TriggerRepo::list_for_group loads a group's templates; load_current's Group arm uses it so the diff is NoOp on re-apply and prune-able. - apply_service reconcile drops the "triggers are app-only" assumption; validate_bundle_for rejects non-event kinds on a group. The semantic- identity diff is already per-owner. - CLI manifest allows event-kind [[triggers]] on [group], rejects cron/queue/email there. Templates don't fire yet — the live dispatch union lands in T3. All trigger/apply/manifest unit tests green; clippy -D clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -67,17 +67,21 @@ impl Manifest {
|
||||
anyhow::bail!("manifest declares neither [app] nor [group]")
|
||||
}
|
||||
}
|
||||
// A group node owns only scripts + vars (+ secret names) — routes and
|
||||
// triggers are app concerns. Reject them early with a clear message.
|
||||
// A group node owns scripts + vars (+ secret names) and, as of §11 tail,
|
||||
// EVENT trigger TEMPLATES (kv/docs/files/pubsub) that fan out live to
|
||||
// descendant apps. Routes stay app-only; so do the stateful trigger
|
||||
// kinds (cron/queue/email need per-app state/secrets → materialization).
|
||||
if m.group.is_some() {
|
||||
if !m.routes.is_empty() {
|
||||
anyhow::bail!(
|
||||
"a [group] manifest cannot declare [[routes]] — routes bind to an app"
|
||||
);
|
||||
}
|
||||
if !m.triggers.is_empty() {
|
||||
let t = &m.triggers;
|
||||
if !t.cron.is_empty() || !t.queue.is_empty() || !t.email.is_empty() {
|
||||
anyhow::bail!(
|
||||
"a [group] manifest cannot declare [[triggers]] — triggers belong to an app"
|
||||
"a [group] trigger template must be an event kind \
|
||||
(kv, docs, files, pubsub); cron/queue/email are app-only"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user