Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix(agents): stop the orchestrator drafting off internal-sync and automated events

merged#2797CedarCopilot

CedarCopilot wants to merge 0 commits into staging from fix/orchestrator-no-draft-on-internal-events

Live on prod, no production signal yetTimeline and evidence
  1. Opened
    Sep 15, 2026, 9:34 AM
  2. Merged
    Sep 15, 2026, 9:46 AM
  3. Live on prod
    Sep 15, 2026, 9:46 AM
  4. Observed 4 hours
    Sep 15, 2026, 9:46 AM
  5. Unobserved

    Live on prod, no production signal yet

    Sep 15, 2026, 9:46 AM
  6. Pipelines steady after this deploy
    Sep 15, 2026, 9:46 AM

Behaviors Libra is checking

When an automated, marketing, or otherwise NOT SIGNIFICANT event triggers orchestration, the agent must not draft a reply, recap, or follow-up based on that event or unrelated overdue conversation context, while still allowing field updates and next-step task management.Not checked
prod
When the triggering task identifies itself as an internal data sync only, the agent must never draft or initiate any external message, including catch-up recaps or follow-ups noticed elsewhere in the conversation, while continuing field updates and task management.Not checked
prod

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

What was wrong

Two related regressions in the on-event orchestrator's drafting decision, both traced with production data.

1. External CRM sync events draft anyway. handleExecuteExternalCrm.ts builds a task prompt that ends with an explicit "Do NOT draft emails... this is an internal data sync only" instruction, but the orchestrator's own system prompt (on-event-orchestrator-agent.ts) has zero awareness of this case , its general drafting instructions ("call run-subagent with @subagents/drafter... when a DRAFT NOW rule applies") compete against, and win over, a single trailing sentence in a large CRM-activity data dump.

Weekly data across 3 Pirros accounts (external_crm-sourced runs that produced a successful draft):

WeekTotal ext_crm runsSpawned a draft
08-031,0880
08-104310
08-171,4550
08-248400
08-3176614
09-071,04127
09-14 (partial)6218

Zero for four straight weeks, then a clean start the week of Aug 31 , lining up with the feat/drafter-system-agent migration (PR #2624, merged 2026-08-31), which moved drafting from inline logic into a decoupled orchestrator + dispatched-subagent design.

2. Automated/calendar-notification events used as a pretext to draft something else. A Google Calendar RSVP notification (<email>, crm_events.is_significant = false) triggered the orchestrator, which correctly identified the sender two tool calls earlier, then dispatched the drafter anyway to write an unrelated, already-drafted meeting recap.

Both incidents produced the identical reasoning string in agent_tool_calls: "A recap email is needed... per the post-meeting email resource" , reasoning from the broader conversation history (a meeting happened recently) rather than the event that actually triggered the run.

What changed

apps/server/src/mastra/agents/on-event-orchestrator-agent.ts , prompt-only change (no tool-access changes, per explicit request):

  • Strengthened the [⚙ NOT SIGNIFICANT] rule: even when the playbook has explicit instructions for the event type, a not-significant/automated event must never be the reason to draft anything , not a reply to it, and not something else that looks overdue.
  • Extended <no_marketing_draft_rule> to explicitly forbid using an automated/marketing event as a pretext to draft something unrelated that "looks overdue."
  • Added a new <internal_sync_no_draft_rule> section that names the exact failure mode observed in production (the "recap... per the post-meeting email resource" rationalization) and states directly: if the task says internal-sync-only, never call run-subagent(@subagents/drafter), for any reason.

Blast radius

This is the shared orchestrator system prompt , it applies to every account's event-triggered runs, not just Pirros. It only adds constraints (narrows when drafting happens); it does not change any tool wiring or access.

How to test

Watch agent_tool_calls for external_crm-sourced runs and [⚙ NOT SIGNIFICANT]-flagged events going forward , run-subagent/run-post-event-executor should no longer appear as a child of either, across all accounts, not just Pirros.

🤖 Generated with Claude Code

https://claude.ai/code/session_01PuWr5XobqBY5mbUvSZjwxy

Greptile Summary

This prompt-only change strengthens the orchestrator’s drafting restrictions for automated, not-significant, and internal CRM-sync events.

  • Prevents automated events from serving as a pretext for unrelated overdue drafts.
  • Adds an explicit internal-sync-only drafting prohibition.
  • Leaves a separate draft-capable notifier pipeline insufficiently covered by that prohibition.

Confidence Score: 4/5

The PR is not yet safe to merge because internal CRM-sync executions retain a reachable draft-capable notifier path.

The new internal-sync rule blocks the dedicated drafter but does not

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/agents/on-event-orchestrator-agent.tsno production surface mapped