docs(workbench): fold execution-shape review into Wave R plan + briefs

- R0a honesty fix unconditional (j/k, /, Enter rows out; R0d restores)
- minimal route conformance test front-loaded R1 -> R0a
- Kbd front-loaded R1 -> R0d; KeyboardHelp becomes registry-driven
  (structurally impossible to advertise unimplemented shortcuts)
- merge order pinned: strict train, R0a first; parallel dispatch retained
- declined with reasons recorded: full echelon-1 serialization, folding
  Playwright into R0a
This commit is contained in:
Shay 2026-06-12 10:22:28 -07:00
parent 8e3995ff10
commit 4d70841386
2 changed files with 73 additions and 34 deletions

View file

@ -7,11 +7,17 @@ governing spec; this pack is the per-operator cut).
## Dispatch order
```
Echelon 1 (parallel): R0a ∥ R0b ∥ R0c
Echelon 2: R0d (after R0c merges)
Echelon 3: R1 (after all R0 merges)
Echelon 1 (parallel dispatch): R0a ∥ R0b ∥ R0c
Echelon 2: R0d (after R0c merges)
Echelon 3: R1 (after all R0 merges)
```
**Merge order is a strict train even though dispatch is parallel:**
R0a merges FIRST (smallest PR; carries the honesty fix; lands the CI lane
that then verifies R0b/R0c). R0b and R0c merge next, either order, each
rebased on merged main before push if R0a landed meanwhile. R0d after R0c.
Never combine R0 briefs into one PR.
Brief 5 (Trace route, `wave-1-evidence-spine-briefs-2026-06-12.md`) **stays
on hold** until R0 lands; it gets re-issued upgraded as the first R2 brief.
@ -80,9 +86,20 @@ workbench-ui at all** (`smoke.yml` / `full-pytest.yml` are Python-only).
- single job, `timeout-minutes: 15`: pnpm setup (frozen lockfile) →
`pnpm build``pnpm test`
- Node 20, pnpm via `corepack` or `pnpm/action-setup`
5. Honesty fix (skip if R0d already merged): remove the `j/k` and `/` rows
from `KeyboardHelp.tsx` and its test expectations — the overlay must not
advertise shortcuts that do not exist.
5. Honesty fix, **unconditional**: remove the `j/k`, `/`, and
`Enter — Open selected item` rows from `KeyboardHelp.tsx` and its test
expectations — the overlay must not advertise shortcuts that do not
exist. (`Enter` is real only inside the palette, which carries its own
hint; as a global list affordance it is false until R0d.) R0d restores
these rows as real, registry-backed entries.
6. Minimal route conformance test (front-loaded from R1):
`src/app/routeConformance.test.tsx`, parametrized over the implemented
routes (Chat, Proposals, Evals, Replay). For each route, under mocked
query states, assert: empty state renders a one-line absence statement +
a next action; error state renders what-failed + mutation status +
reproducer + retry-safety; loading state renders a specific label (never
"Thinking…"). Per ADR-0162 §6. Fix routes that fail; no expected-fail
lists (a test that tolerates violations is decoration).
### Verification before push
@ -247,9 +264,14 @@ ls src/app/evidenceAddress.ts || echo "STOP: R0c not merged"
6. Wire it for real: Proposals list and Replay artifact list adopt
`useListNavigation` + `SearchInput` (the `/` shortcut becomes real where
mounted).
7. `KeyboardHelp.tsx`: advertise exactly the shortcuts that now work — and
only those. (If R0a removed `j/k` and `/` rows, restore them — they are
real now.)
7. `Kbd` primitive (front-loaded from R1): a small token-only component for
rendering key chords; `KeyboardHelp` and palette shortcut hints adopt it.
8. `KeyboardHelp.tsx` becomes **registry-driven**: the overlay renders its
rows from the shortcut/command registry instead of a hand-maintained
array, so advertising an unimplemented shortcut is structurally
impossible. The rows R0a removed (`j/k`, `/`, `Enter`) return here as
real registry entries. Test: every row in the rendered overlay
corresponds to a registered, handled shortcut.
### Verification before push
@ -279,27 +301,24 @@ ls src/design/components/Panel || echo "STOP: R0d not merged"
### Deliverables
1. `Kbd` primitive; adopt in `KeyboardHelp` and palette shortcut hints.
2. Typography precision: `tabular-nums` on every metric cell
1. Typography precision: `tabular-nums` on every metric cell
(`MetadataTable` values, eval metrics, `turn_cost_ms`); type-scale audit
against ADR-0162 §2; `text-wrap: balance` on headings.
3. Selection-state unification: one tokenized treatment for selected vs
2. Selection-state unification: one tokenized treatment for selected vs
focused rows, applied across Proposals / Evals / Replay lists.
4. Hash display standard: 12-char truncation + copy + mono everywhere;
3. Hash display standard: 12-char truncation + copy + mono everywhere;
consolidate `src/app/chat/CopyableHash.tsx` into `DigestBadge` and delete
the duplicate (cleanup-as-you-find: imports, tests, preview entries).
5. `EvidenceChainRail` in `RightInspector`: the seven spine stages (intent →
4. `EvidenceChainRail` in `RightInspector`: the seven spine stages (intent →
subject → provenance → admissibility → replay → authority → action), each
rendered lit (evidence present) / dim (not applicable) / hollow (not
recorded). Status derives ONLY from fields the subject carries — never
inferred, never faked. "Not recorded" is an honest, visible state.
6. Empty-state glyphs: small static deterministic monochrome SVGs (inline,
5. Empty-state glyphs: small static deterministic monochrome SVGs (inline,
no asset fetches).
7. `Panel` adoption in Proposals + Evals routes (Chat/Replay opportunistic).
8. **Doctrine-as-tests:**
- Route conformance test parametrized over implemented routes: empty /
error / loading each render with next-action / reproducer /
specific-label content (ADR-0162 §6, executable).
6. `Panel` adoption in Proposals + Evals routes (Chat/Replay opportunistic).
7. **Doctrine-as-tests** (route conformance shipped in R0a; extend it to any
surface this PR touches, then add):
- Raw-hex scan: vitest node test walking `src/**` asserting no
hex/rgb literals outside `tokens.css`.
- Schema-drift gate: `scripts/dump-schemas.py` (read-only AST walk over

View file

@ -7,6 +7,14 @@ Refines: `wave-1-evidence-spine.md` — keeps its spine and Wave-1 deliverables
sequencing with the upgraded specs below.
Reviewed by: Claude Fable 5 (full re-audit of ADR-0160, ADR-0162, the shipped
`origin/main` code, and the Wave 1 plan), approved by Shay.
Execution-shape review (external, via Shay, 2026-06-12): R0 confirmed as a
strict PR train, never one PR; honesty fix made unconditional in R0a; route
conformance test front-loaded R1 → R0a; `Kbd` front-loaded R1 → R0d;
KeyboardHelp made registry-driven in R0d; merge order pinned R0a-first.
Declined with reasons: full serialization of echelon-1 dispatch (file
surfaces verified disjoint; parallel dispatch retained with serial merge),
and folding the Playwright lane into R0a (ADR-0162 acceptance 57 needs a
dedicated, parallel-safe brief).
## Why a revamp before Wave 2
@ -89,9 +97,18 @@ Trace is the first consumer of this substrate and building it twice is waste.
- [ ] New `.github/workflows/workbench-ui.yml`: path-filtered to
`workbench-ui/**`, runs `pnpm install --frozen-lockfile && pnpm build
&& pnpm test`, `timeout-minutes: 15`
- [ ] Remove the `j/k` and `/` rows from `KeyboardHelp.tsx` **iff** R0d has
not yet made them real at merge time (honesty gap closes from whichever
side lands first)
- [ ] Remove the `j/k`, `/`, and `Enter — Open selected item` rows from
`KeyboardHelp.tsx` **unconditionally** — the overlay must not advertise
shortcuts that do not exist. R0d restores them when they become real
(and makes the overlay registry-driven so this class of dishonesty is
structurally impossible)
- [ ] Minimal route conformance test (front-loaded from R1 on
execution-shape review): parametrized over the implemented routes
(Chat, Proposals, Evals, Replay), asserts empty / error / loading each
render with next-action / reproducer / specific-label content
(ADR-0162 §6). Minimal means existing routes only — the harness is
the contract every later route must pass; fix what fails, no
expected-fail lists
### R0b — Playwright smoke lane (pays ADR-0162 acceptance debt 5/6/7)
@ -144,15 +161,17 @@ Trace is the first consumer of this substrate and building it twice is waste.
- [ ] Wire `useListNavigation` + `SearchInput` into the Proposals list and
Replay artifact list now (at least two real consumers; `/` becomes real
where a `SearchInput` is mounted)
- [ ] `KeyboardHelp.tsx` updated to advertise exactly the shortcuts that are
now real — and only those
- [ ] `Kbd` primitive (front-loaded from R1 — KeyboardHelp and palette
shortcut hints consume it)
- [ ] `KeyboardHelp.tsx` becomes **registry-driven**: the overlay renders
from the shortcut/command registry instead of a hand-maintained list,
so advertising an unimplemented shortcut is structurally impossible.
The rows R0a removed return here as real, registry-backed entries
---
## Wave R1 — Design mastery pass (one PR, after R0)
- [ ] `Kbd` primitive (promoted from the deferred list — KeyboardHelp and
palette hints consume it)
- [ ] Typography precision: `tabular-nums` for every metric cell
(`MetadataTable` values, eval metrics, turn costs); type-scale audit;
`text-wrap: balance` on headings
@ -167,10 +186,7 @@ Trace is the first consumer of this substrate and building it twice is waste.
with no data renders "not recorded", never a guess
- [ ] Empty-state glyphs: small static deterministic monochrome SVGs
- [ ] `Panel` adoption in Proposals + Evals (Chat/Replay opportunistic)
- [ ] **Doctrine-as-tests:**
- [ ] Route conformance test, parametrized over implemented routes:
empty / error / loading all render with next-action / reproducer /
specific-label content (ADR-0162 §6 becomes executable)
- [ ] **Doctrine-as-tests** (route conformance moved to R0a; the rest here):
- [ ] Raw-hex scan test: no hex/rgb literals in `src/**` outside
`tokens.css`
- [ ] Schema-drift gate: `scripts/dump-schemas.py` (read-only AST walk
@ -241,10 +257,14 @@ R0b ─┼─(R0c)──→ R0d ──→ R1 ──→ R2 (Trace ∥ Runs ∥ Au
R0c ─┘
```
R0a / R0b / R0c are parallel-safe (disjoint files; trivial `package.json`
merge between R0b and R0d is sequenced away). R0d follows R0c (shares
R0a / R0b / R0c are parallel-safe to **dispatch** (disjoint files; trivial
`package.json` merge between R0b and R0d is sequenced away). **Merge order
is a strict train: R0a merges first** — it is the smallest PR, carries the
honesty fix, and lands the CI lane that then verifies R0b and R0c; R0b/R0c
merge next in either order; R0d follows R0c (shares
`evidenceContext`/`Shell`). R1 is one PR after all of R0. R2 routes are
mutually independent. R3 follows the R2 routes it draws data from.
mutually independent. R3 follows the R2 routes it draws data from — no
theater work starts before R0/R1 are merged and tested.
## Keyboard map after R0