Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

feat(debug): add test-run action for suppressed-by-default subagent testing

merged#2720CedarCopilot

CedarCopilot wants to merge 0 commits into docs/cedar-authoring-skill-fixes from feat/debug-test-run

Not deployedTimeline and evidence
  1. Opened
    Sep 7, 2026, 10:45 AM
  2. Merged
    Sep 7, 2026, 2:02 PM
  3. Not deployed

    Not deployed

    Pending

Libra has no production signal for this change yet because it has not deployed. Libra checks hourly for 3 days after each deploy.

Summary

Stacked on #2717 (docs/cedar-authoring-skill-fixes) , this is the code half of a gap that doc identifies: the only existing "run it now" path (agent.runNow, the frontend's Run Now button) always sends for real today, with no suppression option, so there was no safe way to test-run a newly authored or edited subagent from a live chat session before this.

  • Adds test-run to the debug MCP tool, wrapping the SAME execution core agent.runNow already uses (runSingleSubagent for an isolated cron-triggered subagent, runPlaybookSectionExecution for the before_meeting bundle on one conversation).
  • deliverForReal defaults to false: every run is wrapped in withSuppressedDelivery and returns the full captured trace (notify calls + tool calls) without sending anything for real.
  • deliverForReal: true sends for real. On an org-level agent this requires org admin/owner , checked via authorize() (a new subagent_execution:org: 'ORG_ADMIN' row in DEFAULT_POLICY), not a direct role read. A user-level agent only ever touches the caller's own destinations, so no extra check applies there.
  • Self-scoped to the caller's own AOPs, or an org AOP the caller's own AOP is actually linked to.
  • Updates cedar-configuration/building-an-agent.md and playbook-authoring/SKILL.md to point a live chat session at test-run; cedar-cli/subagent-runner remain the correct path for an internal engineering session with shell access (cedar-cli isn't reachable from a live chat session at all).

Self-review (thermo-review) , two real issues found and fixed before this was opened

  1. File size. The first pass pushed debugTool.ts past 1,000 lines. Extracted the actual dispatch/scoping logic into services/playbook/test-run-subagent.ts, mirroring the split trpc/routes/admin-subagent.ts already uses ("the risky routing is delegated to the pure, unit-tested subagent-dispatch module; these procedures are the I/O shell around it"). debugTool.ts's case is now a thin shell; the service has its own direct unit tests.
  2. Dependency-direction violation. The first pass read user.organizationRole directly and called isOrgAdminRole. pnpm deps:check caught this immediately , role-predicates-stay-inside-the-auth-authority forbids importing that predicate outside its one carved-out caller. Fixed by adding the new DEFAULT_POLICY row and calling authorize() instead, per that module's own contract ("Call authorize() instead, at the point the EFFECTIVE TARGET resolves"). The existing DEFAULT_POLICY-driven exhaustive test suite in authorize.test.ts picked up the new row automatically , no hand-written test needed for it.

Also fixed 3 non-null assertions (!) from the first draft , one was masking a real gap (RunSingleSubagentResult isn't a discriminated union, so ok: true doesn't type-guarantee agentId; replaced with a real runtime check), the other two were TS closure-widening false positives fixed by binding to a const before the closure rather than asserting past them.

Known gap, disclosed rather than hidden: test coverage is cron-path only (the 4 scenarios asked for: default-suppressed, user-level real-send, org-level refused-for-non-admin, org-level allowed-for-admin). The before_meeting branch has no dedicated test yet , mocking db.query.crmConversations.findFirst cleanly is a bit more setup than the table-routed fake this suite already has, and didn't fit the time box for this pass.

Test plan

  • pnpm --filter @zero/server run types , clean
  • pnpm deps:check , clean (was failing before the authorize() fix)
  • pnpm --filter @zero/server exec vitest run src/mastra/tools/debug/__tests__/debugTool.test.ts src/services/playbook/__tests__/test-run-subagent.test.ts src/services/auth/__tests__/authorize.test.ts src/services/auth/__tests__/role-reads-confined-to-authority.test.ts , 387/387 pass

šŸ¤– Generated with Claude Code

https:/

Show production surfaces and changed-file mapping

Production surfaces

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

Changed files → surfaces

  • apps/server/.claude/skills/cedar-configuration/building-an-agent.mdno production surface mapped
  • apps/server/.claude/skills/playbook-authoring/SKILL.mdno production surface mapped
  • apps/server/src/mastra/mcp/external/__tests__/mutating-actions-are-administrable.test.tsno production surface mapped
  • apps/server/src/mastra/tools/debug/__tests__/debugTool.test.tsno production surface mapped
  • apps/server/src/mastra/tools/debug/debugTool.tsno production surface mapped
  • apps/server/src/services/auth/authorize.tsno production surface mapped
  • apps/server/src/services/playbook/__tests__/test-run-subagent.test.tsno production surface mapped
  • apps/server/src/services/playbook/test-run-subagent.tsno production surface mapped