# /seats page — S1: the admission pool, visible

status: ACTIVE
slug: seats-page
design authority: docs/specs/2026-08-16-seats-page-design.md — READ IT FIRST; it pins the
seat definition (admission slots of `_agent-session-admission`, cap `AGENT_SESSION_SLOTS`,
live state at /run/user/1000/agent-session-slots), the adapter/page contracts, and the
emergency-seat reference (docs/specs/2026-08-15-seat-limiter-emergency-design.md).

**This plan is S1 ONLY: the read-only page.** S2 (cap + emergency configuration) and S3
(automatic priority dispatch) are designed in the spec and explicitly out of scope here.

## Tasks

1. `collector/src/adapters/seats.ts` + tests — implement `collectSeats()` exactly per the
   spec's `SeatsSnapshot` contract: slot classification and holder resolution EXACTLY per the
   spec's corrected model (flock-probe four-state classification; lock-inode holder
   enumeration correlated to ledger pids — never stamp-pid equality; strictly read-only,
   never the limiter's unlink path), then session name + work_key + adwId via the
   attribution seams landed 2026-08-16 (collector/src/sessions/ledger.ts sessions dir,
   collector/src/requests/, task-board-sync mapping files).
   Register on the existing adapter scheduler exactly like the github-checks adapter.
   `emergencyDebt` present, 0 until the limiter journal exists. Fixture-driven tests:
   free-with-stale-stamp (probe succeeds), held (probe fails + stamp matches /proc),
   wedged (probe fails + dead stamp + confirmed), unknown (confirmation unavailable),
   unreadable-dir.
2. `GET /seats` collector route + tests, authenticated same as existing page routes.
3. `apps/web/src/pages/seats.astro` + `apps/web/src/components/seats/SeatsApp.tsx` +
   tests — per-host section (cap, free, queue depth, emergency chip), seat rows (state,
   session NAME, task title, held-for), diagnostics affordance for raw ids (DetailDrawer,
   never a new primitive), click-through: factory holders →
   `/factory/[adwId]` and its agents → `/factory/[adwId]/agents/[agentId]`; non-factory
   holders → their board-row drawer. Degraded state renders "limiter state unavailable" —
   never a fabricated pool. New page = Astryx primitives; tokens only; both themes;
   gallery registration for any new deck-ui component; slopgate green.
4. Sidebar entry for /seats in owner language ("Seats").
5. Verification: collector `bun test` + typecheck green; `pnpm --filter web test` +
   `typecheck` green; barrel⊆gallery test green. Real-data smoke: with the dev collector
   running, GET /seats shows the live pool including the CURRENT session's held slot and
   the existing wedged-lock artifact from 2026-08-15 — quote the real response in the
   receipt.

## Acceptance (owner-visible)

The owner opens /seats and sees: how many seats each machine has, which are free, which
session holds each busy seat by NAME with its task title, how many launches are waiting,
and clicks a factory-held seat through to the run and an agent's human-readable activity
story. Wedged locks are visible, labeled plainly.

## Receipts

2026-08-16: plan authored from the seats-page design spec.
