# Owner dashboard assets and client
A single responsive view for projects, agent lanes, packets and immutable fork-bound
claims, blockers, receipts, review decisions and receive-ordered activity. This
package builds static assets and a typed transport. It does not deploy a Worker.
FH-012 mounts these assets at `/app`, validates Cloudflare Access JWTs (`aud` and
`iss` from owner configuration), and protects the browser's owner API calls.
FH-008 supplies the sidebar/gadget host and its Cap'n Web `ui` capability.
Every user-visible product name and tagline is read from root `brand.json` at
build time. No owner hostname or owner API credential is embedded in the assets.
Production requests use the page's origin, `credentials: same-origin`, no-store,
and redirect refusal. Access supplies its assertion at the edge; the client never
sets an assertion or bearer header. MCP authentication remains a host concern.
## Run and check
After the root and exact pinned submodule dependencies are installed:
```sh
pnpm --filter forkhorse-dashboard dev:fixture
# Open the printed loopback origin at /fixtures.html.
pnpm --filter forkhorse-dashboard test
pnpm --filter forkhorse-dashboard types:check
pnpm --filter forkhorse-dashboard build
pnpm --filter forkhorse-dashboard build:fixture
pnpm test
pnpm lint
```
`dist/` contains the production `index.html` and assets with `/app/` paths.
`dist-fixture/` is a separate, ignored fixture build. The fixture dev server alone
mounts the labeled fictional API. A static fixture build requires that same fixture
API to be served by a test host; it does not emulate responses in the browser.
Restart the dev server (or POST to its test-only `/__fixtures/reset`) to reset fixture approvals/promotions. All fixture actions
are local. Passing these checks is not Access, Cloudflare, deployment or live Git
acceptance.
## Owner decisions on Overview
The owner-only **Needs you** card routes to evidence, with no approval or promotion
controls. It lists uncertain promotions, reconciler alerts, open collisions,
failed required receipts, then reviews ready to promote or approve. Readiness
uses FH-045's shared state derivation and review gate; Returned, Fork moved,
Main moved, Out of date and promoted reviews are excluded, including approved
reviews whose core stale flag is false. Five rows show initially, with expansion in place and a persistent
empty state. Agent connections render no card.
Review rows use the existing detail route; hosts without detail reads fall back
to the review queue. Collisions open Work packet details, and reconciler rows
focus the matching Agents row (or the Agents heading when the canonical repo
has no agent row). Promotion intents without a review route focus Last promotion.
Within each kind, review request times and Core's receive-sequence alert/collision
IDs provide oldest-first ordering. A current-head alert without a sequence follows
the recorded alerts; older capability snapshots without metadata retain their
supplied order and available evidence.
The loopback fixture harness includes `needs-all`, `needs-promotion`,
`needs-reconciler`, `needs-blocker`, `needs-failed`, `needs-promote`,
`needs-approve`, `needs-advisory`, `needs-empty`, and `needs-main-moved`. The last
fixture pairs a nonstale approved Main moved review with a ready-to-promote
review, showing only the latter in the card. Use `role=agent` to check
absence and `host=queue-only` to check the capability fallback. These snapshots
are synthetic, and fixture routes are excluded from production assets.
## Agent status and resume
FH-078 shows one Agents row per enrolled identity, ordered by the existing
saddlecloth rank. Its most urgent live claim determines Status and Packet;
additional live claims appear as a count. Last push comes from loaded fork
activity, with exact time in a tooltip. Returned reviews, failed or running
receipts, current review/approval and pushing-fork collision gates precede
claim activity. The table uses the existing state words, tones and usage figures.
A held claim is not a heartbeat. After 30 minutes without a loaded push the
row says **Claimed, no recent push**, with the observed push or claim time.
That absence requires the oldest loaded activity to reach back at least
30 minutes. Shorter history keeps the active-claim state and says
**Not in loaded activity** when no push is visible. A claim newer than the
loaded history can say **No push yet**. Idle agents, including legacy identities
without enrollment timestamps, start collapsed; Needs you reveals a targeted
idle row before focusing it.
The agent Overview starts with **Resume where you left off** when the host
supplies a caller name found in the existing state. This read-only reduced
card shows Packet, Fork, assignment-scoped Last push and the table's Status.
It has no Next row because this host does not provide `myWork` next actions.
No live claim reads **No live claim.** An unknown caller has no card.
The owner Overview has no Resume card. No adapter or endpoint changes are needed.
Labeled local scenarios `agents-status`, `agents-short-activity`, `agent-resume`
and `agent-idle` cover all statuses and the activity-history boundary. Use
`role=agent` for the read-only frame copy and reduced Resume card. Fixtures
and source authoring do not establish acceptance; focused/root tests, lint,
fresh-clone/D5 and light/dark capture review remain separately gated.
## Race judge advice
FH-063c reads the fixed optional race `judge` and `verdict: {ranking, rationale,
at}` seam. The UI calls this **Judge's advice**, shows the ranked entrant
saddlecloths, and keeps advice visible after a race finishes. Missing judge fields
leave the existing race view unchanged. Rationale is escaped plain text; more
than five rendered lines can expand in place, with the full text retained in the DOM.
The owner can promote the pick through the existing review confirmation and
promote endpoint, with the same visibility gate as the review queue. A pending
promotion or required before-promote re-check hides Promote. Later review or push
evidence also hides the advice action until new advice arrives. Agents see advice
without owner controls. No dashboard advice submission or new write endpoint is
introduced. Review detail and Needs you carry the pick's advisory context.
`judge-*` loopback scenarios cover waiting, owner/agent, later pushes/reviews,
promotion gates, finished results, long rationale and no-judge views. These are
fixed-contract fixtures only; accepted-core integration and live acceptance are
separate gates.
## Host composition
Serve `dist/index.html` for `/app` and `/app/` (or redirect `/app` to `/app/`), and
serve its immutable hashed assets under `/app/assets/`. Protect both the HTML and
asset paths with the configured Access application. Protect state, activity,
approve and promote routes with the same owner identity in core. Never inject an
owner API token. `/app?project=demo` selects a project. The HTML may contain this
inert owner-configured project list before the module entry (escape JSON for HTML
when generating it):
```html
```
There is no project-list route in FH-003/FH-005. The shell therefore uses configured
project IDs and an “Open project by ID” field. It never invents project data.
For a sidebar app or gadget, import `mountDashboard`, include `src/styles.css`,
and pass `brand`, project options, and a host-owned `DashboardTransport`. Its
interface has `getState`, `getActivity`, `approve`, `promote`, and optional
`subscribe`, owner-only `getReceipt` and `setSettings`. The UI does not import Cap'n Web or mint a capability. The future
host adapter implements that interface from its authorized `ui` capability.
## Wire contract
`StateResponse` in `src/contracts.ts` mirrors the public FH-005 JSON snapshot.
`createHttpTransport` maps it through `stateView` into a `ProjectState` view:
- `GET /projects/:id/state` supplies project, credential-free agents, forks with
heads, packets, live-only claims including `{packet, agent, repoName}`, blockers,
reviews, ledger, recent activity, reconciler alerts and `promotionPending`.
- Approval state comes from `{sha, receiptDigest}` or `null`; `stale` and `promoted`
are explicit flags. A changed approval SHA is treated as stale. Digest binding applies to required review-created receipts; a fresh before-promote run or advisory receipt movement does not mean the fork moved.
- FH-048 blockers retain `resolved: null | {at, by, reason?}` history. Missing legacy resolution reads as open; missing `at` and `pushingPacket` read as unknown. Open blockers gate the pushing fork and attributed packet (all reviews on that fork when attribution is null), never the claimant.
- FH-032 adds `project.settings.requireReceipt` and the five-state `reviews[].receipt` summary: status, trigger, digest, timestamps, duration and check outcomes. No logs enter `/state`.
- Owner-only `GET /projects/:id/reviews/:rid/receipt` returns the stored `{digest, receipt}` runner record. The panel shows the last 200 log lines and can expand all bounded output. A changed receipt/selection discards late responses. Agents have no log control or request.
- Owner-only `PATCH /projects/:id/settings` sends only `{requireReceipt}` and reloads authoritative state. An absent setting is unavailable, never synthesized as On or Off.
- Core currently does not return the canonical head in state. The shell says
“Head unavailable” when commits are recorded, or “No commits yet” for an empty
snapshot; it does not infer a current head from push history.
- `GET /projects/:id/activity?after=:sequence&limit=100` follows the last receive
sequence in the recent snapshot. It deduplicates and displays the latest 100
available events; event timestamps do not determine ordering.
- Approve and Promote send `{}` to `POST /projects/:id/reviews/:rid/approve` and
`POST /projects/:id/reviews/:rid/promote`. Promote reads `{sha}`. Every action
reloads state; writes are never automatically retried. A failed refresh clears
actionable old state. A `409` message such as `stale base, rebase` stays visible.
The client hides decisions for a moved fork, stale review or packet blocker.
With `requireReceipt: true`, approve requires a passing summary and digest;
already-approved reviews may enter promote so core can freshly re-run checks,
including after a setting flip. A failed before-promote run hides Promote until
a fix; a timed-out or couldn't-run attempt keeps Promote with its retry caption.
An approved Re-checking row has no gate caption. A required missing receipt shows
Not started in its cell and Waiting for checks only under State. Its exact receipt-failure 409 keeps the dialog
open with the new receipt available and a Warning icon. That handled conflict
has no duplicate page banner or raw core error copy. With `requireReceipt: false`, all receipt
outcomes are advisory and promote shows no re-check state. The server remains
the authority. Unknown settings withhold actions until a valid snapshot arrives.
All API/config text is escaped before entering markup.
HTTP works without sockets. `createHttpTransport` accepts an optional host-supplied
`socketUrl(project)` for an existing same-origin WebSocket route. There is no socket
endpoint in FH-003/FH-005; this package does not claim one exists. Socket messages
invalidate state, then the UI re-reads HTTP; writes never pass through the socket.
The host should provide invalidation/reconnect behavior through its capability
transport if it needs more than this optional socket. Unmount closes subscriptions.
## Fixture acceptance
The package tests mount the real view in jsdom and use fixture JSON at the HTTP
boundary. They exercise Approve and Promote endpoints, duplicate clicks, stale
conflicts, session expiry, project selection, empty views, escaping, exact fork and
receipt gates, activity cursors and socket origin/cleanup. Browser acceptance uses
`/fixtures.html` at phone and desktop widths against the loopback fixture API.
The API deliberately returns the FH-005 wire shape, not a separate mock UI model.
## Kumo workspace UI
The same real Kumo components render the standalone dashboard and agent frame.
Overview, Work, Review and Agents use underline tabs with hash selection. Activity
is a collapsible panel and a Kumo modal drawer below 1024px. Packet details replace
the feed without navigating. React retains focus during snapshot refreshes.
The agent frame subscribes to the pinned host theme once, implements
`GatekeeperAppThemeReceiver.setTheme`, and uses upstream `applyAccentColor`.
The standalone copy follows system appearance. The reviewed host palette, font
and radius definitions are in `src/host-tokens.json`; component CSS consumes
semantic variables. Appearance never supplies authorization.
Approval still binds the exact SHA and receipt digest. Promotion confirms the
target branch and exact head in a Kumo Dialog, then the existing server gate runs
again. Agent connections disable these actions and refuse them in the controller
and transport. Ask an agent opens the host composer without sending the prompt.
Contract gaps remain visible: no older-history cursor
on the snapshot capability; no packet update time,
or connected-agent identity. Those fields/actions are labeled
unavailable or pending rather than synthesized. Activity preference storage is
best-effort: an opaque sandbox may deny browser storage.
## FH-033 offline receipts evidence
The receipt fixtures (`receipts`, `advisory`, `recheck`, `rerun-failed`) implement
FH-032's fixed wire contract and the 16:55 advisory ruling. `role=agent` exercises
the shared read-only view; it does not simulate owner authority. Check copy and
warning dots follow the receipts spec: Timed out and Couldn't run. Durations use
supplied milliseconds, including the timed-out check's own duration. Receipt
completion has a polite announcement; elapsed time changes never spin.
Required handback remains fixture captures in both themes, narrow/phone frames,
owner and agent details/settings, promote pending/409, advisory actions, empty,
error/loading, focus, theme accent and first paint. The fetched base already emits
the sidebar bundle after its loading caption at the end of body. FH-033 leaves
that gatekeeper source unchanged and compares baseline/final first paint.
Farrier design PASS, superprime code acceptance and the post-FH-032L live receipt
capture are separate gates; this package's offline checks cannot establish them.
## FH-036 receipt history
Review shows the five newest public receipt summaries, including promoted reviews.
Finished receipts sort by their finish time; running receipts sort by their start
and show no invented finish time. Each row opens the existing receipt panel.
Overview's Last promotion opens a receipt only when the ledger review ID, commit
and receipt digest match its public summary. Missing or replaced summaries leave
the promotion line without an outcome. Owner logs retain the existing Access
session route; agents have no logs control.
The `history-required` and `history-advisory` fixtures include shuffled promoted
reviews, more than five receipts, both triggers and an advisory failure. These
are offline fixture evidence; design review, owner acceptance and live checks
remain separate.
## FH-040 review detail (FH-039 contract integration pending)
The queue opens a full-width detail at `#review/`; Back returns to the queue.
Recent receipt rows still open the receipt panel. Checks, Approval and Main each
have their own status and evidence control. The final milestone is Promoted to
main. Promotion evidence comes only from this review's ledger entry.
The optional host transport methods `getReview`, `getDiff` and `returnReview` follow
FH-039's fixed contract. The HTTP transport uses the existing same-origin session
for `GET /projects/:id/reviews/:rid`, `GET …/diff?path=…`, and owner-only
`POST …/return` with `{reason}`. Reads and writes never carry browser tokens.
A host without `getReview` keeps non-linking queue rows and resolves detail
bookmarks back to the queue. It does not invent detail from packet claims or push history. FH-039 core integration and
capability-host support must be checked separately after FH-039 lands.
Diffs fetch only when expanded, accept at most 64 KiB, retain added/removed
prefixes, and render as escaped plain text. Returned reasons retain line breaks
for owners and agents. Return requires a visible labelled textarea with 1–2000
characters and a counter. The controller refuses agent writes, stale/promoted/
returned reviews and duplicate writes; an uncertain owner write is never retried.
The dialog alone owns Return errors, including the promoted 409.
`review-*` scenarios in `test/review-fixtures.ts` are labeled offline FH-039
contract fixtures: current, approved, promoted, returned, stale, advisory failed,
400-file changes (200 visible), detail loading/error/not-found, and return 409.
These fixtures do not establish Cloudflare or owner acceptance. Dashboard tests
exercise routes/back navigation, independent evidence, escaping, lazy diffs,
return bounds, agent restrictions, owner conflicts and existing receipt gates.
Farrier design PASS and the superprime's code review remain separate gates.
Use `?scenario=review-current&host=queue-only#review/review-1` in the fixture entry
to exercise the dashboard fallback with `getReview` and `getDiff` absent.
## FH-045 stale causes and quick durations
Review detail and queue derive the first proven state from the public snapshot:
Promoted, Returned, Fork moved, Main moved, then Out of date. Returned reviews
never show a stale banner. Fork moved requires a known fork head different from
the review SHA. Main moved requires a ledger entry whose latest `after` differs
from the review base, no `current-head` reconciler alert, and an explicitly null
`promotionPending`. It applies even when core reports `stale: false`; Approve,
Promote and Return are hidden. Out of date requires `stale: true`. If main movement
cannot be proven and the review is non-stale, its normal state stays visible.
Approval binding and server checks still govern owner writes.
All receipt surfaces format durations below one second in whole milliseconds
(for example `227 ms`); values from one second retain the existing format.
Synthetic fixture projects include `review-main-moved` (the non-stale approved
photo-finish loser), `review-main-moved-stale`, `review-out-of-date`, and stale and
non-stale reconciler/pending-promotion variants. Open their detail at
`#review/review-1` or queue at `#review`. These fixtures establish offline rendering
only. Design PASS and independent code acceptance remain external handback gates.
## FH-051 archive (FH-050 integrated; final acceptance pending)
The owner sees Archive in the packet details header only when the public core
fields allow it: no live standard claim or open race entrant, no raw open review
(`promoted`, `stale`, `returned`), and no unresolved blocker on either its claiming
or pushing packet. A derived Main moved or invalid-approval display state does
not close a raw core review. Legacy absent archive/resolution fields remain
unarchived/open. Agents see the archived list without write controls.
Optional `archivePacket` and `unarchivePacket` transport capabilities use owner
`POST /projects/:id/packets/:pid/archive` and `…/unarchive`, then reload the
snapshot. The packet panel stays open and focus follows Archive/Unarchive.
Conflicts re-fetch without retrying and show the panel's sanitized Warning line;
a missing packet closes its panel. Archived packets leave Work, Review, Needs
you and the Open packets count. The ghost Show archived toggle is local view
state, resets on reload, and appends archived rows newest-first with neutral
badges, full-contrast text and polite announcements.
Recent receipts, promoted review bookmarks, Last promotion and the ledger retain
all historical evidence. Activity remains Git-push-only: FH-050's private keyed
archive audit is not exposed by `/state`, and this UI adds no feed events. The
archived panel caption and badge tooltip read `archived.at` from the packet.
`archive-*` scenarios are labeled offline fixtures checked against the merged FH-050/FH-052 public contract, including
standard claim, race entrant, raw open review, non-stale Main moved, pushing
blocker and archive 409/404/error. Package tests and local captures establish
fixture behavior only. The normal merge retains FH-049 collision history and the
accepted race, graph and token projections. Active race rows still open the track;
Packet details opens the existing panel for archiving, and archived race rows
open the panel directly for Unarchive. Reserved root/fresh-clone/D5 acceptance,
Saddler's design PASS and Superprime's code review
remain separate handback gates.
## FH-049 collision resolution (FH-048 integrated; final acceptance pending)
Owner-only `POST /projects/:id/blockers/:bid/resolve` sends `{reason}` (1–200 characters, with non-whitespace content) through the same-origin session. Agents have no control or request. The dialog shows paths and editable secondary quick-reason buttons in a wrapping row, supports Cmd/Ctrl+Enter, and refreshes authoritative state after success or uncertainty without retrying a write. A repeat uses the returned resolution cause. The resolution record supplies the audit history; no activity event is assumed.
Resolved collisions remain as quiet rows in packet details and matching pushing-fork review details, excluded from counts, gates and Needs you. FH-048 review detail retains its claimant-packet filter; pushing-fork gates and history derive from all `/state.blockers`. Snapshot blockers survive an omitted detail list; a refreshed snapshot wins over stale detail resolution. Needs you opens and focuses the collision; success returns focus to Collisions and offers Open review in a 10-second toast and once on the newest resolved history row while the pushing packet has a current review. Legacy push times are never synthesized.
The `collision-*` scenarios are local contract fixtures, including history, 404, generic failure and claim-release/promote races. They provide no live core or Cloudflare acceptance.
## Recorded races (FH-055)
Race packets open a read-only full-width page at `#race/` in either host.
The track is a semantic table and becomes stacked lanes below 640px. It shows only
recorded milestones in claim order (equal timestamps sort by agent): Claimed, First push, Review requested, Checks
passed, Approved, Promoted. The shared race clock starts at the earliest claim;
there is no running timer or estimated progress. Results use core's recorded places.
The optional FH-052/053 projection carries `races`, agent enrollment `createdAt`,
and `ledgerEntryId` (the exact string `ledger[].id`). Legacy agents are numbered
alphabetically first, then enrolled agents by `createdAt`; response order never
assigns identity. Fixture race, enrollment, ledger and review slices are checked
against the core public types, including recorded `places` and quick receipts.
The reviewed `race-silk-*`, `race-cloth-ink`, and
`race-hurdle-ink` values live in `src/host-tokens.json`, with identity always carried
by the number. Missing ledger evidence or activity counts are omitted.
Results counts only default-branch pushes recorded on each entrant's fork from its
claim through its closure, the race finish, or the current time. It shares
the fork graph's packet interval filter. If the oldest loaded activity is later
than any entrant's claim, Results omits Pushes rather than showing a partial count.
`race-reused-forks` exercises historical fork totals of 20 and 3 with one push each
inside the race; the Results counts are 1 and 1.
The race view uses the accepted FH-057 `LaneRunner`, `LaneMarker`, `LaneConnector`
and enrollment identity helpers from `src/lane.tsx`. Marker shapes and silk styling
are shared with the fork graph; race-specific CSS only places them in the track.
The offline fixture harness includes `race-open`, `race-finished`, `race-photo`,
`race-empty`, `race-loading`, `race-error`, `race-unknown`, `race-standard`, and
`race-advisory`. Open `fixtures.html?scenario=race-photo#race/race-01` for the recorded
rejection panel, or `?scenario=race-open#work`, `#review`, and `#overview` for its
Work badge, Promote warning, and Needs you copy. These snapshots are synthetic;
they do not establish live race, deployment, or owner acceptance.
### Forks on Overview (FH-057)
The read-only hero projects `/state` into a clock-time graph above Needs you. It uses
live claims, loaded push activity, reviews and their current receipt summaries, and
ledger `recordedAt` times. Optional FH-052/053 races appear only when supplied.
Dense events fold at the measured plot width; their tooltips and the always-accessible
table retain the recorded facts. Below 640px only the table renders. The graph caps
at eight lanes with in-place expansion, a 24-hour maximum default window, guarded
viewer disclosure storage, and reduced-motion support.
`src/lane.tsx` owns the shared primitives for FH-055 and FH-057:
`LaneRunner({entrant, state})`, `laneSilkStyle(state, agent)`, `saddlecloth(state, agent)`,
`Saddlecloth({identity})`, `LaneMarker({kind})`, `LaneLine({path})` (SVG), and
`LaneConnector({reached})` (decorative race-table connector). Marker kinds include
`unreached`, `reached`, `claim`, `push`, `review`, `passed`, `failed`, `approved`, and
`promoted`. `agentIdentities(agents)` ranks legacy agents alphabetically before
agents ordered by enrollment timestamp, with alphabetical tie-breaking. The number
always accompanies the repeating `race-silk-1/2/3` colors. Unknown agents get no
invented number. Both `--lane-silk` and FH-055's `--entrant-silk` are supported.
Missing history stays missing: a promoted lane without its claim uses its earliest
loaded event and an open stub, with no invented branch junction. Standard review
approval timestamps and released standard claims are not exposed by the current
core snapshot. Approval marks render only if a recorded `approvedAt` is supplied;
the Approved state word remains available without a timestamp. Race release and
approval times come from recorded entrant fields. Earlier push history is explicitly
labeled when the loaded activity cannot cover a claim's start.
Fixture scenarios: `fork-hero`, `fork-resolved`, `fork-old`, `fork-many`, `fork-empty`,
`fork-dense`, and `fork-live` on `fixtures.html?scenario=…`. The live scenario has a
**Fixture new push** control that appends a synthetic push and invalidates the
snapshot through the normal read path. These routes and controls never ship in
production assets. Captures prove fixture rendering only; reserved root tests,
worker-host integration, fresh-clone/D5 checks, and design/code sign-off remain
separate acceptance gates.
### Fork graph bundle fixes (FH-057b)
The shared graph carries its closed-table hiding styles in the component as well as
in CSS, so an older host stylesheet cannot reveal the accessibility twin. Closed
cell controls leave keyboard tab order; opening the disclosure restores them.
Unloaded starts use a dashed lead-in with a recorded-start tooltip. Main SHA labels
reserve space for the newest promotion first, then fit older labels into the remaining
space. Every event cell shows its three newest events, with `+n earlier` exposing the
omitted facts on focus or hover.
For offline bundle verification, build the dashboard and gatekeeper sidebar assets,
then open `gatekeeper-fixtures.html?bundle=owner&scenario=fork-hero` or
`gatekeeper-fixtures.html?scenario=fork-hero` on the dashboard fixture server.
The owner route uses the built production entry and CSS against the local fixture API;
the gatekeeper route loads its built self-contained sidebar in an opaque-origin frame
with a synthetic host capability. Light/dark controls use the app theme path.
`fork-history` exercises dense main labels and capped cells. These harnesses are excluded
from production assets and prove offline bundle behavior only.
## Reported usage and race Compare (FH-062 local candidate)
The optional FH-060 wire projection keeps reported and metered cumulative snapshots
separate on claims, race entrants, and `usageAtReview`. Every token figure carries
its source. Full figures show report freshness on a separate caption line; dense
queue/graph cells show a compact figure with freshness and full details in the Tooltip. Review detail uses the immutable review snapshot;
promotion gates use the current entrant's core-computed budget state. Over-budget
and unchecked budgeted entrants have no Promote control. Unbudgeted legacy races
and ordinary packets need no usage report. An explicit core `budget: "unchecked"`
on a race with `tokenBudget: null` does not show Budget unchecked or hide Promote.
There is no new Needs you kind.
The accepted FH-055 race view includes Tokens before Result in its milestone track
and results table, with a facts-only Compare section directly below the track.
Compare uses FH-055 claim order and its shared recorded race clock. Checks
count passing receipts out of the entrant's receipts in both Compare and Results.
Files come from exact review detail, and push counts from
the shared FH-055b `packetPushes` interval helper. Results omits its Pushes column
and Compare omits its Recorded pushes row when loaded history does not cover every
entrant's claim. Counts end at entrant close, race finish, or now. No score, inferred line count,
price, or automatic ranking is computed. Missing detail is Unavailable. The queue's
Tokens column appears only when at least one queued review has a snapshot. Fork
usage appears only in label tooltips and the table twin, never lane geometry.
Budget figures appear once per source as a ratio; only core's selected source gets
the true-ratio meter and state. Within budget has no dot. Full ties receive no
fastest/fewest caption. Below 640px Compare stacks by fact and entrant, with no
sideways scrolling. Usage refusals appear only in queue Actions. Compact usage
cells and folded metadata keep the queue and graph table within a docked pane.
Agents totals select the latest snapshot per packet/fork/source, across live claims,
entrants, and retained reviews, before summing. The local-day window selects reports
received today. A cumulative report can include earlier consumption, so the UI
explicitly describes these as cumulative reports received today, not consumption
today. Daily deltas and distinct reclaimed-claim lifetimes need more core history.
The fixture harness provides `usage-race`, `usage-over`, `usage-unchecked`,
`usage-both`, `usage-legacy`, `usage-unbudgeted`, and `usage-reordered`, with `role=agent` for the read-only host. Fixture
checks and browser captures are synthetic. See
[test/captures/fh-062/README.md](test/captures/fh-062/README.md).
Accepted FH-055 main is normally merged at `3d87cdca649740984524882a00408cb9780ae327`
(second parent `ea577a1d96fc52528bd2854d58ee2a29d9ccf679`). Its glyphs, claim order,
neutral unreached rings, centered hurdles, badges and silks remain in the shared track.
Accepted FH-055b main `f924b7a40fa94bc457677dfa3898c940b1676e37` is normally
merged at `97cf0650f6560a0d5d814a85c98d27cc6a342d53`, with parents
`90cabe62afb723d0645e62d3c97976e3f8030fc5` and that accepted main commit.
Accepted FH-057b main `c3dec6e43994b49007b6ad06ab55d13c61607955` is normally
merged onto the local FH-062 head `edb9903c9829a4871b0814fc5bb978a2395ed1d4`.
Its table clipping, focus behavior, compact visible event cells, and full accessible
event labels compose with the usage tooltip and Tokens column.
Accepted FH-057c main `05efc0a346a7e91d4f4e56b52eb4266f12475f5b` is normally
merged onto the saved local FH-062 head `594b51b5d4d914b51dac2e0cc01b30f92e8da57b`.
The graph omits hour labels without room at plot edges and retains every hour grid line.
Accepted FH-057d main `1749a394caa4f78b7fbf6a1645659e89e78e9b99` is normally
merged onto saved FH-062 head `761b777bf2c170fe0b6e6674d330458d157d2111`.
Failed checks retain danger dots, timed-out/infrastructure receipts warning dots,
and review/running receipts info dots, alongside the usage tooltip and Tokens column.
Accepted FH-060 main `f1910e6f571ed7359d30db37d3ddca86f9236803`, including
FH-049 collision UI and FH-050 archive contract, is normally merged onto saved
FH-062 head `d8befb64e9232c67a78044447aa8ec81445555c8`. Collision gates retain
owner/agent explanations; resolving a collision leaves the current entrant budget
gate in force. All eight design fixes remain. Full root tests/lint, fresh-clone/D5 and host workerd acceptance are
serialized operator checks. Saddler PASS and owner review remain open. This local
candidate has no live Cloudflare, deployment, or owner-acceptance evidence.
## First-run guidance
Empty owner views show a placeholder owner CLI command using the existing
clipboard control. Copying it performs no API request. The CLI reads the owner
credential from the owner's private environment; the dashboard never reads,
stores or inserts that credential or actual endpoint/config values into commands.
Agent hosts show the same guidance without commands or Copy controls. Empty
reviews offer owners a Go to Work button using existing tab navigation.
Overview shows one empty state: enroll agents, create packets, then wait for an
agent to claim work. Existing forks, promotion history and race entries preserve
the graph even when no active claim remains. Archive-only Work and reviews with
retained history keep their existing empty-queue behavior. Race packets remain in
Work. The additive Races tab lists all recorded races and has its own first-run
guidance. Missing race bookmarks and real races waiting for entrants retain their
existing messages.
The labeled loopback scenarios are `empty-projects`, `empty-agents`,
`empty-packets`, `empty-forks`, `empty-reviews` and `empty-races`. Select tabs with
`#overview`, `#work`, `#agents` or `#review`; `role=agent` exercises hidden owner
commands. `empty-projects` supplies no configured projects and makes no project
read. These are synthetic fixtures, not evidence from a new deployed instance.
## Races tab (FH-081)
The last tab, `#races`, reads the existing state snapshot without new requests or
owner write controls. Its badge counts open races with entrants. Rows retain lane
order and enrollment identities, separate the judge's advice from the winner,
and link to the existing race view. Opening from this list changes that view's
back link to “← Races”; opening from Work retains “← Work”. Judge and start metadata
fold beneath the title at viewport widths up to 899px or pane widths up to 819px.
`fixtures.html?scenario=races-list#races` supplies labeled synthetic open,
zero-entrant, finished, photo-finish, archived, judged and awaiting-advice races.
`scenario=races-empty` covers first-run guidance; `role=agent` hides the owner CLI
command. Additive fixture tests cover ordering, separate winner/advice semantics,
tab naming, read-only requests, refreshes and return navigation. Browser captures
and scroll measurements at 1440/1024/900/800, dashboard tests/types/lint/build,
fresh-clone review and Saddler approval are operator acceptance gates.