Context. surface studio · domain collaboration · route /studio/review-approval-workflows · auth signed-in + studio entitlement · source apps/oshun/web/src/app/studio/review-approval-workflows/page.tsx
Last walked. 2026-06-29 review-approval-workflows real-BFF addendum — Playwright now covers the admin-scoped route render, real approval-workflow catalog GET, default workflow POST/evaluate result, stage table semantics, quick-action targets, loading/error/unauthorized states, malformed JSON client block, live BFF 400 detail, pending submit lockout, and anonymous redirect-before-render. Evidence: WALKTHROUGH/results/v1-real-infra-run-2026-06-22.md §223; spec: apps/oshun/web/e2e/studio-review-approval-workflows.spec.ts. 2026-05-29 automated runtime walk (Playwright headless) — render, /v1 data (2xx), console/page-errors, expected content, screenshot verified; live screen-reader, touch, offline, and telemetry-delivery checks pending a manual AT pass. Evidence: WALKTHROUGH/results/runtime-sweep-2026-05-29.md; content re-verified 2026-06-03 against current source
Purpose#
Operate review and approval workflows as a live, admin-scoped BFF console. The
workspace (StudioReviewApprovalWorkflowsWorkspace) wires a single Approval
Gate Lane to the real multi-stage approval-gate evaluator via
/v1/admin/studio/approval-workflow: each stage is evaluated for quorum
(required approvals), required-role satisfaction, and reject-override; the
workflow rollup reports approved/pending/rejected stage counts and points at the
current (first non-approved) stage. This is a real domain computation, not a
simulation.
Entry points#
- Studio root (
/studio) — discoverable as a collaboration workspace - Quick-action links from sibling workspaces (asset preview pipeline, file/media ingestion, commenting/annotation, presence, real-time collaboration, activity/change feeds)
- Direct URL / bookmark — yes
Layout regions#
page.tsx mounts ShellLayout active="studio", renders
StudioReviewApprovalWorkflowsWorkspace, then a single panel containing 6
sibling quickAction links. There is no Route Map panel on this page.
- Shell header — from
ShellLayout - Workspace
<h1>— "Studio Review & Approval Workflows" (WorkspaceHeading), followed by adata-review-approval-summaryparagraph describing the evaluation - Approval Gate Lane (
<h2>data-aw-lane-heading"Approval Gate Lane") — the catalog/loading/unauthorized/error states, the evaluate form, and the result - Quick-actions panel —
panelwith 6quickActionlinks to sibling workspaces (no self-link, no Route Map), anchored bydata-review-approval-quick-actions,data-review-approval-quick-action-count="6", and onedata-review-approval-quick-action="<workspace-id>"per link
States#
- Loading —
data-aw-loading"Loading workflow evaluator…" while the GET catalog request is in flight (outcome === null) - Unauthorized —
data-aw-unauthorized"Access restricted." on 401/403 (admin scope required); shows the BFF message or "Studio admin scope required to evaluate the workflow." - Error —
data-aw-error"Could not load the workflow evaluator." on any non-OK catalog response or network failure - Ready (form) — catalog loaded;
data-aw-enumsshows the stage-status and workflow-status counts (for the shipped catalog:3 stage statuses · 3 workflow statuses), and thedata-aw-formform renders - Result — after a successful POST,
data-aw-resultrenders the workflow headline + per-stage table - Evaluate validation error —
data-aw-evaluate-errorfor invalid JSON, network unavailable, or a non-200 POST (shows the POSTdetail) - Pending evaluate POST — form flips
data-aw-form-state="evaluating", submit is disabled witharia-disabled="true"/aria-busy="true"and label "Evaluating workflow…" until the response returns - Anonymous user — redirected to
/welcome?redirect=%2Fstudio%2Freview-approval-workflowsbefore the workspace root renders
Interactions#
Approval Gate Lane form#
-
Workflow (
textareadata-aw-payload,aria-label="workflow json") — seeded with a default JSON payload:```ts { stages: [{ stageId, name, requiredApprovals, requiredRole?, approvals: [{ approver, role, decision }] }] } ``` -
Evaluate workflow (
buttondata-aw-submit,type="submit") —submitEvaluateparses the JSON (rejecting invalid JSON withdata-aw-evaluate-error) and POSTs to/v1/admin/studio/approval-workflow/evaluate -
Client-side malformed JSON block — invalid JSON surfaces "Payload must be valid JSON." and does not send a POST
-
Live BFF invalid payload branch — valid JSON with
stages: []reaches the BFF and surfacesdetail: "stages must be a non-empty array" -
Touch target — the textarea and submit button have measurable boxes; the submit control is ≥44px tall
Result#
- Headline (
data-aw-headline,data-workflow-status) — renders:Workflow {workflowStatus} · current stage: <data-aw-current-stage> · {approvedStages} approved · {pendingStages} pending · {rejectedStages} rejected - Stages table (
data-aw-stages) — onedata-aw-stage-rowper stage withdata-stage-id,data-status, plusdata-aw-stage-name,data-aw-stage-status,data-aw-stage-approvals({approveCount} (need {approvalsNeeded})), anddata-aw-stage-role-okcells - Reject-override precedence — the default fixture proves stage
st3rendersrejectedeven though it has one approval and needs zero more approvals, because one reviewer rejected; workflow status is thereforerejected - Current-stage pointer — the default fixture renders current stage
st2(the first non-approved stage)
Quick actions#
- Open Asset Preview Pipeline workspace
- Open File and Media Ingestion workspace
- Back to Commenting and Annotation System workspace
- Back to Presence and Cursor Systems workspace
- Back to Real-Time Collaboration Substrate workspace
- Back to Activity and Change Feeds workspace
Data & contracts#
-
GET catalog:
/v1/admin/studio/approval-workflow— fetched on mount withbuildBffAuthHeaders()andcache: 'no-store'; returns{ stageStatuses[], workflowStatuses[] } -
POST verb:
/v1/admin/studio/approval-workflow/evaluatewith body:ts{ stages: [{ stageId, name, requiredApprovals, requiredRole?, approvals: [{ approver, role, decision }] }] }Returns a 200
resultwithstages[],workflowStatus,currentStageId, andsummary{ approvedStages, pendingStages, rejectedStages }, or adetailerror. -
Persistence: evaluated workflows are stateless in this lane today. The browser posts a payload and renders the returned verdict; no persisted workflow id, audit id, or review queue mutation is returned by this BFF route.
-
Realtime: none — request/response only
-
Client fetch: direct
fetchwithcache: 'no-store'; no client-side caching -
Auth/role check: admin-scoped, fail-closed at the BFF (
admin:*|admin:studio; 401/403); reached through the signed-in + studio route gate
Cross-references#
- Parent:
../studio-overview.md - Sibling:
studio-commenting-annotation-system.md,studio-real-time-collaboration-substrate.md,studio-notification-center.md - Component:
apps/oshun/web/src/components/studio/StudioReviewApprovalWorkflowsWorkspace.tsx - BFF route:
apps/oshun/bff/src/routes/admin-studio-approval-workflow.ts - BFF store:
apps/oshun/bff/src/studio/approval-workflow-store.ts
E2E coverage#
apps/oshun/web/e2e/studio-review-approval-workflows.spec.ts— signed-in admin/studio/review-approval-workflowsrender; real approval-workflow catalog GET with admin bearer; default workflow evaluate POST through the live BFF; rejected workflow headline/current-stage/summary; all four default stage rows with status, approval counts, and role satisfaction; six quick-action links and target sizes; catalog loading/error; client-side malformed JSON zero-POST; live BFF 400 invalid-payload detail; pending POST disabled/busy state; non-admin 403 fail-closed; anonymous redirect-before-render.apps/oshun/web/e2e/studio-route-render-smoke.spec.ts— broad Studio route render and accessibility smoke coverage includes this route, but does not replace the focused real-BFF workflow spec above.
Open questions / known gaps#
- Confirm whether evaluated workflows persist their verdicts, or whether the lane stays a stateless evaluator — today it is stateless request/response only
- Document the reject-override precedence relative to quorum and required-role satisfaction — reject wins first; otherwise quorum plus required-role approval is needed for a stage to become approved
- Live screen-reader and touch-device manual passes remain outside the automated route spec