Submatter/plans/Plan-workspace-communications/Epic-workspace-chat/Task-chat-persistence.md

4.8 KiB

kind slug title plan_slug epic_slug status priority tenant_id owner cursor_todo_id updated_at
task chat-persistence Persist workspace channels, messages, and read state workspace-communications workspace-chat in_progress P1 global unassigned null 2026-10-22

Task summary

Add the tenant-scoped relational foundation and tRPC API for durable workspace chat.

Description

Model channels, memberships, messages, threads, member read cursors, and the chat outbox contract in Postgres. Use workspaceProcedure and explicit workspace_id filters for every operation. This foundation accepts workspace context only and rejects object/unknown contexts without fallback. Authorization is workspace AND context AND channel permission: public workspace channels require active workspace access; private/direct require membership too; archived channels are read-only. Workspace admins/owners have no private/direct read or self-join bypass. Membership targets must be authorized, and all discovery/history/mention/count endpoints share the policy seam. Project/private-object access is deferred to Task-contextual-chat-authorization.md, not inferred from tenant ownership. Message writes must atomically include an outbox event, support idempotency, and allocate per-channel monotonic sequence cursors. Generate a new Drizzle migration; do not alter existing migrations.

Live PostgreSQL gates

  • Concurrent identical direct-channel creation produces one channel, one exact membership set, and one lifecycle event set.
  • Apply migrations and verify private staging transitions only with an exact canonical participant set; reject malformed transitions and direct membership/identity mutations.
  • Concurrent identical direct creation produces one channel/event set; mutation receipt replay returns the original result, collisions reject, and failed mutations roll back receipt/events.

Subtasks

  • Add chat channel, membership, message, read-state, and outbox schemas with workspace indexes and foreign keys.
  • Define public/private/direct and archived authorization, channel ownership/admin rules, immutable exact-participant direct channels, per-channel sequence cursors, one-level thread semantics, author unions, message statuses, persisted message mentions, and client idempotency keys.
  • Add zod inputs and a workspace-scoped chat router for channel create/list/rename/archive, private membership changes, idempotent direct-channel creation, root-only channel history, one-level thread history, send/reply/edit/tombstone, mention counts, and mark-read.
  • Atomically write versioned outbox events for message, reply, tombstone, lifecycle, membership, and read-state changes.
  • Add authorization, provisioning, idempotency, thread, mark-read, and cross-workspace isolation tests.
  • Against disposable PostgreSQL with migrations through 0019, verify advisory-lock blocking for simultaneous same-key sends; exactly one message, mention set, logical outbox event, and sequence increment; rollback after an injected post-allocation failure; and Drizzle bigint runtime shape from the atomic sequence RETURNING expression. Unit transaction fakes do not satisfy this gate.

Owner or assignee

unassigned

Status

ready

Estimation

L

Acceptance criteria

  • Unsupported/same-workspace object contexts fail closed without a public fallback; private/direct admin bypass and unauthorized title/preview/mention/count leaks are denied.

  • Foundation human mention parsing never installs, adds or invokes an agent; reviewed agent membership is a separate task.

  • Every tenant-owned table includes indexed, non-null workspace_id.

  • Public, private, direct, archived, channel-owner, workspace-admin/owner, author-edit, and moderation operations enforce their documented rules without cross-tenant disclosure.

  • Direct-channel membership is immutable and exact sorted participant sets resolve idempotently.

  • Channel history excludes replies; replies are one level and their root is constrained to the same workspace/channel.

  • Unread mention counts are derived from persisted workspace-scoped mention rows above the recipient's channel read cursor.

  • Duplicate message/reply retries with the same scoped idempotency key produce one durable row and one logical outbox event, demonstrated under real concurrent PostgreSQL transactions rather than only unit fakes.

  • Channel and thread history support stable per-channel sequence pagination and reconnect catch-up.

  • markChannelRead only advances the highest intentionally viewed sequence and emits an invalidation event for other devices.

  • pnpm lint && pnpm type-check && pnpm test pass.

  • Epic: ./Epic-workspace-chat.md
  • Plan: ../Plan-workspace-communications.md