CedarCopilot wants to merge 0 commits into staging from fix/guarded-subagent-agent-id
Live on prod, no production signal yet
OTEL fallback found no prod spans matching writeResourceGuarded (/subagents/ document writes) since deploy.
OTEL fallback found no prod spans matching writeResourceGuarded (/subagents/ document writes) since deploy.
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.
A subagent .md created straight through writeResourceGuarded (the low-level guarded write) persisted with no agent_id whenever the caller hadn't run prepareSubagentWrite first. Everything routed through writeDocumentTool (the chat document-write tool) is fine , it runs the hook. But direct guarded writes (staff/admin scripts, rollout tooling) skip it.
The consequence is a self-inconsistency in the guard: verifyPlaybook requires every cron/subagent <ref> target to carry agent_id in documents.metadata, so a playbook that references a just-written subagent gets rejected by the guard's own verifier:
VERIFY_FAILED: Ref "<id>" in cron trigger has no agent_id in metadata
… Subagent ref "<id>" is missing agent_id , execution will fall back to SHA-256 path
A standing heal-subagent-agent-ids.ts script exists precisely because this drift recurs.
writeResourceGuarded now runs prepareSubagentWrite(path, content, metadata) at the top, so every guarded subagent write is self-consistent , agent_id is injected into both the frontmatter and documents.metadata.
prepareSubagentWrite returns unchanged when an agent_id is already present./subagents/ paths are touched. Resource files and PLAYBOOK.md are unaffected.content is a fragment (or undefined), where injection is a no-op and only config metadata is forwarded , no behavior change for existing edits.One import + a ~13-line guarded call; no new control flow in the branches.
vitest run apps/server/src/services/playbook/__tests__/guarded-write.test.ts
New cases:
agent_id into the frontmatter and mirrors it into metadata.agent_id (what the verifier reads);agent_id keeps it.All 27 pre-existing guarded-write tests still pass (29 total). @zero/server typecheck clean.
🤖 Generated with Claude Code
The PR makes guarded subagent writes inject and mirror a stable agent_id, including for callers that bypass the higher-level document tool.
The PR appears safe to merge after addressing the non-blocking import-convention issue.
The guarded-write behavior is scoped and idempotent for the tested subagent paths, and the only accepted concern is the newly added relative import.
Files Needing Attention: apps/server/src/services/playbook/guarded-write.ts
| Filename | Overview |
|---|---|
| apps/server/src/services/playbook/guarded-write.ts | Adds centralized subagent identity preparation; behavior is appropriately scoped and idempotent, with one non-blocking import-style violation. |
| apps/server/src/services/playbook/tests/guarded-write.test.ts | Adds focused regression tests covering identifier injection into frontmatter and metadata and preservation of existing identifiers. |
### Issue 1
apps/server/src/services/playbook/guarded-write.ts:25
**Relative hook import added**
The new `prepareSubagentWrite` dependency uses a relative path instead of the repository-required absolute import convention, making this import inconsistent with the project’s prescribed module style.
```suggestion
import { prepareSubagentWrite } from '@/services/playbook/playbook-doc-hooks';
```
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.
Reviews (1): Last reviewed commit: ["fix(playbook): guarded subagent writ
Libra has not measured any production surfaces for this change yet.