---
path: /studio/presence-cursor-systems
surface: studio
domain: collaboration
auth: signed-in + studio entitlement
source: apps/oshun/web/src/app/studio/presence-cursor-systems/page.tsx
status: walked
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'
---

# Studio · Presence and Cursor Systems

## 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 panel** — `panel` with 5 `quickAction` links to sibling
  workspaces (no self-link, no Route Map)

## States

- [ ] **Loading** — `data-pr-loading` "Loading presence evaluator…" while the
      GET catalog request is in flight (`outcome === null`)
- [ ] **Unauthorized** — `data-pr-unauthorized` "Access restricted." on 401/403
      (admin scope required); shows the BFF message or "Studio admin scope
      required to evaluate presence."
- [ ] **Error** — `data-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 error** — `data-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

- Parent: [`../studio-overview.md`](../studio-overview.md)
- Sibling:
  [`studio-real-time-collaboration-substrate.md`](./studio-real-time-collaboration-substrate.md),
  [`studio-commenting-annotation-system.md`](./studio-commenting-annotation-system.md),
  [`studio-activity-change-feeds.md`](./studio-activity-change-feeds.md)
- Component:
  `apps/oshun/web/src/components/studio/StudioPresenceCursorSystemsWorkspace.tsx`
- BFF route: `apps/oshun/bff/src/routes/admin-studio-presence.ts`
- BFF store: `apps/oshun/bff/src/studio/presence-fsm-store.ts`

## 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
