CedarCopilot wants to merge 2 commits into staging from worktree-fix-drafter-live-mail-tools
Live on prod, no production signal yet
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.
ON_EVENT_DRAFTING_EXECUTOR_TOOLS granted find-emails, which queries crm_email_events (Cedar's synced store) and nothing that can reach the live mailbox. On a deal whose history predates the user's sync window that returns nothing, so the drafter has no thread to reply on and no contact to address.
That is the normal case for a reactivation, not a corner. For <email>, 90 of 145 closed-lost deals hold no synced email at all, and Cedar holds exactly one thread for 34 of the 50 deals his reactivation drafts landed on , often a calendar invite, a bounce, or a Superhuman reminder, because that is the only message that survived the sync window.
Notably this is not a thread-selection bug: all 34 drafts that anchored onto a pre-existing thread chose the newest thread Cedar holds. Zero picked an older one. The selection logic is fine; the catalogue it selects from is a coverage artifact.
The agent went looking anyway. In one day, 13 of 30 drafting runs hand-rolled Gmail API curls through run-code-executor , the wrong surface (not suppressed by --no-send) and unreliable:
19b2dc840e62339b, drafted correctly.400 Invalid thread_id value, and the retry dropped threading entirely and composed a new email subjected Re: Invoice Butler // Cortex.search-live-emails and read-live-email-thread already existed and were already registered in the conversation skill. They had simply never been reachable from any background path: zero calls fleet-wide in 60 days, against 1,400+ for find-emails.
Two ids added to the allowlist. No prompt change, no playbook change.
live-thread-search-eval.ts, 5 trials per cell, record-only stubs (no DB, no Gmail, no drafts). Two fixtures pulling in opposite directions: Otamiser rewards searching, Cortex punishes fabricating.
| Otamiser: found the real thread | Cortex: abstained | fell back to code-exec | |
|---|---|---|---|
| A , shipped grant | 0/5 | 5/5 | 0/5 |
| B , + live mail tools (this PR) | 5/5 | 4/5 | 1/5 |
| C , B + a "ground or abstain" prompt rule | 5/5 | 2/5 | 4/5 |
Variant C was tested and rejected. Telling the agent to ground its evidence made it escalate to run-code-executor when a search came back empty, then draft anyway , abstention fell from 4/5 to 2/5. email.md already says "NEVER guess or make up thread IDs" and production fabricated one regardless, so adding prose is not the lever. The change worth making there is subtractive (the recipient rule's automated-execution carve-out) and is written up in the plan doc rather than made here.
docs/customer-feedback/2026-09-01-mihir-reactivation-draft-repair.md is the re-anchoring plan for those.SKILL.md rather than the assembled DRAFTING_INSTRUCTIONS, so it never carried the existing "NEVER guess" rule.search-live-emails from the allowlist turns it red.pnpm --filter @zero/server run types clean. types:test shows only pre-existing failures, none in the new file.cedar-cli subagent replay --no-send against a7b28d83-f159-47c2-950e-18bfd61b8ef2 (Cortex) and 091678cb-89d7-4f5c-acc3-7faa24e22e7d (Otamiser). Expect the thread to come from search-live-emails rather than run-code-executor.The
Libra has not measured any production surfaces for this change yet.