Skip to content

Make conversation mutations visible in v2-primary and fix two send-path state bugs (rebuild W4-A)#145

Open
MaxGhenis wants to merge 1 commit into
mainfrom
w4a-conversation-mutations
Open

Make conversation mutations visible in v2-primary and fix two send-path state bugs (rebuild W4-A)#145
MaxGhenis wants to merge 1 commit into
mainfrom
w4a-conversation-mutations

Conversation

@MaxGhenis

Copy link
Copy Markdown
Owner

Rebuild W4-A — conversation mutations in primary + two real bug finds

First Wave-4 PR (spec: wave4-primary-parity-spec-2026-07-18). Mute / mentions-only / favorite / archive now dual-write (legacy → v2 → SSE publish) so primary reads see them; 4 e2e specs un-fixmed unweakened.

Review + probe findings fixed

  • BLOCKING (review): imported gchat/iMessage threads 500'd on any mutation in primary (platform→account map missed the -archive accounts). Fixed via O(1) resolve on the v2 row's RemoteConversationID — no more capped legacy scans.
  • ErrNotFound contract: legacy-only conversations now mutate legacy + skip the v2 leg (debug log) instead of 500ing forever.
  • Bug 1 (probe-proven): mutation responses could insert a mixed-id conversation entry → next open fetched a different id than the DOM rendered from. Patch path now treats legacy ids as aliases; no duplicate rows.
  • Bug 2 (probe-proven, the five-round hunt): optimistic send bubbles derived platform from mutable conversation metadata; one patched entry flipped showMixedSourceChips → every thread message's render signature changed → the keyed reconciler replaced all nodes on send. Captured live: signature diff single-sourcemixed-source at the exact removal. Bubble now derives platform from the rendered messages' field; canonical google-bridge label stays "sms" (legacy schema + R5 parity pins + live installs).

Verification

  • Full GOWORK=off go test -race ./...: 32/32, exit 0
  • Full Playwright (fresh server): 95 passed / 0 failed / 5 skipped
  • Per-mutation discriminators (primary read flips), per-platform resolve cases (gchat/imessage fail pre-fix), bulk-archive non-nil pin

🤖 Generated with Claude Code

…th state bugs (rebuild W4-A)

Mute, mentions-only, favorite, and archive-tab were legacy-store-only writes
that v2-primary reads never observed (silent no-op in the UI). They now
dual-write: legacy first, then the v2 conversations row, then an SSE
conversations publish. v2 failures surface as 500s; a conversation with no
v2 row yet (legacy-only creation paths) mutates legacy and skips the v2 leg
with a debug log rather than failing forever.

- v2 repo setters for notification_mode / is_favorite / archived_at_ms
  (schema 0002 already carried the columns; archive maps tab=="archive" to
  archived_at_ms, other tabs clear it; custom tabs stay legacy-only).
- Handler id resolution is O(1) via the v2 row's RemoteConversationID (the
  migration's natural key) instead of a capped legacy scan, with the
  legacy->v2 account map completed for gchat-archive/imessage-archive —
  previously any mutation on an imported Google Chat or iMessage thread
  500'd in primary.
- v2read serves archived as the legacy-compatible tab field; primary
  mutation responses return the v2 projection, and the frontend patch path
  treats legacy ids as aliases so a response can never mint a duplicate
  mixed-id conversation entry.
- Two real product bugs found by the primary e2e lane and browser
  instrumentation, both fixed at the source:
  1) Mutation responses could insert a conversation-list entry keyed by the
     wrong id family, so the next thread open fetched a different id than
     the DOM was rendered from.
  2) The optimistic queued bubble derived its platform label from mutable
     conversation-list metadata; a patched entry whose source_platform
     differed from the thread's message DTOs flipped every message's
     render signature (single-source -> mixed-source), causing the keyed
     reconciler to replace all thread nodes on send. The bubble now derives
     its platform from the rendered messages' own field, and the canonical
     google-bridge label remains "sms" everywhere (matching the legacy
     schema, the R5 parity pins, and live installs).
- e2e: the four attribute-mutation specs are un-fixmed (header and context
  menu paths, unweakened); the favorites spec cleans up its durable
  favorites; the stable-thread-node helper tags the newest message so
  legitimate tail-window eviction of old messages cannot masquerade as a
  reconciliation bug.
@MaxGhenis
MaxGhenis force-pushed the w4a-conversation-mutations branch from d81a7bd to f61cc51 Compare July 20, 2026 00:32
@MaxGhenis

Copy link
Copy Markdown
Owner Author

Updated with the unread-count work (delta review's blocking find: v2-primary had rendered zero unread badges since R8 because v2read hardcoded UnreadCount:0). Now computed for real from v2 read_cursors (batched, no N+1) and wired through /api/mark-read end-to-end; the e2e fixture overlays that were false-greening it are deleted. Mentions-only is honestly re-fixmed (v2 messages schema has no mention column; migration drops the legacy flag — PRIMARY REGRESSION noted for a schema-bearing Wave-4 follow-up). Local gates: full Playwright 94 passed / 0 failed / 6 skipped; Go -race green for all W4-A-touched packages. Note: CI's Go Race job may flake on internal/signallive TestSignalCLIVersionGateFailsOpenWithWarning — a pre-existing data race (git diff shows the package untouched by this branch), tracked separately as a third flaky-race fix; not introduced here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant