From 8e3995ff109435f68b1a32a701a9e822b9917699 Mon Sep 17 00:00:00 2001 From: Shay Date: Fri, 12 Jun 2026 10:02:46 -0700 Subject: [PATCH 1/3] docs(workbench): Wave R mastery revamp plan + R0/R1 dispatch brief pack Re-audit of the shipped Wave 1 evidence spine found six compounding gaps (advertised-but-unimplemented shortcuts, no evidence addresses, doctrine without tests, deferred substrate, CRUD-shaped Wave 2, no frontend CI lane). Wave R fixes the substrate before Wave 2 routes are built on it: - R0a test-runner hardening + first frontend CI lane - R0b Playwright smoke lane (ADR-0162 acceptance 5-7 debt) - R0c evidence addresses (URL = subject, Cmd+Shift+C copy link) - R0d interaction substrate (list nav, virtualization, panel, resize, verbs) - R1 design mastery pass (chain rail, typography, doctrine-as-tests) - R2/R3 upgraded specs (deterministic DAG viewer, Replay Moment, wrong=0 ledger, Demo Theater) Brief 5 (Trace) dispatch holds until R0 lands; re-issued upgraded as first R2 brief. Wave 1 doc gains a supersession pointer. --- .../wave-R-mastery-briefs-2026-06-12.md | 326 ++++++++++++++++++ docs/workbench/wave-1-evidence-spine.md | 4 +- docs/workbench/wave-R-mastery-revamp.md | 292 ++++++++++++++++ 3 files changed, 621 insertions(+), 1 deletion(-) create mode 100644 docs/handoff/wave-R-mastery-briefs-2026-06-12.md create mode 100644 docs/workbench/wave-R-mastery-revamp.md diff --git a/docs/handoff/wave-R-mastery-briefs-2026-06-12.md b/docs/handoff/wave-R-mastery-briefs-2026-06-12.md new file mode 100644 index 00000000..67ed19dd --- /dev/null +++ b/docs/handoff/wave-R-mastery-briefs-2026-06-12.md @@ -0,0 +1,326 @@ +# Wave R Mastery Revamp — Dispatch Briefs (R0a–R0d, R1) + +Date: 2026-06-12 +Plan: `docs/workbench/wave-R-mastery-revamp.md` (read it first — it is the +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) +``` + +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. + +## Standing constraints (every brief) + +- Worktree per operator, always off fresh `origin/main` — never share a + working dir. +- The merge pipeline is automated; **pre-push verification is the gate.** + Run your brief's verification block before pushing. +- Token-only styling (no raw hex/rgb outside `tokens.css`); no color-only + encoding; motion only via `--motion-*` tokens; `prefers-reduced-motion` + collapses to instant. +- ADR-0162 no-go list applies verbatim (no thinking animations, no + glassmorphism, no toasts that auto-dismiss audit events, no icon-only + buttons). +- Surfaces stay distinct everywhere: `surface` ≠ `articulation_surface` ≠ + `walk_surface`. +- No mutation endpoints, no writes outside `workbench_data/` from workbench + code. +- Until R0a merges: full `pnpm test` may hang on teardown. Verify with + `pnpm build` + per-file `pnpm vitest run `; `pkill -9 -f vitest` + afterwards if needed. +- Commit format `(workbench): `; push with `-u`; open a + PR titled as given in the brief. + +--- + +## Brief R0a — Test-runner hardening + frontend CI lane + +**Suggested operator:** Codex +**Scope:** `workbench-ui/` test config + timer hygiene; one new GitHub +workflow. No product code changes beyond timer cleanup. + +### Worktree + +```bash +cd /Users/kaizenpro/Projects/core +git fetch origin +git worktree add ../core-wb-r0a origin/main -b feat/wb-r0a-test-hardening +cd ../core-wb-r0a/workbench-ui && pnpm install +``` + +### Context + +Full multi-file `pnpm test` hangs on worker teardown — tests pass, the +process never exits. Diagnosed 2026-06-12: live handles at teardown +(`Timestamp.tsx` `setInterval(..., 60_000)`, copy-feedback `setTimeout`s); +`vite.config.ts` has no `testTimeout`/`teardownTimeout`; switching pools does +not fix it; single files exit clean. Separately: **no CI workflow runs +workbench-ui at all** (`smoke.yml` / `full-pytest.yml` are Python-only). + +### Deliverables + +1. `vite.config.ts` test config: `testTimeout: 10_000`, + `hookTimeout: 10_000`, `teardownTimeout: 5_000`. Tune pool options only + if measurements justify it — caps first, knobs second. +2. Timer hygiene: audit every `setInterval`/`setTimeout`/`AbortController` + under `src/`; ensure each has a cleanup path on unmount. In tests that + mount timer-scheduling components (`Timestamp`, copy buttons), use + `vi.useFakeTimers()` with restoration in `afterEach`. +3. Prove it: full `pnpm test` runs to completion AND the process exits. + Record wall-clock in the PR body. +4. New `.github/workflows/workbench-ui.yml`: + - `on: pull_request` + `push: branches: [main]`, both with + `paths: ['workbench-ui/**']` + - 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. + +### Verification before push + +```bash +cd workbench-ui && pnpm build && time pnpm test # must EXIT, all green +git -C .. diff --stat origin/main # only intended files +``` + +PR title: `fix(workbench): test-runner teardown hardening + frontend CI lane` + +--- + +## Brief R0b — Playwright smoke lane + +**Suggested operator:** GPT5.5-Thinking +**Scope:** `workbench-ui/` e2e only. Pays ADR-0162 acceptance-criteria debt +(§ acceptance 5, 6, 7). No product code changes except test hooks +(`data-testid` additions are allowed, sparingly). + +### Worktree + +```bash +cd /Users/kaizenpro/Projects/core +git fetch origin +git worktree add ../core-wb-r0b origin/main -b feat/wb-r0b-playwright +cd ../core-wb-r0b/workbench-ui && pnpm install +``` + +### Deliverables + +1. `@playwright/test` devDependency (pinned), `playwright.config.ts` with + `webServer` serving the built app (`pnpm build && vite preview`), + chromium-only project (keep the lane lean). +2. `e2e/palette.spec.ts` — from each of the ten routes: `Meta+K` opens the + palette; selecting a navigation command reaches every route (URL + asserted). The app must be usable with the backend absent — routes render + their error/empty states, never a white screen (this doubles as an + offline-resilience assertion). +3. `e2e/reduced-motion.spec.ts` — with `reducedMotion: 'reduce'`, tokenized + motion collapses to instant (assert computed transition/animation + durations are `0s` on a drawer open and palette open). +4. `e2e/preview-offline.spec.ts` — `context.route('**', abort)` for + non-localhost requests; `/preview` still renders every primitive section + (fonts are self-hosted; nothing may fetch the network). +5. `pnpm test:e2e` script; add a **separate job** to + `.github/workflows/workbench-ui.yml` if R0a's workflow exists on your + base, else create the file with just your job (merge is trivial either + way) — `timeout-minutes: 15`, cache playwright browsers. + +### Verification before push + +```bash +cd workbench-ui && pnpm build && pnpm test:e2e # all green locally +``` + +PR title: `test(workbench): playwright smoke lane (ADR-0162 acceptance 5-7)` + +--- + +## Brief R0c — Evidence addresses (URL = subject) + +**Suggested operator:** Claude +**Scope:** React/TypeScript. The audit-native deep-linking substrate every +R2 route will consume. + +### Worktree + +```bash +cd /Users/kaizenpro/Projects/core +git fetch origin +git worktree add ../core-wb-r0c origin/main -b feat/wb-r0c-evidence-addresses +cd ../core-wb-r0c/workbench-ui && pnpm install +``` + +### Read first + +- `docs/workbench/wave-R-mastery-revamp.md` § R0c +- `workbench-ui/src/app/evidenceContext.tsx` (the `EvidenceSubject` union) +- `workbench-ui/src/app/App.tsx`, `useGlobalKeyboard.ts`, + `commandRegistry.ts` + +### Deliverables + +1. `workbench-ui/src/app/evidenceAddress.ts`: + - `subjectToUrl(subject: EvidenceSubject): string` — canonical path + (`/trace/42`, `/proposals/`, `/evals/`, `/replay/`) + plus `?inspect=` when the inspector is open on a different subject + - `urlToSubject(params, searchParams): { route: EvidenceSubject | null, + inspect: EvidenceSubject | null }` — total inverse; malformed input + returns `null`, never throws + - The codec speaks ALL subject kinds now, including ones whose routes are + still placeholders — the grammar is fixed once. +2. `App.tsx` route params: `/trace/:turnId?`, `/proposals/:proposalId?`, + `/evals/:laneId?`, `/replay/:artifactId?`. Placeholder routes keep flat + paths. +3. `EvidenceProvider` ↔ URL sync: `?inspect=` carries inspector subject + + open state; deep link restores it; subject changes update the URL + (`replace`, not `push` — selection churn must not pollute history). +4. Existing routes restore selection from their param on load and write it + on selection change: `ProposalsRoute`, `EvalsRoute`, `ReplayRoute`. +5. `Cmd+Shift+C` in `useGlobalKeyboard`: copies + `window.location.origin + subjectToUrl(current subject)`; no-op with no + subject; input-focus guard applies. Visible copy-confirmation must not + auto-dismiss audit context (a transient inline "Copied" on the inspector + header is fine; no toast). +6. Tests: codec round-trip for every subject kind (including malformed + inputs); deep-link restores Proposals selection (MemoryRouter + `initialEntries`); `?inspect=` restores inspector; URL updates use + `replace`. + +### Verification before push + +```bash +cd workbench-ui && pnpm build +pnpm vitest run src/app/evidenceAddress.test.ts src/app/evidenceContext.test.tsx src/app/proposals/ProposalsRoute.test.tsx +# (per-file runs until R0a lands; then plain `pnpm test`) +``` + +PR title: `feat(workbench): evidence addresses — deep-linkable subjects + inspector URL state` + +--- + +## Brief R0d — Interaction substrate (AFTER R0c merges) + +**Suggested operator:** Codex or GPT5.5-Thinking +**Scope:** React/TypeScript. List navigation, virtualization, panel chrome, +inspector resize, palette action verbs. + +### Worktree + +```bash +cd /Users/kaizenpro/Projects/core +git fetch origin +git worktree add ../core-wb-r0d origin/main -b feat/wb-r0d-interaction-substrate +cd ../core-wb-r0d/workbench-ui && pnpm install +ls src/app/evidenceAddress.ts || echo "STOP: R0c not merged" +``` + +### Deliverables + +1. `src/design/hooks/useListNavigation.ts`: `j`/`k`/`ArrowUp`/`ArrowDown`/ + `Home`/`End` move focus, `Enter` activates, input-focus guard (reuse the + `isInputFocused` pattern from `useGlobalKeyboard.ts`). Roving tabindex or + `aria-activedescendant` — focused row visibly distinct, screen-reader + coherent. Unit tested. +2. `src/design/components/VirtualizedList/`: wraps `@tanstack/react-virtual` + (add dep, pinned), composes `useListNavigation`, deterministic item keys, + stable scroll restoration. Tested (virtualization kicks in above the + threshold; keyboard nav still works across virtualized boundaries). +3. `src/design/components/Panel/`: header (title + toolbar slot) + body + chrome, token-only. This is the standard panel every R2 route composes. +4. Inspector resize in `Shell.tsx`: wire the existing `SplitPane` for the + inspector column; width persisted to localStorage **with guarded access** + (see commit `af8d4f75` for the precedent — storage can throw). +5. Palette action commands: `commandRegistry.ts` gains an action kind + (discriminated union: `navigate` | `action`); routes register/unregister + their own commands on mount (the deferred Wave-1 call-site pattern). + First verbs: "Copy evidence link" (calls R0c's address copy), "Toggle + inspector", and Evals registers "Run eval lane " (executes the + existing read-only `POST /evals/run` — ADR-0160-allowed lane, not a + mutation). +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.) + +### Verification before push + +```bash +cd workbench-ui && pnpm build && pnpm test # R0a is merged by now: full run must exit green +``` + +PR title: `feat(workbench): interaction substrate — list nav, virtualization, panel chrome, inspector resize, palette verbs` + +--- + +## Brief R1 — Design mastery pass (AFTER all R0 merges) + +**Suggested operator:** Claude +**Scope:** one PR, visual + doctrine-as-tests. No new routes, no new +endpoints. + +### Worktree + +```bash +cd /Users/kaizenpro/Projects/core +git fetch origin +git worktree add ../core-wb-r1 origin/main -b feat/wb-r1-design-mastery +cd ../core-wb-r1/workbench-ui && pnpm install +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 + (`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 + focused rows, applied across Proposals / Evals / Replay lists. +4. 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 → + 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, + 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). + - 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 + `workbench/schemas.py`, exact `scripts/dump-enums.py` pattern) → + `schema-snapshot.json` → vitest asserts `src/types/api.ts` covers every + dataclass field. Engine schema drift fails loudly at test time. + +### Verification before push + +```bash +cd workbench-ui && pnpm build && pnpm test && pnpm test:e2e +cd .. && uv run python scripts/dump-schemas.py > /dev/null # script runs clean +``` + +PR title: `feat(workbench): design mastery pass — chain rail, typography, doctrine-as-tests` + +--- + +## After this pack + +R2 briefs (Trace upgraded, Runs, Audit, Packs, Vault, Settings — parallel) +and R3 briefs (Replay Moment, deterministic DAG viewer, Demo Theater, +wrong=0 ledger) are authored once R0/R1 are on main, against the real +substrate. Specs and exclusions: `docs/workbench/wave-R-mastery-revamp.md`. diff --git a/docs/workbench/wave-1-evidence-spine.md b/docs/workbench/wave-1-evidence-spine.md index 913ddacb..e2d8e183 100644 --- a/docs/workbench/wave-1-evidence-spine.md +++ b/docs/workbench/wave-1-evidence-spine.md @@ -1,6 +1,8 @@ # Wave 1 — Evidence Spine -Status: approved plan +Status: approved plan (Wave 1 shipped: PRs #702, #703, #704, #706); +Wave 2 / Wave 3 sequencing below is superseded by +[wave-R-mastery-revamp.md](./wave-R-mastery-revamp.md) Date: 2026-06-12 Supersedes: implementation-plan.md phases W-032+ Peer-reviewed by: Codex (architectural path selection), GPT5.5-Thinking (trace diff --git a/docs/workbench/wave-R-mastery-revamp.md b/docs/workbench/wave-R-mastery-revamp.md new file mode 100644 index 00000000..c6b3f99c --- /dev/null +++ b/docs/workbench/wave-R-mastery-revamp.md @@ -0,0 +1,292 @@ +# Wave R — Mastery Revamp: Determinism Made Felt + +Status: approved plan +Date: 2026-06-12 +Refines: `wave-1-evidence-spine.md` — keeps its spine and Wave-1 deliverables +(all shipped: PRs #702, #703, #704, #706); supersedes its Wave 2 / Wave 3 +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. + +## Why a revamp before Wave 2 + +Wave 1 shipped the correct architecture — the evidence spine is real, tested, +and on main. But a fresh audit of the shipped code against the doctrine found +six gaps that compound if Wave 2 routes are built on top of them: + +1. **The UI advertises capabilities that don't exist.** `KeyboardHelp.tsx` + documents `j/k` list navigation and the Wave 1 keyboard map says it + shipped — no list in the app implements it. `SearchInput`'s `/` binding is + mounted nowhere. An audit-native product whose help overlay makes false + claims is violating its own first pillar. +2. **No evidence addresses.** Every route is a flat path (zero URL params in + `App.tsx`). A selected turn, proposal, or eval result cannot be linked, + bookmarked, or cited. For a product whose identity is "evidence you can + hand to someone," the URL is the most important audit artifact — and it + does not exist. +3. **Doctrine lives in prose, not tests.** ADR-0162 acceptance criteria 5–7 + require Playwright (reduced-motion collapse, palette-from-every-route, + offline preview). No Playwright exists. The empty/error/loading contract + has no conformance test. Per CLAUDE.md's schema-defined proof obligations: + a claim without a test that can meaningfully fail is decoration. +4. **The deferred substrate is exactly what compounds.** Inspector resize, + per-route `setSubject` call sites, palette action-commands, shared list + navigation — all deferred "until a route needs them." Six Wave-2 routes + need all of them simultaneously. Deferring means rework ×6. +5. **Wave 2/3 were spec'd as CRUD.** "List with badges + detail panel" six + times. Nothing visualizes the structures that make CORE distinct: + proposition chains, proof-carrying promotion DAGs, entailment traces, the + wrong=0 ledger. +6. **No frontend CI lane exists at all.** `smoke.yml` and `full-pytest.yml` + are Python-only. Frontend PRs merge with zero frontend CI verification, + and the local full `pnpm test` hangs on worker teardown (live timer + handles; diagnosed 2026-06-12). + +## The design thesis + +The blueprint asks for "Linear's calm + Raycast's speed + Instruments' +precision." That is the floor. The ceiling — what no other AI product can +copy honestly — is: + +> **Every other AI UI animates fake cognition. This one makes real +> determinism *felt*.** + +Mastery is not more motion or more chrome (the ADR-0162 no-go list stands +unchanged). Mastery is a small set of **signature interactions** that are +only possible *because* the engine is deterministic: + +| Signature interaction | What it is | Pillar it embodies | +|---|---|---| +| **The Evidence Address** | Every evidence subject (turn / proposal / artifact / eval result) has a canonical URL; inspector state serializes into it; `Cmd+Shift+C` copies it anywhere | Audit-native — a claim becomes a pasteable link | +| **The Evidence Chain Rail** | The spine's seven stages (intent → subject → provenance → admissibility → replay → authority → action) as a persistent compact rail in the inspector, each stage lit with its actual status | Calm default, infinite depth — structure, zero theater | +| **The Replay Moment** | Select any turn → Replay → side-by-side original/replay → two trace hashes resolve **equal**, leaf diff confirms zero divergence | Replay before persuasion — the honest wow | +| **Deterministic structure rendering** | Proposal chains / PCCP proof-promotion DAGs / entailment traces as layered SVG DAGs with deterministic layout (same input → same pixels) | All three — interactive where masterful, static where honest | +| **The wrong=0 ledger surface** | Evals render the correct/refused/wrong triplet as the canonical visualization; refusals first-class; failures-first ordering | Audit-native — the engine's epistemic honesty, visible | + +Deterministic layout is doctrine, not preference: force-directed layouts are +seeded-random, so the same evidence would render differently across sessions — +a literal replay violation. Layered longest-path layout with lexicographic +tie-breaking renders identically forever and is screenshot-diffable. + +--- + +## Wave R0 — Substrate hardening + +Blocks everything. Four briefs; R0a ∥ R0b ∥ R0c can run in parallel, R0d +lands after R0c. **Brief 5 (Trace route) dispatch HOLDS until R0 lands** — +Trace is the first consumer of this substrate and building it twice is waste. + +### R0a — Test-runner hardening + frontend CI lane + +- [ ] `vite.config.ts` test config gains `testTimeout`, `hookTimeout`, + `teardownTimeout` caps so a hung worker fails fast instead of hanging to + a CI wall +- [ ] Timer hygiene audit: every `setInterval`/`setTimeout` in + `workbench-ui/src/` has a cleanup path; component tests that mount + timer-scheduling components (`Timestamp`, copy-feedback buttons) use + fake timers +- [ ] Full multi-file `pnpm test` completes and **exits** locally +- [ ] 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) + +### R0b — Playwright smoke lane (pays ADR-0162 acceptance debt 5/6/7) + +- [ ] `@playwright/test` devDependency + `playwright.config.ts` with + `webServer` (vite preview build) +- [ ] e2e spec 1: `⌘K` opens the palette from every route; palette navigates + to every route (acceptance criterion 6) +- [ ] e2e spec 2: `prefers-reduced-motion: reduce` collapses tokenized motion + to instant (acceptance criterion 5) +- [ ] e2e spec 3: `/preview` renders every primitive with network access + blocked (acceptance criterion 7 — offline-safe, per operator + circumstances) +- [ ] `pnpm test:e2e` script; CI job added to `workbench-ui.yml` (separate + job so vitest results are not hostage to browser install) + +### R0c — Evidence addresses (URL = subject) + +- [ ] `workbench-ui/src/app/evidenceAddress.ts`: `subjectToUrl(subject)` / + `urlToSubject(params, search)` codec, round-trip property tested for + every `EvidenceSubject` kind +- [ ] Route params in `App.tsx`: `/trace/:turnId?`, `/proposals/:proposalId?`, + `/evals/:laneId?`, `/replay/:artifactId?` (placeholders keep flat paths + until their R2 route lands; the codec already speaks their grammar) +- [ ] `?inspect=` query param carries the inspector subject + open state; + `EvidenceProvider` syncs with the URL (deep link restores inspector) +- [ ] Existing routes (Proposals, Evals, Replay) read their param on load to + restore selection and write it on selection change +- [ ] `Cmd+Shift+C` global shortcut + palette command "Copy evidence link" + (registered but inert until R0d's action-command plumbing; ships the + shortcut, R0d ships the palette verb) +- [ ] Tests: codec round-trip; deep-link restores Proposals selection; + inspector state survives a simulated reload + +### R0d — Interaction substrate (lands after R0c) + +- [ ] `useListNavigation` hook: `j/k` + arrows + `Home`/`End` + `Enter` + + input-focus guard; roving-tabindex or `aria-activedescendant` pattern +- [ ] `VirtualizedList` primitive over `@tanstack/react-virtual`, + composing `useListNavigation`; deterministic keys +- [ ] `Panel` primitive: header / toolbar-slot / body chrome so routes stop + hand-rolling borders +- [ ] Inspector resize: wire the existing `SplitPane` into `Shell.tsx` for + the inspector column; width persisted (storage access guarded, per the + `af8d4f75` precedent) +- [ ] Palette action commands: `commandRegistry` gains an action kind; + per-route `register()` call sites (the deferred Wave-1 item); first + verbs: "Copy evidence link", "Toggle inspector", "Run eval lane …" + (registered by the Evals route; executes the existing read-only + `POST /evals/run` — an ADR-0160-allowed lane, not a mutation) +- [ ] 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 + +--- + +## 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 +- [ ] Selection-state unification: selected row vs focused row are visually + distinct and consistent across routes (tokens, not per-route CSS) +- [ ] Hash display standard: one rule (12-char truncation + copy + mono) + everywhere; consolidate `CopyableHash` (chat) with `DigestBadge` + (cleanup-as-you-find: remove the duplicate) +- [ ] `EvidenceChainRail` component in `RightInspector`: seven stages, each + lit (evidence present) / dim (not applicable) / hollow (not recorded). + Statuses derive only from fields the subject actually carries — a stage + 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) + - [ ] Raw-hex scan test: no hex/rgb literals in `src/**` outside + `tokens.css` + - [ ] Schema-drift gate: `scripts/dump-schemas.py` (read-only AST walk + of `workbench/schemas.py`, same pattern as `dump-enums.py`) → + snapshot → vitest asserts `types/api.ts` field coverage + +--- + +## Wave R2 — Projections (parallel briefs, written after R0/R1 land) + +Every route is the same triad — **list (virtualized, `j/k`) → detail (tabs) → +inspector (chain rail)** — with evidence addresses and a conformance test. +Briefs get authored against the landed substrate; the specs below are the +commitments. + +### Trace (Brief 5, upgraded — first dispatch of R2) + +Everything in the existing Brief 5, plus: URL param selection via R0c codec, +`useListNavigation` + `VirtualizedList` for the timeline, `Panel` chrome, +versor-condition strip (see journal note below), and the conformance test. + +### Runs / Audit / Packs / Vault / Settings + +- **Runs** — `GET /runs`, `GET /runs/{session_id}`; session list with + checkpoint badges; every turn cross-links to `/trace/:turnId`. +- **Audit** — `GET /audit/events`; vertical event timeline (new `Timeline` + primitive); mutation-boundary events visually weighted above routine events. +- **Packs** — `GET /packs`, `GET /packs/{pack_id}`; new `TreeView` primitive; + manifest checksum displayed with `DigestBadge` (verify affordance). +- **Vault** — `GET /vault/summary`, `GET /vault/entries`; epistemic-state + badges; exact-recall provenance (`cga_inner` evidence shown, never an + approximate score — runtime recall is exact by doctrine). +- **Settings** — localStorage prefs + read-only runtime config display; no + engine mutation (CLI-only, stated in the UI). + +**Journal addition (additive, append-only-safe):** an optional +`versor_condition` field on journal entries — **investigate first**: it ships +only if `ChatTurnResult`/runtime already exposes the value for the turn. The +workbench layer must never compute field algebra itself. Older entries render +"not recorded". + +--- + +## Wave R3 — Theater (the wow, honest) + +- **The Replay Moment** — implement the `/replay/{id}` backend (currently + 501): re-run a journaled turn deterministically, return original + replay + envelopes; frontend hero flow renders hash-to-hash equality and a leaf diff + with `≠` glyphs. Read-only; replay does not checkpoint. +- **Deterministic DAG viewer** — one component, hand-rolled layered layout + (longest-path layering, lexicographic tie-break, ~150 lines, golden-file + layout tests, no graph dependency). Pan/zoom/click-node-→-inspector. + Consumers: proposal chains, PCCP proof-promotion (8 scenarios from + `demos/proof_carrying_promotion`), entailment traces. +- **Demo Theater route** — `GET /demos`, `POST /demos/{id}/run`; scenario + results with evidence-class badges and "what this proves / what this does + not prove" honesty cards; proposer-was-wrong scenarios visually highlighted. +- **wrong=0 ledger view** — Evals renders the correct/refused/wrong triplet + as the primary visualization; refusal reasons inspectable; failures-first. + +--- + +## Dependency DAG + +``` +R0a ─┐ +R0b ─┼─(R0c)──→ R0d ──→ R1 ──→ R2 (Trace ∥ Runs ∥ Audit ∥ Packs ∥ Vault ∥ Settings) ──→ R3 +R0c ─┘ +``` + +R0a / R0b / R0c are parallel-safe (disjoint files; trivial `package.json` +merge between R0b and R0d is sequenced away). 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. + +## Keyboard map after R0 + +| Shortcut | Action | Status | +|---|---|---| +| `Cmd+K` | Command palette | shipped (Wave 1) | +| `Cmd+I` | Toggle inspector | shipped (Wave 1) | +| `Cmd+1`..`Cmd+0` | Navigate routes | shipped (Wave 1) | +| `Cmd+Shift+C` | Copy evidence link | R0c | +| `j/k` / arrows | List navigation | R0d (real, via `useListNavigation`) | +| `/` | Focus search | R0d (real where `SearchInput` is mounted) | +| `Enter` | Open selection | R0d | +| `Esc` | Close overlay / clear selection | shipped + R0d | +| `?` | Keyboard help | shipped (Wave 1; overlay lists only real shortcuts) | + +## Explicit exclusions (unchanged from ADR-0160/0162 + Wave 1) + +- No multi-user auth, cloud, SaaS, mobile +- No animated cognition theater, glassmorphism, neon, decorative motion +- No force-directed / nondeterministic graph layout — ever +- No corpus/pack mutation from the UI; mutation only through admitted + corridors (ADR-0172 pattern) +- No framework churn: React 18 + Vite + stdlib-HTTP backend stay +- New dependencies limited to: `@playwright/test` (dev), + `@tanstack/react-virtual` (runtime, ~3 kB) + +## Risks and standing decisions + +- **DAG layout**: hand-rolled over `dagre`/`elkjs` — determinism is doctrine, + graphs are small (<50 nodes), and the layout is a pure function with + golden-file tests. +- **`versor_condition` in the journal**: investigate-first; never computed in + the workbench layer. +- **Vitest hang**: if R0a's timer hygiene doesn't fully resolve teardown, + the timeout caps still convert "hangs 30 min" into "fails in seconds" — + the CI lane is safe either way. +- **Pipeline merge model**: the AssetOverflow pipeline auto-merges agent + branches; the reliable gate is pre-push verification. Every brief carries + its own verification commands for this reason. + +## Brief pack + +Dispatch briefs for R0a–R0d and R1 live in +`docs/handoff/wave-R-mastery-briefs-2026-06-12.md`. R2/R3 briefs are +authored after R0/R1 land, against the real substrate. From 4d7084138695836ce79cde356c6fe5399cc9f887 Mon Sep 17 00:00:00 2001 From: Shay Date: Fri, 12 Jun 2026 10:22:28 -0700 Subject: [PATCH 2/3] 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 --- .../wave-R-mastery-briefs-2026-06-12.md | 59 ++++++++++++------- docs/workbench/wave-R-mastery-revamp.md | 48 ++++++++++----- 2 files changed, 73 insertions(+), 34 deletions(-) diff --git a/docs/handoff/wave-R-mastery-briefs-2026-06-12.md b/docs/handoff/wave-R-mastery-briefs-2026-06-12.md index 67ed19dd..62626946 100644 --- a/docs/handoff/wave-R-mastery-briefs-2026-06-12.md +++ b/docs/handoff/wave-R-mastery-briefs-2026-06-12.md @@ -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 diff --git a/docs/workbench/wave-R-mastery-revamp.md b/docs/workbench/wave-R-mastery-revamp.md index c6b3f99c..7e8e5c10 100644 --- a/docs/workbench/wave-R-mastery-revamp.md +++ b/docs/workbench/wave-R-mastery-revamp.md @@ -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 5–7 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 From e77403f7734d1e9dd9e49ed2d3ff91e9a2186c9a Mon Sep 17 00:00:00 2001 From: Shay Date: Fri, 12 Jun 2026 10:29:29 -0700 Subject: [PATCH 3/3] docs(workbench): R0a CI path filter includes the workflow file itself --- docs/handoff/wave-R-mastery-briefs-2026-06-12.md | 3 ++- docs/workbench/wave-R-mastery-revamp.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/handoff/wave-R-mastery-briefs-2026-06-12.md b/docs/handoff/wave-R-mastery-briefs-2026-06-12.md index 62626946..88e4aef0 100644 --- a/docs/handoff/wave-R-mastery-briefs-2026-06-12.md +++ b/docs/handoff/wave-R-mastery-briefs-2026-06-12.md @@ -82,7 +82,8 @@ workbench-ui at all** (`smoke.yml` / `full-pytest.yml` are Python-only). Record wall-clock in the PR body. 4. New `.github/workflows/workbench-ui.yml`: - `on: pull_request` + `push: branches: [main]`, both with - `paths: ['workbench-ui/**']` + `paths: ['workbench-ui/**', '.github/workflows/workbench-ui.yml']` + (the workflow validates changes to itself) - single job, `timeout-minutes: 15`: pnpm setup (frozen lockfile) → `pnpm build` → `pnpm test` - Node 20, pnpm via `corepack` or `pnpm/action-setup` diff --git a/docs/workbench/wave-R-mastery-revamp.md b/docs/workbench/wave-R-mastery-revamp.md index 7e8e5c10..ae2d84fd 100644 --- a/docs/workbench/wave-R-mastery-revamp.md +++ b/docs/workbench/wave-R-mastery-revamp.md @@ -95,7 +95,8 @@ Trace is the first consumer of this substrate and building it twice is waste. fake timers - [ ] Full multi-file `pnpm test` completes and **exits** locally - [ ] New `.github/workflows/workbench-ui.yml`: path-filtered to - `workbench-ui/**`, runs `pnpm install --frozen-lockfile && pnpm build + `workbench-ui/**` **and the workflow file itself** (it validates its + own changes), runs `pnpm install --frozen-lockfile && pnpm build && pnpm test`, `timeout-minutes: 15` - [ ] Remove the `j/k`, `/`, and `Enter — Open selected item` rows from `KeyboardHelp.tsx` **unconditionally** — the overlay must not advertise