V1 Web PWA · Surface walkthrough

Studio · Presence and Cursor Systems

A per-surface walkthrough of the V1 Web PWA studio surface: layout, states, interactions, data, and cross-references.

walked
8sections3 minread

On this page

Context. surface studio · domain collaboration · route /studio/presence-cursor-systems · auth signed-in + studio entitlement · source apps/oshun/web/src/app/studio/presence-cursor-systems/page.tsx

Last walked. 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 presence and cursor systems as a live, admin-scoped BFF console. The workspace (StudioPresenceCursorSystemsWorkspace) wires a single Presence FSM Lane to the real presence finite-state machine via /v1/admin/studio/presence: it derives each participant's presence state — active, idle (cursor stale), away (heartbeat stale), or offline — from heartbeat age and cursor idle time against the policy thresholds. 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 (real-time collaboration, commenting/annotation, review/approval, activity/change feeds, notification center)
  • Direct URL / bookmark — yes

Layout regions#

page.tsx mounts ShellLayout active="studio", renders StudioPresenceCursorSystemsWorkspace, then a single panel containing 5 sibling quickAction links. There is no Route Map panel on this page.

  • Shell header — from ShellLayout
  • Workspace <h1> — "Studio Presence & Cursor Systems" (WorkspaceHeading), followed by a data-presence-cursor-summary paragraph describing the derivation
  • Presence FSM Lane (<h2> data-pr-lane-heading "Presence FSM Lane") — the catalog/loading/unauthorized/error states, the evaluate form, and the result
  • Quick-actions panelpanel with 5 quickAction links to sibling workspaces (no self-link, no Route Map)

States#

  • Loadingdata-pr-loading "Loading presence evaluator…" while the GET catalog request is in flight (outcome === null)
  • Unauthorizeddata-pr-unauthorized "Access restricted." on 401/403 (admin scope required); shows the BFF message or "Studio admin scope required to evaluate presence."
  • Errordata-pr-error "Could not load the presence evaluator." on any non-OK catalog response or network failure
  • Ready (form) — catalog loaded; data-pr-enums shows {presenceStates.length} presence states, and the data-pr-form form renders
  • Result — after a successful POST, data-pr-result renders the presence headline + per-participant table
  • Evaluate validation errordata-pr-evaluate-error for invalid JSON, network unavailable, or a non-200 POST (shows the POST detail)

Interactions#

Presence FSM Lane form#

  • Presence (textarea data-pr-payload, aria-label="presence json") — JSON { policy{ idleAfterMs, awayAfterMs, offlineAfterMs }, participants[{ participantId, heartbeatAgeMs, cursorIdleMs }] }; seeded with a default payload
  • Evaluate presence (button data-pr-submit, type="submit") — submitEvaluate parses the JSON (rejecting invalid JSON with data-pr-evaluate-error) and POSTs to /v1/admin/studio/presence/evaluate

Result#

  • Headline (data-pr-headline) — <data-pr-active-count> · {idleCount} idle · {awayCount} away · {offlineCount} offline
  • Participants table (data-pr-participants) — one data-pr-participant-row per participant with data-participant-id, data-presence, plus a data-pr-presence cell

Quick actions#

  • Back to Real-Time Collaboration Substrate workspace
  • Open Commenting and Annotation System workspace
  • Open Review and Approval Workflows workspace
  • Back to Activity and Change Feeds workspace
  • Back to Notification Center workspace

Data & contracts#

  • GET catalog: /v1/admin/studio/presence — fetched on mount with buildBffAuthHeaders() and cache: 'no-store'; returns { presenceStates[] }
  • POST verb: /v1/admin/studio/presence/evaluate with body { policy{ idleAfterMs, awayAfterMs, offlineAfterMs }, participants[{ participantId, heartbeatAgeMs, cursorIdleMs }] }{ result: { participants[], activeParticipants[], summary{ activeCount, idleCount, awayCount, offlineCount } } } (200) or a detail error
  • Realtime: none — request/response only
  • Client fetch: direct fetch with cache: '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#

Open questions / known gaps#

  • Confirm the live presence transport (WebSocket / awareness channel) if the lane moves beyond a stateless evaluator
  • Document the precedence between cursor-idle (idle) and heartbeat-age (away/offline) thresholds when both are exceeded