exit-link-on-space.test.tsfix(drafting): the drafts a send leaves behind, the drafts nobody sees, and the link Space ate
Three A5 items, all in the gap between a draft existing in Gmail and Cedar knowing
about it.
Leftover drafts after a send (Vooma, Horison). Gmail retires the draft that was
actually sent; nothing retired the others, and a send without a draftId only deleted
one when an emailHeaderMessageId/id happened to ride along. So a thread that had
collected a second Cedar draft kept it, and the rep found a stale pre-send copy under
a message they had already sent — 77 of these across 21 accounts in three weeks.
mail.send now sweeps the thread afterwards, deleting only drafts Cedar authored
(checked against analytics_draft_actions, the same authorship record the rewrite path
adopts by). Fire-and-forget: the mail is sent, and nothing here may fail that.
Drafts with no task at all. The chat/headless save-draft path created a real Gmail
draft and attached it to nothing — 90% of emailResponses drafts and 100% of
preMeetingPrep drafts in the week to Aug 30 had no user_tasks row pointing at them,
so the user could not open, close or track them and nothing could find them to clean
up. It now upserts the draft task the on-event path has always created, via the same
idempotent upsertDraftTask, skipping interactive surfaces where the draft card
already carries its own task descriptor.
Link formatting (Concentrate). ExitLinkOnSpace bound Space globally and called
unsetLink() whenever the caret was anywhere inside a link — and unsetLink extends
over the whole mark range on a collapsed selection, so editing a word in the middle
of a hyperlink and pressing space stripped the href off the entire link, with the
next autosave writing the de-linked body to Gmail. It now acts only at the END of a
link, which is the case it was written for; mid-link it declines and the space types
normally. The Link mark is inclusive only when autolink is on, which the composer
turns on, so the extension does still have a job.
Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 30, 2026, 9:19 PMlist-exit-on-empty.test.tsfix(mail): clear 309 typecheck errors and 5 live ReferenceErrors
apps/mail 792 -> 483. Jest stays green at 111 suites / 1071 tests.
The single biggest cluster was one missing line: tsconfig pins `types` to
["node","vite/client","jest"] and jest.setup.js is a .js file outside `include`, so the
@testing-library/jest-dom matcher augmentation never entered the program — 170 errors from
`expect(...).toBeInTheDocument()` and friends. A 4-line jest-dom.d.ts clears all of them.
Stripping @jest/globals from 22 test files was a prerequisite, not a tidy-up: only the
globally-injected `expect` picks up that augmentation. The other 145 test files already
used injected globals, so this also converges on the dominant convention. Do not install
@jest/globals.
The rest: CRM store test fixtures still extending state that moved to conversationsSlice in
Dec 2025 and importing a deleted CRMTypes module; barrel files re-exporting modules that no
longer exist (each one an import-time throw); a locally-declared SelectorItem whose module
was deleted; sanitizeJson taking `object` when its body explicitly handles primitives and
both callers already cast.
Five of these were live runtime bugs, not type noise:
- setForcePropagate(false) in the AOP-save onSuccess threw on EVERY successful playbook
save (cedarAdmin/page.tsx, admin/page.tsx)
- backendResponsiveness referenced in a useMemo dependency array, evaluated every render,
while the memo body no longer mentions it
- "Force re-sync" called handleForceSync() with no argument against a z.object() input
that rejects undefined, so it always failed validation
- an empty-string icon key produced `Icon === ''`, i.e. <'' />
- showOverlay leaked an unknown attribute onto a Radix DOM node
Worth knowing separately: the 792 were invisible. Plain tsc reported 3, because a syntax
error elsewhere in the tree makes TypeScript skip ALL semantic checking. One stray comma
blinds the entire typecheck.
Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 2, 2026, 2:11 PM