Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix(config-agent): five fixes from the production config-session audit

merged#2651CedarCopilot

CedarCopilot wants to merge 3 commits into staging from fix/config-agent-audit-fixes

Live on prod, no production signal yetTimeline and evidence
  1. Opened
    Sep 1, 2026, 11:17 PM
  2. Sep 2, 2026, 8:38 AM
  3. Merged
    Sep 2, 2026, 9:29 AM
  4. Live on prod
    Sep 2, 2026, 9:29 AM
  5. Observed 2 hours
    Sep 2, 2026, 9:29 AM
  6. Unobserved

    Live on prod, no production signal yet

    Sep 2, 2026, 9:29 AM
  7. Pipelines steady after this deploy
    Sep 2, 2026, 9:29 AM

Behaviors Libra is checking

If organization or connected-system context loading fails, the account-configuration skill still loads its static instructions and closes the database connection instead of blocking the agent session.Not checked
prod
Every account-configuration skill load exposes the current tool allowlists for inline system agents, including conversation-field-updater, next-steps, on-event drafting, and table-row-fill agents, rather than requiring the rarely loaded playbook-design resource.Not checked
prod
The account-configuration agent receives organization member count, caller role, inherited org playbooks, and available org playbooks so it uses user scope for one-person organizations and explicitly resolves scope for multi-person organizations instead of silently defaulting to the caller's playbook.Not checked
prod
When designing playbook subagents, the configuration agent distinguishes inline allowlists from playbook-subagent connection_scopes, treats absent/present/empty grants correctly, handles daily-agenda's separate grants, and does not recreate the retired permissions frontmatter.Not checked
prod
An owner or admin can have the configuration agent read or write named teammates' playbooks through targetUserId, while remaining authorized only within their own organization and never impersonating another user.Not checked
prod
After changing PLAYBOOK.md, the configuration agent verifies and quotes the write.triggerSummary returned by the write instead of performing a second manifest read, and it does not claim a trigger is live when the expected summary entry is absent.Not checked
prod

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

Five fixes from the audit of two weeks of production config-agent sessions. One commit per bug, plus one commit of thermo-review fixes.

1. Config tools reject the flat params shape

337 failures / 104 threads / 14 days , the most common failure in the corpus by a factor of six. Family tools take params in an action-named sub-object; agents send them flat, fail, retry nested, succeed. It recurs within the same thread after being corrected. No user-visible damage, but it doubles latency on every config operation and burns context during exactly the tasks that need the most room.

The nesting is not a deliberate API design , it exists only because the MCP SDK's normalizeObjectSchema() will not convert a top-level union to JSON Schema (typescript-sdk#1643), so accepting the flat form costs nothing semantically.

Layer chosen: the MCP registration boundary, not family-tool-dispatch.ts. Verified against the installed packages rather than assumed , there are two Zod parses in front of every family-tool call, and both strip unknown keys before any of our code can read them:

  • @modelcontextprotocol/sdk's tools/call handler calls validateToolInput() and only then executeToolHandler(), so even the registerTool callback gets already-stripped args.
  • @mastra/core's createTool wraps the supplied execute with validateToolInput(this.inputSchema, …) and invokes the original body with the parsed data, so family-tool-dispatch.ts , which runs inside that body , never sees a flat key either.

So the fix is two halves at registration (mcp/external/flat-args.ts):

  • allowFlatArgs() widens the registered schema with .passthrough() so the stray keys survive. Deliberately not a z.preprocess() wrapper , that yields a ZodEffects, which has no .shape, which reintroduces the exact {properties:{}} bug above. .passthrough() returns a real ZodObject, and survives the .extend() injectIntentFields applies afterwards.
  • normalizeFlatArgs() re-nests them in the handler, before the scope check , this ordering is a security invariant, not a style choice (see below).

Rewriting is refused unless the action names a real params object on the tool's own unwidened schema, and a call sending both forms keeps the nested one.

2. Playbook writes don't return the recompiled manifest

Read-back was skipped on 14 of 36 playbook writes, with several threads ending in confident "it's set up" claims. The rule lives in an always-loaded SKILL.md and still lost to the agent's desire to answer the user.

writePlaybookXml already compiles the manifest and persists it in the same write , the verification the agent is told to go and fetch was already in scope, just discarded. Now returned as a compact triggerSummary (eventTypes, fieldWatchers, cronSchedules, beforeMeeting, webhooks, refIds), one short line each, threaded through all four boundaries that would otherwise drop it. Built only after verifyPlaybook passes, so it never describes a reverted write. Empty lists are kept , "the cron you asked for is NOT armed" is the failure this makes visible.

3. The config agent cannot see the org it is configuring

Root cause of the largest failure class: 233 user-scope playbook writes vs 5 at org scope, and in 88 threads the agent never once asked which was meant. It wasn't choosing badly , it was blind.

Adds <organization> to the per-user <runtime_context>: member count (excluding archived), the caller's organizationRole, and whether their playbooks inherit from a shared org playbook. Each stated with what it implies , a one-person org is told user scope is trivially correct and not to ask; a multi-person org is told the question is real.

4. targetUserId is built and invisible

8 uses in 1,698 config/document calls, almost all the staff debug tool. Not one session had an admin edit a teammate'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/.claude/skills/cedar-configuration/SKILL.mdno production surface mapped
  • apps/server/.claude/skills/cedar-configuration/cedar-system-knowledge.mdno production surface mapped
  • apps/server/.claude/skills/cedar-configuration/playbook-design.mdno production surface mapped
  • apps/server/src/mastra/mcp/external/__tests__/flat-args.test.tsno production surface mapped
  • apps/server/src/mastra/mcp/external/__tests__/scope-map.test.tsno production surface mapped
  • apps/server/src/mastra/mcp/external/flat-args.tsno production surface mapped
  • apps/server/src/mastra/mcp/external/scope-map.tsno production surface mapped
  • apps/server/src/mastra/mcp/external/server.tsno production surface mapped
  • apps/server/src/mastra/skills/account-config/__tests__/allowlist-splice.test.tsno production surface mapped
  • apps/server/src/mastra/skills/account-config/__tests__/cedar-configuration-docs.test.tsno production surface mapped
  • apps/server/src/mastra/skills/account-config/__tests__/contextual-instructions.test.tsno production surface mapped
  • apps/server/src/mastra/skills/account-config/contextual-instructions.tsno production surface mapped
  • apps/server/src/mastra/skills/account-config/index.tsno production surface mapped
  • apps/server/src/mastra/tools/document/__tests__/documentTool.test.tsno production surface mapped
  • apps/server/src/mastra/tools/document/documentTool.tsno production surface mapped
  • apps/server/src/mastra/tools/document/writeDocumentTool.tsno production surface mapped
  • apps/server/src/mastra/tools/subagent-tool-allowlists.tsno production surface mapped
  • apps/server/src/services/playbook/__tests__/guarded-write.test.tsno production surface mapped
  • apps/server/src/services/playbook/__tests__/trigger-summary.test.tsno production surface mapped
  • apps/server/src/services/playbook/guarded-write.tsno production surface mapped
  • apps/server/src/services/playbook/trigger-summary.tsno production surface mapped
  • apps/server/src/services/playbook/write-document-guarded.tsno production surface mapped