Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

Playbook trigger sources: fire an agent from a Slack channel

merged#2575CedarCopilot

CedarCopilot wants to merge 4 commits into staging from feat/slack-trigger-sources

Live on prod, no production signal yetTimeline and evidence
  1. Opened
    Aug 24, 2026, 8:59 PM
  2. Aug 24, 2026, 10:36 PM
  3. Merged
    Aug 25, 2026, 11:07 AM
  4. Live on prod
    Aug 25, 2026, 11:07 AM
  5. Observed 31 hours
    Aug 25, 2026, 11:07 AM
  6. Pipelines steady after this deploy
    Aug 25, 2026, 11:07 AM
  7. Unobserved

    Live on prod, no production signal yet

    Aug 26, 2026, 6:04 PM

Behaviors Libra is checking

AOP context behavior from db/aop-schema.ts, routes/aop.ts keeps working in prod.Inconclusivelow confidence

Strict CloudWatch fallback saw 3,233 success-shaped log lines matching pipeline, analytics, quota, aop, but no tied operation was present, so Libra is not calling this working.

prod, checked Aug 26, 2026, 5:57 PM
CRM integration behavior from tools/applyConfigChangeTool.ts keeps working in prod.Inconclusivelow confidence

Strict CloudWatch fallback found 91 recent prod failure log lines for [UpdateExternalCrmWorkflow] Driver update failed (Step updateOpportunityField failed: HTTP 400: [{\), but the same failure had 83 log lines in the comparable pre-deploy baseline. Libra is not blaming this PR.

prod, checked Aug 26, 2026, 5:57 PM
Data model behavior from migrations/<id>.sql, migrations/<id>.sql keeps working in prod.Inconclusivelow confidence

Strict CloudWatch fallback saw 7,079 success-shaped log lines matching /<id>.sql, /<id>.sql, applyconfigchangetool, sherlock_system_knowledge, playbook-anatomy, but no tied operation was present, so Libra is not calling this working.

prod, checked Aug 26, 2026, 6:04 PM
Slack integration behavior from slack/slack-api.ts, slack/slack-events-webhook.ts keeps working in prod.Inconclusivelow confidence

Strict CloudWatch fallback found 2 recent prod failure log lines for [backfillSlackChannelHistory] Backfill failed (Failed query: insert into \), but the same failure had 7 log lines in the comparable pre-deploy baseline. Libra is not blaming this PR intent without a.

prod, checked Aug 26, 2026, 6:04 PM

Libra has verdicts on 0 of 4 tracked behaviors on prod; 4 are still being checked. Libra checks hourly for 3 days after each deploy.

⚠️ Read first: the migration is already applied to production

0047 and 0048 were applied by hand on 2026-08-24 (single prod/staging database), in one transaction, verified. Do not re-run them , and a reviewer does not need to. Both use IF NOT EXISTS, so a later drizzle-kit migrate is a harmless no-op, but Drizzle's journal does not know they ran.

This PR also edits 0047_playbook_webhook.sql, which someone else authored. That is deliberate and safe: it had never been applied on any database, because it could not be , see below.

Why this feature never worked

Two independent reasons, which is likely why it was abandoned:

  1. Never applied. The playbook_webhook table did not exist in production.
  2. Unapplicable. 0047 declared org_id as text against organizations.id (uuid). Postgres refuses that FK: "Key columns "org_id" and "id" are of incompatible types: text and uuid." Proved in a rolled-back transaction. Every other org FK in the schema is uuid.

The unit tests stayed green throughout, because the dispatch test mocked runPlaybookSectionExecution , the only step that could have failed loudly. Five passing tests over a feature that could not run at all. This PR replaces that mock.

What this adds

A webhook row gains a source , what fires it. http is the original behaviour and the default for every existing row. slack_channel is fired by Cedar's own Slack ingest, reaching the same queue, dispatch and blocks. One entrance more, not a second machine; playbook compilation learns nothing about Slack.

Three behaviours worth reviewing closely:

  • Fan-out is deliberate. One message fires every registration , three reps who each registered get three executions. Only org scope is singular, enforced by a partial unique index rather than by code.
  • Both paths run. A channel that is both linked to a deal and registered as a source keeps its deal timeline and runs its agent. So two executions for one message is expected.
  • topLevelOnly defaults on. The self-post filter catches Cedar's bot, not a human replying beneath the agent's own post , which would otherwise re-fire it forever.

Service parity

A webhook could previously only be minted from the playbook editor. Extracting the rules into playbook-webhook-registration.ts is what surfaced the authz hole: ownership was checked in the tRPC route only, so config-write could register a webhook against another user's AOP.

SurfaceBeforeAfter
cedar-clino webhook commandcreate · list · enable · disable · test
config-writeno webhook change typemanage_playbook_webhook
tRPCcreate only+ listPlaybookWebhooks, testFirePlaybookWebhook
playbook-authoring / anatomywebhook absentfull recipe + trigger reference
Sherlocknothing10-step failure ladder
account-setupnothingchannel-triggered agent recipe

cedar-cli webhook test --id <id> --text "…" fires through the real dispatch path and waits, printing which agents ran. outcome: null means nothing ran, which almost always means no <trigger> block references the row.

Fixes to the pre-existing feature

  • No DLQ on PlaybookWebhookQueue , a persistently failing dispatch retried, then vanished.
  • 5-minute visibility timeout against an unbounded consumer; a redelivery re-runs the whole agent and posts its output twice. Now 15, matching calendar-sync.
  • Org-scope needed no admin role , any member could create an org-wide webhook.
  • Duplicate-org-source guard read the wrong error property, so it would have 500'd instead of explaining itself (crm.ts:553 documents this exact trap).

Verified

types 0 errors · deps:check clean, 1535 modules · 758 tests / 44 files passing (services/playbook, services/integrations/slack, skills/account-config).

Constraints were exercised against live Postgres in a rolled-back

Show production surfaces and changed-file mapping

Production surfaces

Libra has not measured any production surfaces for this change yet.

Changed files → surfaces

  • .claude/skills/account-setup-from-transcript/SKILL.mdno production surface mapped
  • apps/server/.claude/skills/cedar-configuration/playbook-anatomy.mdno production surface mapped
  • apps/server/.claude/skills/playbook-authoring/SKILL.mdno production surface mapped
  • apps/server/.claude/skills/sherlock/SHERLOCK_SYSTEM_KNOWLEDGE.mdno production surface mapped
  • apps/server/src/cli/index.tsno production surface mapped
  • apps/server/src/cli/webhook.tsno production surface mapped
  • apps/server/src/db/aop-schema.tsno production surface mapped
  • apps/server/src/db/migrations/0047_playbook_webhook.sqlno production surface mapped
  • apps/server/src/db/migrations/0048_playbook_webhook_sources.sqlno production surface mapped
  • apps/server/src/mastra/skills/account-config/tools/applyConfigChangeTool.tsno production surface mapped
  • apps/server/src/mastra/tools/config/config-write-tool.tsno production surface mapped
  • apps/server/src/services/integrations/slack/slack-api.tsno production surface mapped
  • apps/server/src/services/integrations/slack/slack-events-webhook.tsno production surface mapped
  • apps/server/src/services/playbook/__tests__/playbook-webhook-execution.test.tsno production surface mapped
  • apps/server/src/services/playbook/__tests__/playbook-webhook-registration.test.tsno production surface mapped
  • apps/server/src/services/playbook/__tests__/playbook-webhook-sources.test.tsno production surface mapped
  • apps/server/src/services/playbook/playbook-webhook-registration.tsno production surface mapped
  • apps/server/src/services/playbook/playbook-webhook-sources.tsno production surface mapped
  • apps/server/src/services/playbook/playbook-webhook.tsno production surface mapped
  • apps/server/src/trpc/routes/aop.tsno production surface mapped
  • aws/lib/stacks/data-stack.tsno production surface mapped
  • docs/design/playbook-trigger-sources-slack-channel.mdno production surface mapped