feat(service): a person joins a workspace's chat once, and a read writes nothing

Merged
CH
8d ago
+146-22

Part of making workspace chat live over Neon Realtime. It's the bottom of the chat stack (stack 6), on luke/live-same-user (#4228). One change: a person joins a workspace's chat once, and reading the chat no longer writes anything.

Why

Every chat read used to run ensureMember, a locking transaction, and ensureGeneral. Both are writes. A live subscription can't write on every change, so joining has to become a one-time step before subscribing.

What changes

  • Explicit join. The messages.chatWrite procedure (POST /api/workspaces/chat) now also takes {join: true}.
    • It creates the person's chat member and #general if they don't exist yet.
    • It answers {self}.
    • New wire schemas: chatJoinRequestSchema and chatJoinAnswerSchema.
  • Reads don't write. workspaceChatPast finds the reader's own member and makes nothing. Someone who hasn't joined gets not found.
  • Released desktops still work. The held GET still joins when it reads from the start cursor (0). Released desktops never send a join, so they join once per read loop rather than on every read.

Tests

  • workspace-chat.test.ts: a read past the start by someone who hasn't joined is not found and creates no member. After a join it answers. Joining twice gives the same member, and a stranger's join is not found.
  • New JSON Schema goldens for the two new schemas.
  • Checks run one at a time on the stack's top:
    • tsc for each touched package
    • biome, oxlint, knip
    • discarded-effect
    • repository checks
    • the affected vitest suites