Story E2E (Playwright) + acceptance video — an admin, a member and a viewer walk the same shell and are offered exactly what they hold
The story's end-to-end flow, automated, and its acceptance receipt. The parent story's verification recipe is the specification — the spec walks it, and the recorded video is what a reviewer watches to accept the story rather than re-driving it by hand.
The flow
tests/e2e/acceptance-permission-gated-ui.spec.ts, reachable by pnpm test:e2e --grep permission-gated-ui, in chapters:
- Admin — nothing was taken. The settings rail carries every entry, in its groups, and each is reachable and still saves. This chapter goes FIRST on purpose: the risk in a story that hides things is that it hides one too many, and the reviewer should see the unchanged case before the changed ones.
- Member — the door is gone. The bottom nav has no Project settings row;
⌘Kreturns no settings deep link for a settings query. - Member — the room still says no. Direct navigation to
/settings/project/membersrenders the no-access state, not a read-only form. - Viewer — the nav is narrower. The project-nav rows whose destinations refuse a viewer outright are absent; the ones that only refuse a WRITE are still there.
- Viewer — and nothing else moved. The Create button is visible-and-disabled with its tooltip, the board shows its read-only banner, and the issue-detail Edit link is absent — the 2026-06-09 in-place treatments, exactly as they shipped.
Discipline
- Wait on authoritative signals, never a timeout: a rendered rail, a settled navigation, the no-access panel's own test id. A gating assertion that races is worse than none, because it fails intermittently in the direction of "looks fine".
- Reuse the shipped multi-role harness rather than inventing one.
tests/e2e/acceptance-roles-permissions.spec.ts,per-domain-admin-permissions.spec.tsandmember-facing-permissions.spec.tsalready seed and sign in actors at each built-in role in this suite; take their fixtures. - Assert ABSENCE properly. Every chapter here is about something not being there, and
not.toBeVisible()passes just as happily when the page never loaded. Anchor each absence to a positive assertion in the same view — the rail rendered, and this row is not in it. - The video is PACED for a person. It is the acceptance receipt, watched by a reviewer who did not write the code; a clip that races through five chapters in four seconds meets the letter of "records the flow" and is useless as evidence. The harness paces chapters by default and the uploader refuses an unwatchable clip, and the spec is still written so that the pacing is intended rather than inherited.
Scope boundary
In: the E2E spec, its fixtures, and the acceptance-video recording and publish for this story. Out: unit and integration coverage (the vitest gate); the custom-role walkthrough, which cannot be driven until the custom-roles story ships and is that story's recipe step; any production change — a failure here is a defect in the card that owns the surface, not something to patch from the spec.
Acceptance criteria
pnpm test:e2e --grep permission-gated-uiruns the spec green in CI, and it covers all five chapters above.- Each absence assertion is anchored to a positive assertion of the surrounding surface in the same view, so a page that failed to load cannot pass as a hidden row.
- Every wait is on an authoritative signal; the spec contains no fixed sleep.
- The spec drives real sign-ins at admin, member and viewer using the suite's shipped role fixtures, and pins the active workspace and project explicitly rather than relying on a default.
- The run records the acceptance video and publishes it to the story; the clip is paced for a human to watch and its chapters are labelled to match the five above.
- The admin chapter proves at least one settings page still SAVES, not merely that it renders — "nothing was taken" is a claim about capability, not visibility.
- The spec adds no new external dependency and needs no service beyond the ones the suite already starts.
Context refs
tests/e2e/acceptance-roles-permissions.spec.ts,tests/e2e/per-domain-admin-permissions.spec.ts,tests/e2e/member-facing-permissions.spec.ts— the shipped role fixtures and sign-in flow this spec reuses.tests/e2e/settings-area.spec.ts— the settings rail's existing coverage, whose selectors this spec should not duplicate or contradict.tests/e2e/_helpers/acceptance-video.ts— the chapter and pacing harness, and the publish step.components/projects/NoAccessState.tsx— the panel chapter 3 asserts.- The permission-gated UI story — its verification recipe is this spec's specification.
- The design card — the panels each chapter should look like.