CanvasKanbanCard.tsxperf(crm): give conversation rows their own event projection A row draws a dot timeline and three "last <type> at" columns, and nothing else — but crm.listConversations was building up to seven nested sub-objects per event through eight LEFT JOINs to feed it. Rows now get five scalars and a flat threadId, ~3x smaller per event, so the cap can move 15 -> 60 and draw three times the history for no more bytes: measured on a 50-row page, 547KB over 641 events before, 538KB over 1,919 now. That cap was also quietly wrong. "Last meeting" was derived by scanning whatever events the list happened to ship, so a deal whose last meeting fell outside its 15 most recent rendered blank — 53 of the 304 conversations with a meeting, in the org this was measured on — while SORTING by the same field used an unbounded MAX(occurred_at) and placed it somewhere the column contradicted. The three columns now read scalars computed over the whole history, from the same expressions the ORDER BY builder already used. Splitting the projections needed guards the single shared type had been hiding: - setConversations takes an explicit `projection`. Both queries write one store key and replace wholesale, so a list refetch would otherwise empty the timeline of whatever deal was open — the same bug that previously just shrank it to 15 (client-data-architecture.md §1.2). - getEventType/getEventTitle read `eventType` rather than sniffing which sub-object came back from a join, which equated "this join returned nothing" with "this is a note" and would have greyed out every dot. - a dot click opens EventDetailDialog on an id and fetches the full event, since the row no longer carries one. Also carries the in-flight pending-conversation-field-writes mask and its tests: it edits the same setConversations body, so it cannot be split out. Co-Authored-By: Claude Opus 5 <<email>>Sep 2, 2026, 11:01 PMCanvasKanbanColumn.tsxfeat: in-flight work across layout, kanban, and account deletion Carries the working tree as it stood: the /agent home hero and the rail behind it, back/up navigation through LayoutUrlSync and useBackOrUp, the kanban conversation canvas, a list-field chip renderer for CRM cells, and further account-deletion purge coverage. Committed as one unit at the user's request so it travels with the branch's PR rather than sitting uncommitted. Boundaries between the pieces are the individual files, not this commit. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 2, 2026, 10:29 PMKanbanConfigPopover.tsxfeat(forms): the settings row layout, and a select that admits it opens Every field is one row now — icon and name left, the sentence under it, the control right — because a form with eight fields laid out label-over-control gives the eye nothing to run down. Fields read the layout from context (`FieldRows`), so a form declares it once instead of per field. The select rendered no chevron at all, and no call site had noticed: it was a bordered box with a word in it. It now owns its chevron, comes in two sizes instead of the four that had accumulated across 62 call sites, and has a `ghost` variant for the ones that sit inside a sentence. Two bugs found by reading computed styles rather than screenshots: FormControl is a Slot that hardcoded `bg-popover`, so it was overriding the background of every control in every react-hook-form in the app; and the menu's `shadow-[...]` compiled to three fully transparent layers. Geometry is measured off Linear's settings surface; the colours are Cedar's own tokens, since our light theme is warm and theirs is grey. Co-Authored-By: Claude Opus 5 (1M context) <<email>> Claude-Session: https://claude.ai/code/session_01DFkAfWRuLsdTkpxrFhTKYWAug 30, 2026, 11:37 PMKanbanConversationCanvas.tsxfeat: in-flight work across layout, kanban, and account deletion Carries the working tree as it stood: the /agent home hero and the rail behind it, back/up navigation through LayoutUrlSync and useBackOrUp, the kanban conversation canvas, a list-field chip renderer for CRM cells, and further account-deletion purge coverage. Committed as one unit at the user's request so it travels with the branch's PR rather than sitting uncommitted. Boundaries between the pieces are the individual files, not this commit. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 2, 2026, 10:29 PM