---
path: /studio/activity-change-feeds
surface: studio
domain: collaboration
auth: signed-in + studio entitlement
source: apps/oshun/web/src/app/studio/activity-change-feeds/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 · Activity and Change Feeds

## Purpose

Operate activity and change feeds as a live, admin-scoped BFF console. The
workspace (`StudioActivityChangeFeedsWorkspace`) wires a single **Change
Coalesce Lane** to the real change-data-capture (CDC) coalescer via
`/v1/admin/studio/change-feed`: it folds a chronological event log into one net
change per entity (a `created` then `deleted` cancels to no-op, repeated
`updated` events collapse), reporting contributing event counts and the distinct
actors per entity. 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,
  presence, commenting/annotation, review/approval, notification center,
  audit/compliance surfaces, enterprise tenant isolation)
- **Direct URL / bookmark** — yes

## Layout regions

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

- **Shell header** — from `ShellLayout`
- **Workspace `<h1>`** — "Studio Activity & Change Feeds" (`WorkspaceHeading`),
  followed by a `data-activity-change-feeds-summary` paragraph describing the
  coalescing
- **Change Coalesce Lane** (`<h2>` `data-cf-lane-heading` "Change Coalesce
  Lane") — the catalog/loading/unauthorized/error states, the coalesce form, and
  the result
- **Quick-actions panel** — `panel` with 7 `quickAction` links to sibling
  workspaces (no self-link, no Route Map)

## States

- [ ] **Loading** — `data-cf-loading` "Loading change-feed coalescer…" while the
      GET catalog request is in flight (`outcome === null`)
- [ ] **Unauthorized** — `data-cf-unauthorized` "Access restricted." on 401/403
      (admin scope required); shows the BFF message or "Studio admin scope
      required to coalesce the feed."
- [ ] **Error** — `data-cf-error` "Could not load the change-feed coalescer." on
      any non-OK catalog response or network failure
- [ ] **Ready (form)** — catalog loaded; `data-cf-enums` shows
      `{changeTypes.length} change types · {netChanges.length} net changes`, and
      the `data-cf-form` form renders
- [ ] **Result** — after a successful POST, `data-cf-result` renders the feed
      headline + per-entity table
- [ ] **Coalesce validation error** — `data-cf-coalesce-error` for invalid JSON,
      network unavailable, or a non-200 POST (shows the POST `detail`)

## Interactions

### Change Coalesce Lane form

- [ ] **Events** (`textarea` `data-cf-payload`, `aria-label="events json"`) —
      JSON `{ events[{ eventId, entityId, changeType, actor }] }`; seeded with a
      default payload
- [ ] **Coalesce feed** (`button` `data-cf-submit`, `type="submit"`) —
      `submitCoalesce` parses the JSON (rejecting invalid JSON with
      `data-cf-coalesce-error`) and POSTs to
      `/v1/admin/studio/change-feed/coalesce`

### Result

- [ ] **Headline** (`data-cf-headline`) — `<data-cf-total-events> →
      {entries.length} entities · {createdCount} created · {updatedCount}
      updated · {deletedCount} deleted · {noOpCount} no-op`
- [ ] **Entries table** (`data-cf-entries`) — one `data-cf-entry-row` per entity
      with `data-entity-id`, `data-net-change`, plus `data-cf-net-change` /
      `data-cf-event-count` / `data-cf-actors` cells

### Quick actions

- [ ] Open Real-Time Collaboration Substrate workspace
- [ ] Open Presence and Cursor Systems workspace
- [ ] Open Commenting and Annotation System workspace
- [ ] Open Review and Approval Workflows workspace
- [ ] Back to Notification Center workspace
- [ ] Back to Audit and Compliance Surfaces workspace
- [ ] Back to Enterprise Tenant Isolation workspace

## Data & contracts

- **GET catalog**: `/v1/admin/studio/change-feed` — fetched on mount with
  `buildBffAuthHeaders()` and `cache: 'no-store'`; returns `{ changeTypes[],
  netChanges[] }`
- **POST verb**: `/v1/admin/studio/change-feed/coalesce` with body `{ events[{
  eventId, entityId, changeType, actor }] }` → `{ result: { entries[],
  totalEvents, summary{ createdCount, updatedCount, deletedCount, noOpCount } } }`
  (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-notification-center.md`](./studio-notification-center.md),
  [`studio-presence-cursor-systems.md`](./studio-presence-cursor-systems.md),
  [`../governance/studio-audit-compliance-surfaces.md`](../governance/studio-audit-compliance-surfaces.md)
- Component:
  `apps/oshun/web/src/components/studio/StudioActivityChangeFeedsWorkspace.tsx`
- BFF route: `apps/oshun/bff/src/routes/admin-studio-change-feed.ts`
- BFF store: `apps/oshun/bff/src/studio/change-feed-store.ts`

## Open questions / known gaps

- [ ] Confirm whether coalesced feeds persist, or whether the lane stays a
      stateless coalescer
- [ ] Document the net-change resolution for interleaved create/delete/update
      runs beyond the default cancel/collapse rules
