CedarCopilot wants to merge 3 commits into staging from fix/subagent-agent-id-strip
Live on prod, no production signal yet
Only 10 prod OTEL spans matching writeDocument reached Libra since the deploy, below the 20-span floor for calling a change verified. Nothing is failing; there is not yet enough traffic to confirm it.
Only 10 prod OTEL spans matching writeDocument reached Libra since the deploy, below the 20-span floor for calling a change verified. Nothing is failing; there is not yet enough traffic to confirm it.
Only 10 prod OTEL spans matching writeDocument reached Libra since the deploy, below the 20-span floor for calling a change verified. Nothing is failing; there is not yet enough traffic to confirm it.
Only 4 prod OTEL spans matching writeDocument reached Libra since the deploy, below the 20-span floor for calling a change verified. Nothing is failing; there is not yet enough traffic to confirm it.
Only 4 prod OTEL spans matching writeDocument reached Libra since the deploy, below the 20-span floor for calling a change verified. Nothing is failing; there is not yet enough traffic to confirm it.
Only 4 prod OTEL spans matching writeDocument reached Libra since the deploy, below the 20-span floor for calling a change verified. Nothing is failing; there is not yet enough traffic to confirm it.
internal_only
internal_only
internal_only
internal_only
internal_only
Libra has verdicts on 0 of 3 tracked behaviors on prod; 3 are still being checked. Libra checks hourly for 3 days after each deploy.
A subagent's agent_id is its identity , runSubagentTool resolves the doc by it, findSubagentByAgentId keys on metadata->>'agent_id', and the playbook verifier rejects any cron/subagent <ref> pointing at a doc without one.
writeDocument persisted it with REPLACE semantics:
const finalMetadata = metadata ?? existing?.metadata ?? null;
So any caller supplying a metadata object , an avatar update, a whole-file upsert carrying output_field_ids , whose content no longer had the agent_id: frontmatter line overwrote the stored id with an object that had none, unlinking the subagent from every ref that resolved it.
prepareSubagentWrite can't defend against this: it's pure and never sees the existing row.
Route the metadata through resolveSubagentMetadata. For /subagents/ paths it merges over the stored object and pins agent_id by precedence:
That also closes two adjacent holes in injectSubagentAgentId:
agent_id: line, silently abandoning the previous identity. The stored id now wins.---, so a subagent authored as a bare body never received an id at all. One is now minted into metadata , safe because resolveSubagentAgentId reads metadata first and frontmatter second, so the author's body is never rewritten.Non-subagent paths keep the previous behaviour exactly.
Placement: convention-paths.ts already owns isSubagentDocPath. playbook-doc-hooks.ts would be the more obvious home but imports ../documents, so putting it there closes a cycle.
Audited every subagent doc wired into a Deals playbook for users with agentExecutionEnabled = true (63 users, 122 playbook docs, every <ref id> parsed out):
| Wired subagent refs | 449 |
| Distinct subagent docs | 415 |
Missing agent_id entirely | 1 |
| metadata/frontmatter mismatch or one-sided | 0 |
The single hit is organisation/playbooks/86890e2d-.../subagents/inbound-lead-research.md (Aspireiq), which is also the only wired doc with no frontmatter at all , exactly the blind-spot population. That doc is being repaired separately; this PR is the write-path fix only.
8 new tests in subagent-agent-id.test.ts covering both strip paths, the re-mint, the blind spot, content adoption, explicit override, blank-id handling, and non-subagent passthrough.
Reverting the helper to the old semantics fails 6 of 8. With the fix: 8/8 pass, and the full documents + playbook suites are green (781 passed, 0 failed).
🤖 Generated with Claude Code
The PR preserves subagent identity across document writes by merging stored metadata, adopting an existing content ID, or minting a new UUID when needed.
The PR appears safe to merge with a non-blocking correction to restrict content-derived agent IDs to actual frontmatter.
The identity-preservation behavior is otherwise coherent, but the new multiline regex can persist arbitrary body text as a subagent identity when no incoming or stored ID exists.
Files Needing Attention: apps/server/src/services/documents/convention-paths.ts
| Filename | Overview |
|---|---|
| apps/server/src/services/ |
Libra has not measured any production surfaces for this change yet.