Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix(documents): keep a subagent's agent_id stable across writes

merged#2589CedarCopilot

CedarCopilot wants to merge 3 commits into staging from fix/subagent-agent-id-strip

Live on prod, no production signal yetTimeline and evidence
  1. Opened
    Aug 26, 2026, 11:23 AM
  2. Aug 26, 2026, 2:13 PM
  3. Merged
    Aug 26, 2026, 5:32 PM
  4. Live on staging
    Aug 26, 2026, 5:32 PM
  5. Live on prod
    Aug 26, 2026, 5:32 PM
  6. Observed 2 days
    Aug 26, 2026, 5:32 PM
  7. Pipelines steady after this deploy
    Aug 26, 2026, 5:32 PM
  8. Pipelines steady after this deploy
    Aug 26, 2026, 5:32 PM
  9. Unobserved

    Live on prod, no production signal yet

    Aug 29, 2026, 12:10 AM

Behaviors Libra is checking

A subagent document with no valid stored, incoming, or frontmatter agent_id receives a new metadata agent_id so it can be resolved as a subagent without modifying a frontmatter-less body.Inconclusivelow confidence

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.

prod, checked Aug 28, 2026, 10:09 PM
When a subagent document has frontmatter whose agent_id disagrees with its canonical persisted identity, writes update only that frontmatter field to match metadata while leaving body examples and other body content unchanged.Inconclusivelow confidence

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.

prod, checked Aug 28, 2026, 11:11 PM
Writing an existing subagent document preserves its stored agent_id regardless of incoming metadata or rewritten frontmatter, while retaining stored sibling metadata that the write does not specify.Inconclusivelow confidence

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.

prod, checked Aug 29, 2026, 12:10 AM
Writing an existing subagent document preserves its stored agent_id even when incoming metadata or regenerated frontmatter supplies no ID or a different ID, so existing subagent references remain linked.Inconclusivelow confidence

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.

staging, checked Aug 29, 2026, 12:20 PM
A subagent document with no stored ID adopts an incoming or frontmatter ID, or receives a new UUID in metadata when authored as a body-only document, making it resolvable as a subagent.Inconclusivelow confidence

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.

staging, checked Aug 29, 2026, 12:20 PM
When a subagent frontmatter agent_id disagrees with the canonical stored identity, the frontmatter ID is rewritten to match metadata without modifying agent_id examples in the document body.Inconclusivelow confidence

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.

staging, checked Aug 29, 2026, 2:22 PM

Failures attributed to this change

No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 4 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Aug 26, 2026, 10:10 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 0 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Aug 26, 2026, 11:02 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 0 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Aug 26, 2026, 11:10 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 0 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Aug 26, 2026, 11:10 PM
No prod customers are affected while this is only in staging. If promoted, prod impact is unknown because Libra still needs a concrete exception, route, and failed user action before assigning severity. 0 hits · no retained affected-user count · no retained trace sample.Introducedmedium confidence

internal_only

staging, first seen Aug 27, 2026, 9:17 AM

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.

Problem

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.

Fix

Route the metadata through resolveSubagentMetadata. For /subagents/ paths it merges over the stored object and pins agent_id by precedence:

  1. explicit incoming id (deliberate re-identification)
  2. stored id
  3. id parsed out of the content being written
  4. fresh UUID

That also closes two adjacent holes in injectSubagentAgentId:

  • Re-mint. It mints a new UUID whenever frontmatter exists without an agent_id: line, silently abandoning the previous identity. The stored id now wins.
  • Blind spot. It returns early on content not starting with ---, 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.

Production impact

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 refs449
Distinct subagent docs415
Missing agent_id entirely1
metadata/frontmatter mismatch or one-sided0

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.

Testing

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

Greptile Summary

The PR preserves subagent identity across document writes by merging stored metadata, adopting an existing content ID, or minting a new UUID when needed.

  • Adds centralized subagent metadata resolution to the document write path.
  • Preserves stored identity and sibling subagent configuration across writes.
  • Adds regression coverage for stripped frontmatter, bare-body documents, explicit overrides, and non-subagent behavior.
  • Content-based identity extraction should be restricted to frontmatter rather than matching arbitrary body lines.

Confidence Score: 4/5

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

Important Files Changed

FilenameOverview
apps/server/src/services/
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/src/services/documents/__tests__/subagent-agent-id.test.tsno production surface mapped
  • apps/server/src/services/documents/convention-paths.tsno production surface mapped
  • apps/server/src/services/documents/index.tsno production surface mapped