oficina / sheets Public

"Access removed" is untrue when team standing still admits the person Issue closed

Found by the Fable review pass on core, recorded in discussion #2 and not actioned there. Re-verified present on c4a3954.

Revoking a team member's explicit grant on a sheet reports "Access removed". The grant is indeed removed — and the person keeps editor access, because team standing is a fallback beneath the explicit grant.

The fallback is deliberate and stays. One instance, one team: a team member reaching a team sheet is the design, not a bug (spec §8.2). What is wrong is only the sentence. An owner who reads "Access removed" and walks away believes they have done something they have not done, and that belief is about who can read a sealed sheet.

This is PRODUCT.md's first principle failing in one string: say the true thing, including the unflattering half. The unflattering half here is that revoking a grant from a teammate does not lock them out and cannot.

Fix: after revoking, work out whether the person still reaches the sheet by another route and say so plainly. No cell content, sheet title or tab name in the string (AGENTS.md).

Test that must fail first: grant a team member explicit access, revoke it, assert the notice says their team standing still admits them — and assert the unchanged case still reads as a plain removal.

Copy note: the replacement sentence is user-facing language and goes past Paul before it lands, not after.

Fixed on branch fix/revoke-notice-team-standing, commit 9b723a5, deployed to edge and verified on the process: GET /api/version returns 9b723a5.

The notice is Paul's wording:

> The explicit permission is removed, but since \<address\> is on the > team, they still have access.

The person is named, and that is not decoration. Revoking the grant also takes them off the list below, so an unnamed sentence would refer to somebody the owner can no longer see anywhere on the screen.

The member id travels in the query; the address does not. An email address in a request line is an email address in the platform edge's access logs — the same leak the share-link token was moved out of in 3b8870a, four commits earlier on this run. An id is already in every revoke form's hidden field on this screen. The address is read back from the row, and only after checking the sheet genuinely still admits that member, because the id came from a URL.

Paul's question is what found the real defect, and it is bigger than this notice. He asked what the point of the notice was. Measuring rather than defending it:

BEFORE revoke: role="viewer" mayWrite=false AFTER revoke: role="editor" mayWrite=true

roleFor consults the explicit grant first and falls back to team standing only when no grant row exists — so an explicit grant is the only way to hold a teammate below editor, and "Remove" deletes it. A button labelled Remove, styled as a danger action, grants edit access.

Ruled by Paul, 2026-09-01: the sentence changes, the behaviour stays. Filed as its own issue rather than closed over, because a notice that describes an escalation politely is still an escalation, and this is exactly the kind of thing §14 exists to stop from being rediscovered in three months.

Closing this one. make test green including -race; not reviewed, per AGENTS.md §Review.