CedarCopilot wants to merge 2 commits into staging from fix/playbook-system-subagent-org-refs
Live on prod, no production signal yet
Since 2026-08-25T14:25:41Z, production CloudWatch returned 0 lines for concrete playbook.compile, compilePlaybookForDoc, successful compilation, aborted save, writeCompiledPlaybook, or MISSING_SYSTEM_SUBAGENTS tokens. OTEL likewise returned 0 spans whose attributes/status.
Since 2026-08-25T14:25:41Z, there is no positive runtime evidence that compilePlaybookForDoc/checkSystemSubagents processed a playbook with global.anyBlock=null. OTEL returned 0 playbook/MISSING_SYSTEM_SUBAGENTS-tied spans; CloudWatch returned 0 exact MISSING_SYSTEM_SUBAGENTS.
Libra has verdicts on 0 of 2 tracked behaviors on prod; 2 are still being checked. Libra checks hourly for 3 days after each deploy.
checkSystemSubagents required the global <trigger type="any"> to reference the user-scoped crm-updater and next-steps doc ids specifically. Because writeDocumentGuarded reverts on any compile error, a playbook that fails this check is frozen: every subsequent write, to any unrelated part of it, compiles, hits this error, and gets rolled back.
Two legitimate configurations fail it today:
1. An org-linked user playbook referencing the shared org copy of a system subagent. userSubagentPaths is built from user/playbooks/{aopId}/subagents/%.md only (persist-playbook.ts:100), so a ref to organisation/playbooks/…/subagents/crm-updater.md looks like a ref to nothing. The markdown-era checker at verify-playbook.ts:305 never had this problem, because it matches on the referenced doc's path. The XML checker now does the same, with org subagent paths threaded through as orgSubagentPaths.
2. A secondary AOP with no any-block at all. Fundraising, Recruiting, and Non-deal AOPs commonly have crm-updater and next-steps docs seeded but never wired to a trigger. Editing such a playbook does not make that worse, so blocking every write to it is the wrong response. That case is now a warning that names the real condition (seeded but never run) instead of an error.
The hard error is unchanged where it matters: an any-block that exists but has dropped one of the two agents still fails.
Removes the copy of this check inlined in compile-playbook.ts. It duplicated the extracted checkSystemSubagents that runCompiledChecks already runs from the same function , which is why the failure surfaced with its message printed twice:
playbook verification failed ... (reverted): Global <trigger type="any"> must reference both
crm-updater and next-steps subagents; Global <trigger type="any"> must reference both
crm-updater and next-steps subagents
Compiled all 10 user playbooks across the 5 affected accounts with the new checker, reading real documents:
| before | after | |
|---|---|---|
| · Deals | FAIL (org ref) | pass |
| · Deals | FAIL (org ref) | pass |
| · Fundraising, Non-deal, Recruiting | FAIL (no any-block) | pass |
| · Event Partnerships | FAIL (no any-block) | pass |
| · Fundraising | FAIL (no any-block) | pass |
| the 4 remaining Deals playbooks | pass | pass |
No data edits to any account. This matters for Karim in particular: the "obvious" data fix (repoint his any-block at his own user copy) would have been harmful , his user copy is missing the "Read-only pilot , HubSpot writes are OFF" section that the org copy carries, so the swap would have let his crm-updater start writing to HubSpot on a read-only pilot.
pnpm --filter @zero/server exec vitest run src/services/playbook , 625 passed / 29 files, including 3 new cases: org ref accepted, neither-ref still errors, no-any-block warns.pnpm --filter @zero/server run types , clean.pnpm deps:check , no violations.🤖 Generated with Claude Code
The PR changes compiled playbook validation so org-scoped system-subagent references are recognized, playbooks without a global any-trigger receive a warning rather than an error, and the duplicate inline validation is removed.
The PR needs the organization subagent map scoped to the linked org AOP before merging, otherwise a reference to a different AOP's same-named agent can satisfy the required-agent guard.
The new path-based validation receives subagents from every org AOP and compares only filename suffixes, so i
Libra has not measured any production surfaces for this change yet.