Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix(strategist): dedicated dispatch tool, relevance gate, core field ownership

merged#2691CedarCopilot

CedarCopilot wants to merge 3 commits into staging from fix/strategist-agent-efficiency

Live on prod, no production signal yetTimeline and evidence
  1. Opened
    Sep 6, 2026, 9:48 AM
  2. Sep 6, 2026, 10:43 AM
  3. Merged
    Sep 6, 2026, 11:50 AM
  4. Live on prod
    Sep 6, 2026, 11:50 AM
  5. Observed 3 days
    Sep 6, 2026, 11:50 AM
  6. Pipelines steady after this deploy
    Sep 6, 2026, 11:50 AM
  7. Unobserved

    Live on prod, no production signal yet

    Sep 9, 2026, 11:46 AM

Behaviors Libra is checking

The strategist skips pure Slack-delivery or calendar-RSVP noise before any LLM call, but proceeds when the triggering event is unmatched or contains substantive content rather than treating missing hydration as proof of noise.Brokenlow confidence

Unable to complete verification because the production OTEL query timed out before returning telemetry.

prod, checked Sep 9, 2026, 11:46 AM
The conversation field updater forwards the caller's agent identity into its internal agent runtime context so downstream field permissions use the actual executing subagent.Inconclusivelow confidence

Verification reached its 130s outer budget before the verifier returned. Libra is keeping this intent verifying until the next check.

prod, checked Sep 8, 2026, 9:50 AM
Conversation-field updater generations emit prompt-cache usage telemetry for the standard and Haiku workloads.Inconclusivelow confidence

Verification reached its budget before a verdict.

prod, checked Sep 8, 2026, 10:49 AM
Core conversation field definitions can persist an `ownerAgentId`, allowing `statusOverview` and `nextSteps` to identify their maintaining system subagents.Inconclusivelow confidence

Verification reached its 130s outer budget before the verifier returned. Libra is keeping this intent verifying until the next check.

prod, checked Sep 8, 2026, 11:50 AM
Field permission resolution applies ownership to core fields as well as custom fields, allowing the owning agent to update its field, forbidding different active agents, and leaving unowned or disabled-owner fields writable under the existing fallback behavior.Inconclusivelow confidence

Unable to complete a reliable verdict from the available evidence; additional telemetry correlation is needed to distinguish owner-successful core-field updates from unrelated permitted updates and intentional non-owner blocks.

prod, checked Sep 8, 2026, 12:49 PM
The core-field rollout stamps existing AOPs' unowned `statusOverview` and `nextSteps` fields with the crm-updater and next-steps owners, scanning every AOP rather than relying on a user-editable display name.Inconclusivelow confidence

Verification reached its budget before a verdict.

prod, checked Sep 8, 2026, 1:46 PM

Libra has verdicts on 0 of 15 tracked behaviors on prod; 15 are still being checked. Libra has 1 low-confidence broken verdict it is still confirming. Libra checks hourly for 3 days after each deploy.

Summary

Strategist runs on the same event cadence as crm-updater and next-steps but costs ~4x more per run (avg 9.0 tool calls vs. crm-updater's 2.16 and next-steps' 3.22, measured against 3 days of production execution logs), because it falls through to the generic automation-agent path instead of having a dedicated dispatch tool like its two siblings. Separately, production logs showed statusOverview and nextSteps , two core CRM fields with no ownership concept , being independently overwritten by all three agents on the same triggering events, discarding real work.

This bundles four changes:

  1. Prompt-cache telemetry , logPromptCacheUsage() added to crm-updater's and next-steps' existing generate() calls. Zero per-subagent cache-hit visibility existed anywhere before this (the one existing call site only covers the top-level orchestrator).
  2. Dedicated strategist dispatch tool (strategist-updates.ts) , mirrors crm-updater's conversation-field-updates.ts: structural pre-LLM guards (missing context, disabled frontmatter), a new deterministic noise-skip (Cedar's own delivery-failure alert emails, bare calendar RSVPs , gated on the email's actual body/snippet rather than classification presence, so an unclassified-but-real reply never false-skips), context hydration replacing four forced read tool calls, 3-layer Anthropic prompt-cache breakpoints, maxSteps 50→15, and its own tool allowlist (STRATEGIST_TOOLS).
  3. Relevance-gate prompt instruction , a new step 0 in strategist's prompt: judge from the hydrated triggering event alone whether it's capable of moving the deal before doing any reads; biased conservatively toward proceeding when ambiguous ("clearly nothing," not "probably nothing").
  4. Core field ownership , extends the existing (previously custom-fields-only) ownership mechanism to statusOverview (→ crm-updater) and nextSteps (→ next-steps), matching each field's plurality writer in production and each agent's stated job. Wires agentId propagation through every caller of the shared ownership-check path , including the compose-send flow (handleExecuteFromClientSend.ts), which a first review pass had missed and would otherwise have been silently locked out of statusOverview once ownership is stamped. Includes a backfill script (rollout-core-field-ownership.ts) since seed-time stamping alone only protects accounts created going forward , the accounts actually racing today need it run explicitly.

Review

Ran both thermo-review and /review against this diff. Findings and resolutions:

  • /review: found the handleExecuteFromClientSend.ts missing-agentId regression (fixed), a false-skip risk in the RSVP noise guard where emailClassification's async timing could catch a real reply mid-flight (fixed , now gated on the email's snippet instead, which is set synchronously at ingest), and a stale comment (fixed).
  • thermo-review: no blocking findings. Flagged the missing backfill path (fixed , see above) and that both the new and an existing precedent ownership-stamper resolve subagents by user-editable display name rather than filename, so a renamed agent silently never gets ownership stamped , this is inherited from the existing stampStrategistOwnership precedent, not introduced here, so left as a known follow-up rather than expanding this PR's scope.

Open questions for reviewers

  • statusOverview → crm-updater was a deliberate choice against the raw production frequency (strategist is actually the plurality writer, 45.4% vs. crm-updater's 40.6%) , flagging in case anyone wants to weigh in before merge.
  • maxSteps: 15 , production median was 9, but observed max was 33. If the high end represents legitimate complex-deal work rather than runaway loops, this could start truncating real output. Can't be settled by static review; worth watching on canary.

Test plan

  • pnpm --filter @zero/server run types , 0 errors
  • pnpm deps:check ,
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/db/aop-schema.tsno production surface mapped
  • apps/server/src/db/migrations/scripts/rollout-core-field-ownership.tsno production surface mapped
  • apps/server/src/mastra/routeHandlers/event-execution/__tests__/handleExecuteFromClientSend-crm-updater-agentid.test.tsno production surface mapped
  • apps/server/src/mastra/routeHandlers/event-execution/__tests__/handleExecuteFromClientSend-draft-closure.test.tsno production surface mapped
  • apps/server/src/mastra/routeHandlers/event-execution/__tests__/handleExecuteFromClientSend-live-next-steps.test.tsno production surface mapped
  • apps/server/src/mastra/routeHandlers/event-execution/handleExecuteFromClientSend.tsno production surface mapped
  • apps/server/src/mastra/tools/conversation/updateConversationFieldsTool.tsno production surface mapped
  • apps/server/src/mastra/tools/event-execution/__tests__/runSubagentTool-drafter.test.tsno production surface mapped
  • apps/server/src/mastra/tools/event-execution/__tests__/runSubagentTool-strategist.test.tsno production surface mapped
  • apps/server/src/mastra/tools/event-execution/orchestrator-dispatch-tools.tsno production surface mapped
  • apps/server/src/mastra/tools/event-execution/runSubagentTool.tsno production surface mapped
  • apps/server/src/mastra/tools/subagent-tool-allowlists.tsno production surface mapped
  • apps/server/src/mastra/tools/task/updateNextStepsAndTasksTool.tsno production surface mapped
  • apps/server/src/mastra/utils/__tests__/strategist-updates-tier1-guard.test.tsno production surface mapped
  • apps/server/src/mastra/utils/conversation-field-updates.tsno production surface mapped
  • apps/server/src/mastra/utils/strategist-updates.tsno production surface mapped
  • apps/server/src/mastra/workflows/event-execution/pre-execution-setup.tsno production surface mapped
  • apps/server/src/services/aop/__tests__/field-ownership.test.tsno production surface mapped
  • apps/server/src/services/aop/core-field-ownership.tsno production surface mapped
  • apps/server/src/services/aop/field-ownership.tsno production surface mapped
  • apps/server/src/services/aop/types.tsno production surface mapped
  • apps/server/src/services/playbook/__tests__/seed-playbook.test.tsno production surface mapped
  • apps/server/src/services/playbook/__tests__/strategist-relevance-gate-migration.test.tsno production surface mapped
  • apps/server/src/services/playbook/agent-defaults/index.tsno production surface mapped
  • apps/server/src/services/playbook/agent-defaults/strategist.tsno production surface mapped
  • apps/server/src/services/playbook/backfill/strategist-relevance-gate-migration.tsno production surface mapped
  • apps/server/src/services/playbook/playbook-renderers.tsno production surface mapped
  • apps/server/src/services/playbook/seed-playbook.tsno production surface mapped