CedarCopilot wants to merge 1 commit into staging from fix/dm-container-kind
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.
Follow-up to #2653, which merged before this landed.
The thread-adoption rung added in #2653 ran before every create, including the first save of a brand-new compose , which arrives with no draft id at all. On a thread that already holds one draft (the user's own half-written reply, or an agent's), that first save adopted the stranger's draft and overwrote it.
The length === 1 guard in #2653 was written against the wrong risk. It reasoned about two
drafts on a thread, where you cannot tell which is yours, and never asked the prior question:
whether this session had a draft here at all. Thread membership is evidence a draft is ours only
once the client claimed to own one; on its own it is a coincidence, and acting on it destroys
writing , the exact harm the guard existed to prevent.
Caught by Greptile on #2653 (P1). It is right, and this is currently live on staging.
google.ts , the recovery rung is gated on the client having supplied a draft id. A save with
no id is a first save and creates, as it always should have.Two other findings from the same review:
sweep-duplicate-drafts.ts , the sweep compared bodies alone, so the same words to a different
recipient or under a different subject read as a duplicate. It now compares recipients and
subject too, and names attachments as the blind spot it cannot see through ParsedDraft.draftIdResolvedResponseProcessor.ts , the read side now narrows the stream payload instead of
asserting through it. The write side keeps one assertion, because updateMessage is typed to
the base Message union and cannot name a custom variant's fields; it carries the
justification comment CLAUDE.md requires.ConversationSearchCommandBar.tsx:346 , Greptile P1 (security): selecting a non-shared AOP and
searching the organization may surface conversations past the private filter. That came from
b19e95a6d, not this work, and also merged in #2653. Flagging rather than fixing blind , it
wants its own change and someone who knows that filter's intent.
pnpm run autofix:local green on the fix commit (404 Jest suites / 3,916 tests;
856 Vitest files)š¤ Generated with Claude Code
The PR prevents first saves from adopting unrelated drafts while making duplicate-draft cleanup more conservative and stream-event validation more type-safe.
The PR appears safe to merge, with no concrete changed-code failure remaining on current production paths.
The recovery gate prevents first-save overwrites, all production callers normalize placeholder IDs before reaching the driver, duplicate deletion eligibility is narrowed, and the response-processor changes preserve existing validated behavior.
| Filename | Overview |
|---|---|
| apps/server/src/lib/driver/google.ts | Correctly prevents no-ID first saves from entering sole-thread-draft recovery while preserving recovery for failed updates of client-claimed drafts. |
| apps/server/src/lib/driver/tests/google-create-draft-404-recovery.test.ts | Adds focused regression coverage proving a first save creates rather than overwrites an unrelated draft. |
| apps/server/src/scripts/sweep-duplicate-drafts.ts | Narrows destructive duplicate classification to matc |
Libra has not measured any production surfaces for this change yet.