The revocation watcher only helps if the mutations that can take
access away actually announce it. Seven of them now do, after the write
that changed access:
- delete-team-member and leave-team announce the affected member.
- update-team-member-role announces too. A role change never removes
read on its own, but announcing lets the watcher decide from fresh
data instead of assuming, which is what keeps a downgrade to viewer
from being mistaken for a revocation.
- delete-file, delete-project, move-files and move-project announce to
the whole team. Which profiles had the content open is only known to
their own connections, and over-announcing is cheap: the watcher
re-checks and closes nothing for members who keep access.
delete-share-link is deliberately excluded. A share-link visitor never
passes the websocket subscribe check, because `check-read-permissions!`
is called without a share-id and that arity only considers team
membership, so there is no subscription to revoke.
leave-team also gains the notification it never had: it published
nothing at all on the leaver's own topic, so a session that left kept
its editor open with no idea, while a removed member was told.
The topic and the publish helper move to `app.rpc.notifications`, next
to the other bus notifications. The command namespaces cannot require
the watcher itself: `app.http.websocket` already requires these command
namespaces, so that would be a cycle. The watcher keeps the subscription
registry, which is the part it owns.
Closes#12155
AI-assisted-by: space-bunny-free