Revoking write access must terminate live sessions and let a user with
pending offline edits export them rather than lose them silently.
Backend (generic, reused by M5 grants #53):
- packages/shared: POND_ACCESS_CHANGED_CHANNEL, the LISTEN/NOTIFY channel
shared by api and collab.
- api: PondAccessNotifier emits pg_notify(pond_access_changed, pondId) on
a permission-relevant change; the single generic seam for revocation.
Wired into pond soft-delete as the interim trigger (see==modify until
#53).
- collab: a dedicated-connection LISTEN listener (LISTEN is connection-
bound, not pooled) that, on a notification, closes every open connection
to the pond's open pages. Clients then reconnect and the api re-issues a
token reflecting current access (downgrade to ro, or 403/404). Reconnects
and re-LISTENs if its connection drops.
Frontend:
- use-collab-provider: a refused token (403/404) on (re)connect sets
accessRevoked and stops the reconnect loop; exposes discardLocal.
- AccessRevokedDialog: keeps local content visible and offers Markdown
copy/download (derived from the live editor doc, so offline edits are
included) and an explicit discard that clears IndexedDB. de+en strings.
Tests: collab DB-backed integration test proves a direct NOTIFY closes a
live session within seconds (AC1) and leaves unrelated ponds untouched;
listener unit tests; api test asserts soft-delete fires the notifier;
web test for the export Markdown derivation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PGdhRiwU1WRL4XxJfZYipY