Read the full contract
# Contract: the supervision console (hero demo)
You are working in a git worktree for an orchestrator who reviews, refactors and commits your
work. Work here, not in the main checkout. Leave changes uncommitted and do not push.
Ask through `ask_orchestrator` only when blocked by something the contract and repo do not
settle, or when a decision with real consequences is needed. Those calls pause your work.
Progress reports, findings and corrections go in the hand-off, not in questions.
## Context for every unit
`site/` is foreman's landing page (Astro 7 static, React islands, Tailwind 4, Bun), at
https://foreman.steamfield.io. Four delegates build its sections in parallel from the same
commit, each owning different files. The orchestrator designed the page and owns the layout,
the tokens and `src/pages/index.astro`, which already renders your component through the
stub interface in your scope. Keep that interface's export names and props unless the
contract says otherwise; you may enrich the types it names.
Read first, in `site/`: `AGENTS.md`, `docs/PRODUCT.md` (the spec: your section's question and
outline entry), `DESIGN.md` (all of it: tokens, the rule about orange, type, shape, motion,
accessibility), `src/styles/global.css` (token utility names), `src/components/landing/primitives.tsx`
(reuse `Section`, `DataTable`, `CodeFrame`, `StateBadge`, `Inline`; don't fork them),
`src/data/runs.ts` (the recorded-run types), `src/lib/format.ts`, `src/data/landing.ts` (copy),
and `src/pages/index.astro` (where your component sits). Look at one recording in
`fixtures/runs/` and `fixtures/cli/`.
Rules that apply to every unit:
- Demos replay recordings. Never hand-write foreman output or Codex events: take them from
`fixtures/`. Prose written for the page (Claude's side of a conversation, captions) is
labeled as written for the page where it appears beside recorded output.
- Style with the token utilities from `global.css` (`bg-console`, `text-ink-3`, `rounded-frame`,
`font-display`...), never raw hex, never `dark:` variants. A value you need that isn't a
token: ask, don't invent one.
- Every island server-renders a useful final state: with no JavaScript and under reduced
motion it shows the finished content, statically, and nothing depends on animation to be
readable. Use `@steamfield/ui` (read `site/node_modules/@steamfield/ui/README.md` and its
`.d.ts` files) for clocks, gating to on-screen, copy buttons and tabs rather than writing
your own. Its playback gate already handles reduced motion and `[data-sf-motion="reduce"]`.
- Keyboard: every control is a real `<button>` or link, focus is visible (the global focus
ring), and interactive demos announce state changes politely (`aria-live="polite"`) without
re-announcing every streamed line.
- Phone widths (375px) work: no horizontal page scroll, long command lines scroll inside
their own frame.
- Analytics: islands call `track()` from `src/lib/analytics.ts` with events declared in
`src/lib/events.ts`; the names you need already exist.
- A sibling site built the same way, for reading only (never copy names or paths into tracked
files): `~/Projects/rr/site/` (`src/components/landing/TerminalPlayer.tsx`,
`CommandTabs.tsx`, `FlowDiagram.tsx`, `MonitorStage.tsx` and their tests show how islands
there use `@steamfield/ui` and how they're tested).
## Goal
Build the hero's supervision console: one framed console, two panes on one clock, replaying the
recorded `fix-supervisor` run (`fixtures/runs/fix-supervisor.json`, loaded with
`loadRun("fix-supervisor")`). It answers the hero's question, "what is it?", by showing
supervision happening: Claude delegates, Codex works, Codex asks, Claude answers, the turn
finishes, Claude reviews. See `docs/PRODUCT.md` section 1 and `src/data/landing.ts` `hero`.
The run took 70.5 minutes. The replay compresses it to about 60 seconds of playback.
### The two panes
Left, "Claude Code" (narrower, about 5/12 at desktop): Claude's side, as a Claude-Code-style
transcript: a `❯` user turn, Claude's short prose, and `Bash(...)` tool calls with an indented
`⎿` result. Steps, in order, each pinned to a moment of the run's clock:
1. User (written for the page): "Fix part B of the release findings: supervisor state,
persistence and recovery. Hand it to Codex."
2. Claude prose (written): one sentence saying it wrote a contract and is starting a delegate
in its own worktree, waiting in the background.
3. Tool call at t=0: `foreman start fix-supervisor --new-worktree --setup "bun install"
--contract fix-supervisor.md --write --wait`, shown as running in the background. Its
result lines are the recorded digest's `run:`, `worktree:` and `branch:` lines (from
`run.digest`, the first lines of a real `foreman status` for this run). Label the step
"background" while the run is active.
4. At q1's asked time: the background wait returns `question` with id `q1` and the full
recorded question text (from `run.questions`). The `q1` marker uses `signal`, per the rule
about orange.
5. Immediately after: tool call `foreman answer fix-supervisor q1 "<recorded answer text>"
--wait`, result `ok`, then the step continues in the background. The answer text is
`run.questions` q1's `Resolved.text`, verbatim (it is long; the console truncates it
visually with a "show all" control, never in the data).
6. At q2's asked time: the wait returns `question` q2 (recorded text). Claude does not answer
it (in reality no one did). At q2's resolved time, show the recorded auto-answer text and a
one-line note written for the page that the 15-minute question timeout expired and foreman
told Codex to proceed and record its choice.
7. At turn completion: the wait returns `done`, followed by the recorded digest's `commands:`
and `outside May change:` lines, then Claude prose (written): it will read the hand-off,
review the diff and run the checks before committing.
Right, "Codex: fix-supervisor" (wider): the delegate as foreman sees it. A header row with a
`StateBadge` (surface `console`) that follows the recorded `state` events (`running`,
`waiting` while a question is pending, `done` once idle after a completed turn), the elapsed
run time in foreman's format (`formatElapsed`), and counters for commands (with failed count),
files written and tokens (`formatTokens` on input and output, from the latest recorded totals
you can reach at that point, or the final `run.tokens` once done; don't invent intermediate
token values if the fixture lacks them: in that case show tokens only once done). Below it,
a feed of the recorded events: commands with an exit mark (exit 0 in the `done` state color,
non-zero in `failed`, null as running), file writes as relative paths, Codex's messages,
the hook block, and the two questions highlighted in `signal`. The feed keeps roughly the
last 12 entries visible and scrolls within itself; long commands truncate to one line with
the full text available (title attribute is enough).
The replay clock: map run time to playback time piecewise, so long quiet stretches speed by,
and hold about 2.5 seconds of playback at each question, the answer and the completion so a
reader can read them. Put this mapping in `src/data/supervision.ts` as pure functions with
unit tests (monotonic, total close to 60s, holds at the marked moments). Not every one of the
417 events needs to appear: thin the command stream deterministically (for example the
commands around each marked moment plus an even sample elsewhere) while keeping every
message, question, hook block, file write and state change.
Controls in the frame's bar: Pause/Play and Replay buttons, and a short label such as
"replaying 70m30s in 1m". Playback starts when the console is on screen (the library's
playback gate) and fires `demo_replayed {id: "supervision"}` when the reader presses Replay.
Final state (no JS, reduced motion, and after playback): both panes show the whole story: the
left pane's full transcript, the right pane's header in its final state with the last entries
of the feed and the two questions visible. Below the frame, render `hero.consoleCaption` from
`src/data/landing.ts` as the caption.
Layout: at 1024px and up the panes sit side by side inside one console frame; below, they
stack, Claude first. Height is fixed at desktop (about 34rem) so the page doesn't jump while
playing. The frame follows `DESIGN.md` "Shape and depth" (the console shadow) and uses the
`console` tokens.
## Interface
- `src/data/supervision.ts`: `buildConsoleScript(run: Run): ConsoleScript` and the
`ConsoleScript` type (you design its fields), plus the time-mapping helpers. Pure, no React.
- `src/components/landing/SupervisionConsole.tsx`: `SupervisionConsole({ script })`, rendered
by `index.astro` as `<SupervisionConsole client:visible script={...} />`. Keep the export
name and the `script` prop.
## Scope
- May change: `site/src/data/supervision.ts`, `site/src/data/supervision.test.ts`,
`site/src/components/landing/SupervisionConsole.tsx`,
`site/src/components/landing/SupervisionConsole.test.tsx`,
`site/src/components/landing/console/` (new, for subcomponents if you need them).
- Must not change: everything else, including `index.astro`, `primitives.tsx`, `global.css`,
`runs.ts`, `landing.ts` and `fixtures/`. If you need a primitive or token changed, say so in
the hand-off with the exact change; if it blocks you, ask.
- Locked: none.
## Skills to apply
Find the newest installed Claude Code `claude-essentials` marketplace copy:
```sh
ce=$(ls -d "$HOME"/.claude/plugins/cache/*/ce/*/ | sort -V | tail -1)
```
Read the applicable skills; say in the hand-off where they changed your approach. The repo's
own rules win where they differ.
- `ce:writing-tests`: `${ce}skills/writing-tests/SKILL.md` for every test, including
`references/waiting-typescript.md` for tests that wait on timers, and
`references/flaky-tests.md` for isolation and repeat-run checks.
- `${ce}skills/design/SKILL.md` and `references/craft-tailwind.md`: craft rules for anything
visual. `DESIGN.md` wins where they differ.
- `${ce}skills/architecting-systems/SKILL.md`: keep data shaping (pure, tested, in `src/data/`
or `src/lib/`) separate from rendering.
- `${ce}skills/writer/SKILL.md`: any copy you write (captions, labels, empty states).
## Acceptance (every unit)
From `site/`, iterate until these pass:
```sh
bunx vitest run <your test files>
make lint
make check
make test
make build
```
Tests run in Vitest with happy-dom and Testing Library, through the component's real props
built from the real fixtures; drive time with fake timers or the library's test hooks, never
sleeps. Test the server-rendered final state (render to static markup) as well as the
interactive behaviour. Run each new test file three times on its own, then `make test` once.
Don't run `stacks verify` (the orchestrator does).
Then QA as the first user: run `make dev` (set `PORT_WEB` to a free port; `free-port 4400`
prints one), open the page with a real browser (Playwright from `node_modules` or the
`playwright-cli` skill if installed), and check your section at 1440px and 375px wide, with
motion on and with `prefers-reduced-motion: reduce`, and with JavaScript disabled. Save
screenshots to your review directory (below) and list them in the hand-off. Fix what looks
wrong against `DESIGN.md`. Stop the dev server when done.
## Do not
- Commit or push; leave changes for review.
- Disable lint, weaken a test, add a cast or suppress a type error to get green.
- Add features, options, abstractions or mechanisms the goal and acceptance do not need.
- Add a dependency; `@steamfield/ui`, React and Tailwind cover this.
- Edit `~/.codex` config, hooks, trust or auth.
- Mention any repository outside this one in a tracked file.
## Review before hand-off
Once acceptance checks pass, get one independent review in a fresh Codex process and resolve
its findings. Read `${ce}agents/code-reviewer.md`. There is no repo reviewer for `site/`.
Use separate simple commands and the file-writing tool, not a shell function, grouped command,
heredoc or generated shell script. Run from the worktree root:
```sh
mktemp -d
```
Keep the printed absolute directory as the review directory, outside the worktree. With the
file-writing tool, write `<review-directory>/prompt.md` containing, in this order:
1. The marketplace reviewer agent body without YAML frontmatter.
2. The applicable skill paths.
3. These task instructions and the full filled contract:
"Review uncommitted changes and new files in {ABSOLUTE WORKTREE PATH} against base
site/landing. Your sandbox is read-only; don't run tests or edit files. The author already
ran the checks. Check the contract, site/DESIGN.md and site/AGENTS.md and report every real
defect with a file:line reference."
Then run one plain command, replacing `{REVIEW DIR}` with that printed absolute path:
```sh
codex exec --ephemeral --sandbox read-only -m "gpt-6-astra" -c 'model_reasoning_effort="high"' -c 'service_tier="priority"' -o "{REVIEW DIR}/review.md" - < "{REVIEW DIR}/prompt.md"
```
Read the resulting `review.md`. Fix every finding or explain in the hand-off why it is wrong.
A bug finding gets a regression test before the fix. Rerun affected acceptance checks after
fixing. One independent review only. If the review cannot run, ask the orchestrator rather
than claiming it passed.
## Hand-off
Leave everything uncommitted and end with this report, naming files and symbols:
1. Summary: what you built, in two or three sentences.
2. Files: each file created or changed and why.
3. Decisions: choices left open, deviations and why, and where skills changed the approach.
4. Checks: each command and result with test counts; each new test file's three isolated runs
and the full-suite result. QA: what you ran, the screenshot paths, what you changed.
5. Review: the review file's absolute path, each finding and its resolution or dispute.
6. Look closer at: assumptions, edge cases or tests the orchestrator should examine.
7. Open: unfinished work, blockers and questions.