Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix(documents): resolve a document id, and teach the resolver contact/

merged#2569CedarCopilot

CedarCopilot wants to merge 3 commits into staging from fix/document-address-resolution

Live on prod since Mon, 1 of 4 behaviors workingTimeline and evidence
  1. Opened
    Aug 24, 2026, 12:09 PM
  2. Aug 24, 2026, 3:15 PM
  3. Merged
    Aug 24, 2026, 3:30 PM
  4. Live on prod
    Aug 24, 2026, 3:30 PM
  5. Observed 47 hours
    Aug 24, 2026, 3:30 PM
  6. Pipelines steady after this deploy
    Aug 24, 2026, 3:30 PM
  7. Verified

    Live on prod since Mon, 1 of 4 behaviors working

    Aug 26, 2026, 2:03 PM

Behaviors Libra is checking

Contact profile documents under `contact/` can be resolved, read, and written as organization-shared documents instead of failing as an unknown path prefix.Workingmedium confidence

Since the deploy, 49 prod OTEL spans matching contact/{personKey}/profile ran with 0 error spans. Libra found no comparable traffic on this surface in the 44h before the deploy, so there is no before-and-after contrast. Confidence is medium because there was no pre-deploy.

prod, checked Aug 26, 2026, 11:03 AM
Agents can read a document by its UUID, including an appended content shortcut, with the UUID resolved to its virtual path before normal document access handling runs.Inconclusivelow confidence

Only 11 prod OTEL spans matching read-document 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 26, 2026, 12:03 PM
A document UUID from another organization resolves as not found and does not expose its virtual path or content.Inconclusivelow confidence

Only 11 prod OTEL spans matching read-document 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 26, 2026, 1:03 PM
Document writes now use the same path-based user scope as reads and listings, preventing organization-scoped documents from being written under an individual user and becoming unreadable.Inconclusivelow confidence

Only 8 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 26, 2026, 2:03 PM

Libra has verdicts on 1 of 4 tracked behaviors on prod; 3 are still being checked. Libra checks hourly for 3 days after each deploy.

What was wrong

Two defects producing one error string , Unrecognized path prefix in "<x>" , at ~100 failures a day across ~25 users:

DayFailed callsUsers
2026-08-249825
2026-08-2110227
2026-08-187222
2026-08-177823

1. The tool returned an identifier it then refused as input

Document ids are first-class in Cedar: <ref id="…"/> is the playbook's own reference format, read-document returns an id in its output, and list-documents returns one per row. But every document tool accepted only a path.

So an agent holding an id had no way to turn it into content. It called read-document({ path: "<uuid>" }) , the only inference available to it , and failed. This was never the model guessing; no call it could have made would have worked.

Resolving the ids agents actually asked for over 7 days: 40 distinct email-style.md, plus organisation/knowledge-base, subagents/strategist.md, resources/templates.md, and a customer's positioning-customs-broker.md. For email-style.md the drafting skill's own skillDependencies injection covered it, so it self-healed. Everything else was advertised to the agent, attempted, and silently never read , capability loss, not a visible error.

read-document now accepts a bare id. Rather than adding a by-id read branch, the id is translated to a path and the existing path route runs unchanged , so content shortcuts (<uuid>#sections, a shape observed failing in production), the crm-updater view, local overrides, and every per-scope access rule still apply. A by-id branch would have had to re-implement user/ ownership, thread/ privacy, and the org-scoped playbook allowance, and would have drifted from them. The lookup is org-scoped, so a foreign id reports not-found rather than leaking a path.

2. The contact/ scope was never taught to the resolver

contact/{personKey}/profile shipped with contactProfilePath, isContactDocPath, unit tests, a smoke script, and a meeting-prep instruction telling agents to read then rewrite these documents. But parsePath had no contact/ branch, so every read and write threw. Constructing a path and resolving one are different things, and only the first was ever exercised.

Production holds exactly one contact/ document , contact/smoke_contact_example_test, 0 bytes, written by the smoke script in July. The scope has never worked.

Resolved org-scoped (userId: null), like conversation/ and thread/: a contact profile describes an external person, not the Cedar user, and its stated purpose is reuse across every deal that person touches. Verified consistent with both downstream classifiers, which already treated it that way by fallthrough , scopeFromPath (files.ts:76) returns {type:'org'} and queryUserIdForDocumentPath returns null for non-user//thread/ paths.

Note on approach

An earlier revision of this PR fixed (1) by changing the playbook renderer to advertise the path instead of the id. That was narrower and worse: it fixed one producer of ids while leaving the tool's own output and list-documents still handing agents an address the tool rejects. Fixing the tool covers the whole class, and needs no prompt change , so no prompt-cache invalidation across users, and no churn in the renderer tests or the golden-output comparison.

How to test

  • mastra/tools/document/__tests__/readDocumentTool.by-id.test.ts , an id returns content; the resolved path (not the id) is what reaches the reader, so scope rules still gate it; <uuid>#sections works; a foreign id is not-found with no path leaked; normal paths and #shortcuts are untouched.
  • services/documents/__tests__/contact-paths.test.ts , contact paths resolve, are org-scoped, and an unknown prefix still throws while naming contact/ as valid.

Verified: types clean · deps:check clean (1532 modules) · 6509 tests passing across src/services, `s

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/mastra/tools/document/__tests__/readDocumentTool.by-id.test.tsno production surface mapped
  • apps/server/src/mastra/tools/document/readDocumentTool.tsno production surface mapped
  • apps/server/src/services/documents/__tests__/contact-paths.test.tsno production surface mapped
  • apps/server/src/services/documents/index.tsno production surface mapped
  • apps/server/src/services/documents/virtual-paths.tsno production surface mapped