docs(handoff): commit Wave 1 + Wave R2 brief packs incl. part 2 (R2-S/Runs/Packs/Vault/Settings)

Production-line record: the already-executed Wave 1 and R2-B/R2-T/R2-A
briefs, plus the new part-2 pack — R2-S (EvidenceSubject extension +
Proposals wrinkles, gating), then Runs, Packs, Vault (fail-closed),
Settings. Strictly sequential merges; operators stop after checks green.
This commit is contained in:
Shay 2026-06-12 16:34:53 -07:00
parent 21718cffb2
commit 5182b495d5
4 changed files with 1425 additions and 0 deletions

View file

@ -0,0 +1,890 @@
# Wave 1 Evidence Spine — Dispatch Briefs
Date: 2026-06-12
Plan: `docs/workbench/wave-1-evidence-spine.md`
Merge target: `main`
## Dispatch DAG
```
Wave A (parallel — no dependencies between them):
Brief 1: Backend Journal + Trace API [GPT5.5] feat/wb-journal
Brief 2: Evidence Primitives [Claude] feat/wb-primitives
Brief 3: Mutation Doctrine Docs [GPT5.5] feat/wb-mutation-docs
Wave B (after Brief 2 merges):
Brief 4: Evidence Context + Inspector + [Claude] feat/wb-evidence-ui
Command Registry
Wave C (after Briefs 1 + 4 merge):
Brief 5: Trace Route [GPT5.5] feat/wb-trace-route
```
Merge order: `3 → (1 ∥ 2) → 4 → 5`
Brief 3 can merge anytime (docs-only). Briefs 1 and 2 are parallel and
independent. Brief 4 requires Brief 2 on main. Brief 5 requires Briefs 1
and 4 on main.
---
## Brief 1 — Backend Journal + Trace API
**Agent:** GPT5.5-Thinking
**Scope:** Python backend only. No frontend, no React, no TypeScript.
### Worktree setup
```bash
cd /Users/kaizenpro/Projects/core
git fetch origin
git worktree add ../core-wb-journal origin/main -b feat/wb-journal
cd ../core-wb-journal
```
### Brief
You are building the Workbench Turn Evidence Journal — a local, append-only
JSONL record of the `ChatTurnResult` envelope already returned by
`/chat/turn`. This is a read model, not runtime memory, not teaching memory,
and not a cognitive runtime fork.
**Why this exists:** The workbench backend currently does NOT attach a
telemetry sink to chat turns. `WorkbenchApi()` constructs with no sink,
`_run_chat_turn()` creates a bare `ChatRuntime()`, and
`serialize_turn_event` redacts content by default. The journal records the
exact evidence the operator already saw, so the Trace route (Brief 5) can
display it honestly.
**Read before writing code:**
- `docs/workbench/wave-1-evidence-spine.md` — section 1D (your spec)
- `docs/workbench/api-contract-v1.md` — existing contract, especially line 231
(three surfaces must stay separate)
- `workbench/api.py` — current `_run_chat_turn()` implementation
- `workbench/schemas.py` — existing schema patterns
- `workbench/server.py` — request routing
- `workbench/readers.py` — existing reader patterns
**Deliverables:**
1. `workbench/journal.py`
- `TurnJournalEntry` frozen dataclass: `turn_id` (int, sequential),
`timestamp` (ISO-8601 UTC), `trace_hash`, `prompt`, `surface`,
`articulation_surface`, `walk_surface`, `grounding_source`,
`epistemic_state`, `normative_clearance`, `verdicts` (identity/safety/
ethics), `refusal_emitted`, `hedge_injected`, `proposal_candidates`,
`turn_cost_ms`, `checkpoint_emitted`, `journal_digest` (SHA-256)
- `TurnJournalSummary` frozen dataclass: `turn_id`, `timestamp`,
`prompt_excerpt` (first 120 chars), `surface_excerpt` (first 120 chars),
`trace_hash`, `grounding_source`
- `TurnJournal` class: `__init__(journal_dir: Path)`, `append(entry)`,
`list_summaries(limit, offset)`, `get_entry(turn_id)`,
`next_turn_id() -> int`
- Journal path: `workbench_data/turn_journal.jsonl`
- JSONL is pure — every line valid JSON. No text headers.
- `journal_digest` = SHA-256 of the canonical JSON serialization of the
entry (excluding the digest field itself)
- Append-only: no update, no delete, no truncation
2. `workbench_data/README.md`
- Content-bearing warning: journal entries contain user prompts and engine
surfaces. This file stores evidence already returned to the local
operator. Not teaching memory. Not runtime memory.
3. Wire journal into `workbench/api.py`
- `WorkbenchApi.__init__` creates a `TurnJournal` instance
- After a successful `/chat/turn` response, append the result to the
journal before returning the HTTP response
- `turn_id` assigned by `TurnJournal.next_turn_id()`
- Add `turn_id` to the existing `ChatTurnResult` response so the frontend
can reference it
4. New API routes in `workbench/api.py`
- `GET /trace/turns?limit=50&offset=0` — returns journal summaries in
stable sequential order, inside `{ ok, generated_at, data: { items } }`
envelope
- `GET /trace/{turn_id}` — returns full journal entry for a turn, or 404
for unknown turn_id. Never return synthetic data.
- Update route dispatch in `do_GET` / `do_POST` handlers
5. Schema additions in `workbench/schemas.py`
- `TurnJournalEntrySchema` and `TurnJournalSummarySchema` for API
serialization (following existing patterns in the file)
6. Tests in `tests/test_workbench_journal.py`
- Append-only: entries never modified or deleted
- Stable ordering: sequential by turn_id
- Prompt/content size limits: max 4096 chars prompt (matching existing
`/chat/turn` validation)
- Path confinement: journal writes only to `workbench_data/`
- No journal writes to `teaching/`, `packs/`, `language_packs/data/`
- No NEW writes to `engine_state/` beyond existing chat checkpoint behavior
(ADR-0146/0150)
- Journal digest is deterministic for identical content
- Round-trip: `/chat/turn` response fields == `/trace/{turn_id}` fields
- Unknown turn_id returns 404
- Pagination: limit/offset produce correct slices
- Empty journal returns empty items list, not error
**Constraints:**
- Use Python stdlib only (no FastAPI, no pydantic, no new dependencies)
- Follow existing `workbench/` code patterns (frozen dataclasses, readers)
- All three surfaces stored as separate fields — never merge them
- `workbench_data/` should be gitignored (add to `.gitignore` if not present)
- Run `uv run python -m pytest tests/test_workbench_journal.py -q` green
- Run `uv run python -m pytest tests/test_workbench_api.py -q` still green
(existing tests must not break)
- Run `core test --suite smoke -q` green
### Work completed when
- [ ] `workbench/journal.py` exists with `TurnJournal`, `TurnJournalEntry`,
`TurnJournalSummary`
- [ ] `/chat/turn` appends to journal and returns `turn_id`
- [ ] `GET /trace/turns` returns paginated summaries from real journal
- [ ] `GET /trace/{turn_id}` returns full entry or 404
- [ ] `workbench_data/README.md` documents content-bearing nature
- [ ] `workbench_data/` is gitignored
- [ ] All tests green: journal tests + existing workbench tests + smoke
### Update the plan
In `docs/workbench/wave-1-evidence-spine.md`, check off every box under
section 1D (Backend, New API endpoints, Tests). Do NOT check off section 1E
(Trace Route frontend) — that is Brief 5.
### PR and merge
```bash
cd ../core-wb-journal
git add workbench/journal.py workbench_data/README.md tests/test_workbench_journal.py
git add workbench/api.py workbench/schemas.py workbench/server.py
git add .gitignore docs/workbench/wave-1-evidence-spine.md
# Include any other changed files
git commit -m "feat(workbench): turn evidence journal + trace API (Wave 1D)
Append-only JSONL journal records the exact ChatTurnResult envelope
returned by /chat/turn with stable turn_id, trace_hash, all three
surfaces, verdicts, and deterministic journal_digest.
GET /trace/turns and GET /trace/{turn_id} serve journal evidence
for the Trace route frontend (Brief 5).
Read model only — no teaching/pack/engine_state mutation."
```
Push and open PR against `main`:
```bash
git push -u origin feat/wb-journal
gh pr create --title "feat(workbench): turn evidence journal + trace API (Wave 1D)" \
--body "$(cat <<'EOF'
## Summary
- Adds `workbench/journal.py` — append-only JSONL turn evidence journal
- Wires journal into `/chat/turn` response path
- Adds `GET /trace/turns` and `GET /trace/{turn_id}` API endpoints
- Part of Wave 1 evidence spine (`docs/workbench/wave-1-evidence-spine.md`)
## Test plan
- [ ] `uv run python -m pytest tests/test_workbench_journal.py -q`
- [ ] `uv run python -m pytest tests/test_workbench_api.py -q`
- [ ] `core test --suite smoke -q`
- [ ] Manual: `core workbench api`, POST a chat turn, GET /trace/turns
EOF
)"
```
### Worktree cleanup
After merge:
```bash
cd /Users/kaizenpro/Projects/core
git worktree remove ../core-wb-journal
git branch -d feat/wb-journal
```
---
## Brief 2 — Evidence Primitives
**Agent:** Claude (Opus 4.6)
**Scope:** React/TypeScript frontend only. No Python, no backend changes.
### Worktree setup
```bash
cd /Users/kaizenpro/Projects/core
git fetch origin
git worktree add ../core-wb-primitives origin/main -b feat/wb-primitives
cd ../core-wb-primitives
```
### Brief
Build the six evidence primitives that Wave 1 needs. These are shared
design-system components used by the Trace route, Inspector, Command
Registry, and future Wave 2 routes. Build them with the same care as
building an oscilloscope panel — precise, token-driven, keyboard-navigable,
dark, calm.
**Read before writing code:**
- `docs/workbench/wave-1-evidence-spine.md` — section 1C
- `docs/workbench/design-system.md` — existing design system baseline
- `workbench-ui/src/design/tokens/tokens.css` — actual token names
- `workbench-ui/src/design/tokens/tokens.ts` — TypeScript token mirror
- `workbench-ui/src/design/components/` — existing component patterns
(badges, primitives, states, StableJsonViewer)
- `workbench-ui/src/preview/PreviewPage.tsx` — preview page structure
**Deliverables — six components under
`workbench-ui/src/design/components/`:**
1. `SplitPane/SplitPane.tsx`
- Resizable horizontal or vertical split via drag handle
- Props: `direction: 'horizontal' | 'vertical'`, `defaultSplit: number`
(percentage), `minSize: number` (px), `children: [ReactNode, ReactNode]`
- Drag handle uses `cursor: col-resize` or `row-resize`
- Persists split position in localStorage (keyed by caller-provided id)
- Respects `prefers-reduced-motion` for any resize animation
2. `TabBar/TabBar.tsx`
- Accessible tab bar with ARIA `role="tablist"`, `role="tab"`,
`role="tabpanel"` attributes
- Do NOT add `@radix-ui/react-tabs` as a dependency — implement with
native ARIA semantics
- Props: `tabs: Array<{ id: string, label: string }>`,
`activeTab: string`, `onTabChange: (id: string) => void`,
`children: ReactNode` (renders the active panel)
- Keyboard: `ArrowLeft`/`ArrowRight` moves focus between tabs,
`Enter`/`Space` activates, `Home`/`End` jumps to first/last
- Active tab gets `aria-selected="true"` + visual indicator via
`--color-border-accent`
3. `MetadataTable/MetadataTable.tsx`
- Key-value pair display for structured metadata
- Props: `rows: Array<{ key: string, value: ReactNode, copyable?: boolean }>`
- Keys rendered in `--font-size-xs` with `--color-text-secondary`
- Values rendered in `--font-size-sm` with `--color-text-primary`
- Copyable values show a copy icon on hover, click copies to clipboard
- Monospace font (`--font-mono`) for hash/digest/numeric values
- Stable row ordering (render in array order, no sorting)
4. `DigestBadge/DigestBadge.tsx`
- Copyable hash/digest display with truncation and verification indicator
- Props: `digest: string`, `algorithm?: string` (default 'sha256'),
`verified?: boolean | null`, `truncate?: number` (default 16)
- Display: `sha256:abc123de...` with monospace font
- Click copies full digest to clipboard, shows brief "Copied" feedback
- Verified indicator: green dot (true), red dot (false), gray dot (null)
- `aria-label` includes full digest for screen readers
5. `Timestamp/Timestamp.tsx`
- Relative + absolute time display
- Props: `iso: string` (ISO-8601 UTC), `format?: 'relative' | 'absolute'
| 'both'` (default 'both')
- Relative: "2m ago", "3h ago", "yesterday" (updates on 60s interval)
- Absolute: formatted in PST/PDT (America/Los_Angeles) per user preference
- Hover tooltip shows the other format (if relative shown, tooltip shows
absolute, and vice versa)
- `<time datetime="...">` element for semantic HTML
6. `SearchInput/SearchInput.tsx`
- Filtered search input with keyboard shortcut binding
- Props: `placeholder: string`, `value: string`,
`onChange: (value: string) => void`, `shortcut?: string` (default '/')
- Global keyboard listener: pressing the shortcut key focuses the input
(unless already in an input/textarea)
- Clear button (x) when value is non-empty
- `--font-size-sm`, border uses `--color-border-default`, focus ring via
`--color-focus-ring`
- Debounced onChange (150ms) for performance
**For every component:**
- Built with design tokens only — no raw hex/rgb/hardcoded colors
- Motion via `--motion-duration-*` and `--motion-ease-*` tokens
- `prefers-reduced-motion: reduce` collapses motion to instant
- `:focus-visible` ring via `--color-focus-ring`
- Unit test in sibling `*.test.tsx` file
- Added to `PreviewPage.tsx` with all meaningful states rendered
**Constraints:**
- No new npm dependencies unless explicitly justified (TabBar is native ARIA,
not Radix Tabs)
- TypeScript strict mode
- Follow existing component patterns in `design/components/`
- `pnpm build` green, `pnpm test` green
### Work completed when
- [ ] All six components exist under `design/components/`
- [ ] Each has a sibling test file, all passing
- [ ] PreviewPage renders all six with representative states
- [ ] `pnpm build` and `pnpm test` green
- [ ] No new npm dependencies added (unless justified and lockfile updated)
### Update the plan
In `docs/workbench/wave-1-evidence-spine.md`, check off every box under
section 1C. Check off the "Renders in PreviewPage" and "Unit test" boxes.
### PR and merge
```bash
cd ../core-wb-primitives
git add workbench-ui/src/design/components/SplitPane/
git add workbench-ui/src/design/components/TabBar/
git add workbench-ui/src/design/components/MetadataTable/
git add workbench-ui/src/design/components/DigestBadge/
git add workbench-ui/src/design/components/Timestamp/
git add workbench-ui/src/design/components/SearchInput/
git add workbench-ui/src/preview/PreviewPage.tsx
git add docs/workbench/wave-1-evidence-spine.md
git commit -m "feat(workbench): six evidence primitives (Wave 1C)
SplitPane, TabBar, MetadataTable, DigestBadge, Timestamp, SearchInput.
All token-driven, keyboard-navigable, ARIA-accessible, dark theme,
motion-respectful. No new dependencies."
```
Push and open PR:
```bash
git push -u origin feat/wb-primitives
gh pr create --title "feat(workbench): six evidence primitives (Wave 1C)" \
--body "$(cat <<'EOF'
## Summary
- Six shared design-system components for the evidence spine
- All built with existing design tokens, no new dependencies
- PreviewPage updated with all component states
- Part of Wave 1 (`docs/workbench/wave-1-evidence-spine.md`)
## Test plan
- [ ] `cd workbench-ui && pnpm test`
- [ ] `cd workbench-ui && pnpm build`
- [ ] Visual: open `/preview`, verify all six components render correctly
EOF
)"
```
**Merge before Brief 4 starts.** Brief 4 imports these components.
### Worktree cleanup
After merge:
```bash
cd /Users/kaizenpro/Projects/core
git worktree remove ../core-wb-primitives
git branch -d feat/wb-primitives
```
---
## Brief 3 — Mutation Doctrine Docs
**Agent:** GPT5.5-Thinking
**Scope:** Documentation only. No code changes.
### Worktree setup
```bash
cd /Users/kaizenpro/Projects/core
git fetch origin
git worktree add ../core-wb-mutation-docs origin/main -b feat/wb-mutation-docs
cd ../core-wb-mutation-docs
```
### Brief
Update the workbench planning docs to reconcile mutation doctrine with
reality. The current docs say "no accept/reject buttons" and "no mutation
in v1," but math proposal ratify/reject/defer already exists in
`workbench/api.py` (lines 112+) and the UI has a gated ratification
corridor in `RatificationCommandPanel.tsx`.
The honest rule is not "no mutation ever." It is: no mutation without an
admitted corridor, explicit preconditions, telemetry, and replay evidence.
**Read before writing:**
- `docs/workbench/wave-1-evidence-spine.md` — section 1F
- `docs/workbench/implementation-plan.md` — current mutation language
- `docs/workbench/acceptance-gates.md` — current forbidden/required lists
- `workbench/api.py` — existing ratify/reject/defer routes (lines 112+)
- `workbench-ui/src/app/proposals/RatificationCommandPanel.tsx` — existing
UI with precondition gates
**Deliverables:**
1. Update `docs/workbench/implementation-plan.md`
- Replace blanket "no mutation" language with the admitted corridor rule
- Document existing math ratification as the first admitted corridor
- Keep the list of what remains forbidden (corpus mutation, pack mutation,
workflow dispatch, arbitrary file writes)
- Add a section: "Mutation doctrine" defining the four requirements:
(1) ADR-governed path, (2) visible preconditions, (3) auditable
telemetry, (4) replay evidence before action
2. Update `docs/workbench/acceptance-gates.md`
- W-029 gates: replace "Forbidden: accept button / reject button" with
"Mutation only through admitted corridors (see Mutation doctrine)"
- Keep the red flags section but reframe: the red flag is not "UI can
accept proposals" — it is "UI can accept proposals WITHOUT admitted
corridor, preconditions, telemetry, or replay evidence"
- Update V1 release gate #8: "verify from UI copy and docs that no
UNADMITTED mutation path exists" (not "no mutation path")
3. Check off section 1F in `docs/workbench/wave-1-evidence-spine.md`
**Constraints:**
- Do not change any code — docs only
- Do not weaken security boundaries — the list of forbidden things (corpus
mutation, pack mutation, workflow dispatch, etc.) stays forbidden
- The correction is precise: math ratification IS admitted; everything else
remains forbidden until a future ADR admits it
- Keep existing document structure; amend sections in place
### Work completed when
- [ ] `implementation-plan.md` has a Mutation doctrine section with the
four-requirement rule
- [ ] `acceptance-gates.md` W-029 gates updated to match reality
- [ ] `acceptance-gates.md` red flags reframed around unadmitted mutation
- [ ] `wave-1-evidence-spine.md` section 1F boxes checked
- [ ] No code changes in the PR
### PR and merge
```bash
cd ../core-wb-mutation-docs
git add docs/workbench/implementation-plan.md
git add docs/workbench/acceptance-gates.md
git add docs/workbench/wave-1-evidence-spine.md
git commit -m "docs(workbench): reconcile mutation doctrine with admitted corridors (Wave 1F)
Replace blanket 'no mutation' language with the honest rule:
no mutation without admitted ADR corridor, visible preconditions,
auditable telemetry, and replay evidence.
Math ratification is the first admitted corridor. Corpus/pack/workflow
mutation remains forbidden."
```
Push and open PR:
```bash
git push -u origin feat/wb-mutation-docs
gh pr create --title "docs(workbench): reconcile mutation doctrine (Wave 1F)" \
--body "$(cat <<'EOF'
## Summary
- Updates implementation-plan.md and acceptance-gates.md
- Replaces blanket "no mutation" with admitted corridor doctrine
- Documents math ratification as the first admitted example
- Docs only — no code changes
## Test plan
- [ ] Read the diff — verify forbidden list unchanged, only language corrected
EOF
)"
```
Can merge anytime — no code dependencies.
### Worktree cleanup
After merge:
```bash
cd /Users/kaizenpro/Projects/core
git worktree remove ../core-wb-mutation-docs
git branch -d feat/wb-mutation-docs
```
---
## Brief 4 — Evidence Context + Inspector + Command Registry
**Agent:** Claude (Opus 4.6)
**Scope:** React/TypeScript frontend only. No Python, no backend changes.
**Prerequisite:** Brief 2 merged to main.
### Worktree setup
```bash
cd /Users/kaizenpro/Projects/core
git fetch origin
git worktree add ../core-wb-evidence-ui origin/main -b feat/wb-evidence-ui
cd ../core-wb-evidence-ui
```
Verify Brief 2 is on main before starting:
```bash
ls workbench-ui/src/design/components/SplitPane/SplitPane.tsx || echo "STOP: Brief 2 not merged yet"
```
### Brief
Build the evidence spine's UI architecture — the shared context that makes
every route a projection of one evidence manifold, plus the two surfaces
that consume it (Inspector and Command Palette).
**Read before writing code:**
- `docs/workbench/wave-1-evidence-spine.md` — sections 1A, 1B
- `workbench-ui/src/app/Shell.tsx` — current shell layout (hardcodes
collapsed inspector)
- `workbench-ui/src/app/RightInspector.tsx` — currently returns null
- `workbench-ui/src/design/components/primitives/CommandPalette.tsx`
currently hardcoded list
- `workbench-ui/src/app/LeftNav.tsx` — route list (for command registration)
- `workbench-ui/src/types/api.ts` — existing API types
- `workbench-ui/src/design/components/` — primitives from Brief 2
**Deliverables:**
1. `workbench-ui/src/app/evidenceContext.tsx`
- `EvidenceSubject` union type:
```
{ kind: 'turn', turnId: number, data: TurnJournalEntry }
| { kind: 'proposal', proposalId: string, data: ProposalDetail }
| { kind: 'artifact', artifactId: string, data: ArtifactDetail }
| { kind: 'eval_result', lane: string, data: EvalRunResult }
| { kind: 'none' }
```
- `EvidenceContext` React context with `subject`, `setSubject`,
`clearSubject`
- `EvidenceProvider` component wrapping the Shell
- `useEvidenceSubject()` hook for consumers
2. Rewrite `workbench-ui/src/app/RightInspector.tsx`
- Consumes `useEvidenceSubject()`
- Collapsed by default (calm default). Toggle via `Cmd+I` or explicit
"inspect" action
- When open, renders the appropriate evidence projection based on
`subject.kind`:
- `turn`: surfaces, grounding, verdicts, trace hash (compact view)
- `proposal`: state, source, replay evidence, suggested CLI
- `artifact`: metadata, digest, content preview
- `eval_result`: pass/fail, metrics, failure detail
- `none`: empty state with hint text
- Resizable width via SplitPane (from Brief 2)
- Stays open across route transitions (state in context, not route)
- Uses `MetadataTable`, `DigestBadge`, `Timestamp` from Brief 2
3. Rewrite `workbench-ui/src/app/Shell.tsx`
- Remove hardcoded `collapsed={true}`
- Wrap content in `EvidenceProvider`
- Inspector panel controlled by evidence context + user toggle
4. Rewrite `workbench-ui/src/design/components/primitives/CommandPalette.tsx`
- `CommandRegistry` — registration-based command system:
- `useCommandRegistry()` hook returns `register(commands)` and
`unregister(id)`
- Each route registers its commands on mount, unregisters on unmount
- Command shape: `{ id, label, section, shortcut?, action: () => void }`
- Built-in commands: navigate to each route (Chat, Trace, Replay,
Proposals, Evals, Runs, Packs, Vault, Audit, Settings)
- Fuzzy search over command labels
- Recent items section: last 10 evidence subjects visited (stored in
localStorage)
- `Cmd+K` opens, type-ahead filters, arrow keys navigate, `Enter`
executes, `Esc` closes
- Uses `SearchInput` from Brief 2
5. Global keyboard listener (in Shell or dedicated hook)
- `Cmd+K` — command palette
- `Cmd+I` — toggle inspector
- `Cmd+1` through `Cmd+0` — navigate to routes 1-10
- `?` — show keyboard shortcut overlay (simple modal listing shortcuts)
- `Esc` — close topmost overlay (palette > inspector > shortcut help)
- Do not capture when focus is in an input/textarea
6. `StatusFooter.tsx` enrichment
- Add API round-trip latency indicator (measure from last query)
- Add turn count from journal (if endpoint available — otherwise defer)
- Add proposal queue count badge (from existing `/proposals` data)
7. Tests
- Evidence context: set subject, read subject, clear subject, persists
across simulated route change
- Inspector: opens on Cmd+I, closes on Cmd+I, renders correct projection
per subject kind, stays open across route change
- Command palette: registers route commands, fuzzy filters, executes
navigation
- Keyboard: Cmd+K opens palette, Esc closes, Cmd+I toggles inspector
**Constraints:**
- No new npm dependencies
- TypeScript strict mode
- Do not modify backend or Python code
- `pnpm build` green, `pnpm test` green
### Work completed when
- [ ] Evidence context provider wraps the shell
- [ ] Inspector opens/closes with Cmd+I, shows contextual evidence
- [ ] Command palette has route-registered commands with fuzzy search
- [ ] Global keyboard shortcuts work (Cmd+K, Cmd+I, Cmd+1-0, ?, Esc)
- [ ] All tests pass, build passes
### Update the plan
In `docs/workbench/wave-1-evidence-spine.md`, check off all boxes under
sections 1A and 1B.
### PR and merge
```bash
cd ../core-wb-evidence-ui
git add workbench-ui/src/app/evidenceContext.tsx
git add workbench-ui/src/app/RightInspector.tsx
git add workbench-ui/src/app/Shell.tsx
git add workbench-ui/src/design/components/primitives/CommandPalette.tsx
git add workbench-ui/src/app/StatusFooter.tsx
# Add test files and any other changed files
git add docs/workbench/wave-1-evidence-spine.md
git commit -m "feat(workbench): evidence context + inspector + command registry (Wave 1A+1B)
Evidence context provider makes every route a projection of one
evidence manifold. RightInspector shows contextual evidence per
selected subject. CommandPalette is route-registered with fuzzy
search. Global keyboard shortcuts for power-user workflow."
```
Push and open PR:
```bash
git push -u origin feat/wb-evidence-ui
gh pr create --title "feat(workbench): evidence context + inspector + command registry (Wave 1A+1B)" \
--body "$(cat <<'EOF'
## Summary
- Shared evidence-subject context across all routes
- RightInspector: contextual evidence drawer (Cmd+I toggle)
- CommandPalette: route-registered, fuzzy-searchable (Cmd+K)
- Global keyboard shortcuts (Cmd+1-0, ?, Esc)
- Part of Wave 1 evidence spine
## Test plan
- [ ] `cd workbench-ui && pnpm test`
- [ ] `cd workbench-ui && pnpm build`
- [ ] Manual: open workbench, Cmd+K opens palette, navigate to routes,
Cmd+I opens inspector
EOF
)"
```
**Merge before Brief 5 starts.**
### Worktree cleanup
After merge:
```bash
cd /Users/kaizenpro/Projects/core
git worktree remove ../core-wb-evidence-ui
git branch -d feat/wb-evidence-ui
```
---
## Brief 5 — Trace Route
**Agent:** GPT5.5-Thinking
**Scope:** React/TypeScript frontend. Consumes the backend from Brief 1.
**Prerequisites:** Briefs 1, 2, and 4 all merged to main.
### Worktree setup
```bash
cd /Users/kaizenpro/Projects/core
git fetch origin
git worktree add ../core-wb-trace-route origin/main -b feat/wb-trace-route
cd ../core-wb-trace-route
```
Verify prerequisites:
```bash
ls workbench/journal.py || echo "STOP: Brief 1 not merged"
ls workbench-ui/src/design/components/SplitPane/SplitPane.tsx || echo "STOP: Brief 2 not merged"
ls workbench-ui/src/app/evidenceContext.tsx || echo "STOP: Brief 4 not merged"
```
### Brief
Build the Trace route — the canonical proof of CORE's surface-separation
contract and the deepest inspection surface in the workbench. This route
makes "the deeper you go, the more transparent it becomes" literally true.
**Read before writing code:**
- `docs/workbench/wave-1-evidence-spine.md` — section 1E (your spec)
- `docs/workbench/api-contract-v1.md` — line 231 (surface separation is
sacred)
- `workbench-ui/src/app/evidenceContext.tsx` — evidence subject context
(from Brief 4)
- `workbench-ui/src/api/client.ts` — existing API client patterns
- `workbench-ui/src/api/queries.ts` — existing React Query hooks
- `workbench-ui/src/design/components/` — all primitives (from Brief 2)
- `workbench-ui/src/app/chat/TraceDrawer.tsx` — existing chat trace drawer
(reference, not to be replaced — the Trace route is the full-page version)
**Deliverables:**
1. TypeScript types in `workbench-ui/src/types/api.ts`
- `TurnJournalEntry` interface (mirrors Python `TurnJournalEntry`)
- `TurnJournalSummary` interface (mirrors Python `TurnJournalSummary`)
2. API client in `workbench-ui/src/api/client.ts`
- `fetchTraceTurns(limit?: number, offset?: number): Promise<ApiResponse<{ items: TurnJournalSummary[] }>>`
- `fetchTraceTurn(turnId: number): Promise<ApiResponse<TurnJournalEntry>>`
3. React Query hooks in `workbench-ui/src/api/queries.ts`
- `useTraceTurns(limit?, offset?)` — queries `/trace/turns`
- `useTraceTurn(turnId)` — queries `/trace/{turn_id}`, enabled only when
turnId is defined
4. Replace `TraceRoutePlaceholder.tsx` with `workbench-ui/src/app/trace/TraceRoute.tsx`
Layout: `SplitPane` (horizontal) — turn timeline left, evidence panel right.
**Left pane — Turn Timeline:**
- List of `TurnJournalSummary` items from `useTraceTurns()`
- Each item shows: `Timestamp`, prompt excerpt (first line, truncated),
`DigestBadge` (trace_hash, truncated to 12 chars)
- Keyboard navigation: `j/k` or arrow keys to move, `Enter` to select
- Selected turn gets highlight border via `--color-border-accent`
- `SearchInput` at top: filters by prompt text or trace_hash prefix
- Empty state: "No turns recorded yet. Use Chat to create evidence."
**Right pane — Trace Evidence Panel:**
- `TabBar` with five tabs:
- **Surfaces** — all three surfaces displayed with explicit labels:
- `surface` labeled "User Surface (response)"
- `articulation_surface` labeled "Articulation Surface (realizer)"
- `walk_surface` labeled "Walk Surface (telemetry/evidence)"
- Each in its own bordered card with monospace font for content
- THIS IS the canonical proof of api-contract-v1.md surface separation
- **Grounding**`MetadataTable` showing `grounding_source` (with
`GroundingSourceBadge`), `epistemic_state` (with
`EpistemicStateBadge`), `normative_clearance` (with
`NormativeClearanceBadge`)
- **Verdicts**`MetadataTable` showing identity/safety/ethics verdicts
with outcome badges, refusal_emitted, hedge_injected
- **Metadata**`MetadataTable` showing turn_id, turn_cost_ms,
checkpoint_emitted, proposal_candidates (count + IDs),
journal_digest (`DigestBadge`)
- **Raw**`StableJsonViewer` showing the full `TurnJournalEntry` JSON,
collapsed by default (operator must explicitly expand — calm default,
infinite depth)
5. Evidence context integration
- Selecting a turn in the timeline calls `setSubject({ kind: 'turn',
turnId, data: fullEntry })`
- This makes the RightInspector (from Brief 4) show the same turn from
its compact angle
- Deselecting (pressing `Esc` with no overlay open) calls
`clearSubject()`
6. Update `workbench-ui/src/app/App.tsx`
- Replace the placeholder import with the real `TraceRoute` component
7. Tests in `workbench-ui/src/app/trace/TraceRoute.test.tsx`
- Empty state renders when no turns exist
- Turn list renders journal summaries
- Selecting a turn shows evidence panel with all five tabs
- Surface tab shows all three surfaces with correct labels
- Surface labels never say "response" for walk_surface
- Raw tab is collapsed by default
- Keyboard: j/k navigates, Enter selects, Esc deselects
- Evidence subject context is updated on selection
**Design rules:**
- Walk surface is ALWAYS labeled "telemetry/evidence" — never confused with
user surface
- Trace hash always visible and copyable (replay before persuasion)
- Raw JSON behind explicit expand (calm default, infinite depth)
- No decorative animation, no "thinking" indicators
- Failures/refusals more visually prominent than successes (audit-native)
**Constraints:**
- No new npm dependencies
- TypeScript strict mode
- Do not modify Python backend
- `pnpm build` green, `pnpm test` green
- Existing tests (chat, proposals, evals, replay) must still pass
### Work completed when
- [ ] Trace route replaces the placeholder
- [ ] Turn timeline shows real journal entries with search/filter
- [ ] Evidence panel shows all five tabs with correct content
- [ ] Three surfaces are displayed separately with explicit labels
- [ ] Walk surface labeled "telemetry/evidence"
- [ ] Raw JSON collapsed by default
- [ ] Evidence context updated on turn selection
- [ ] All tests pass, build passes
### Update the plan
In `docs/workbench/wave-1-evidence-spine.md`, check off all boxes under
section 1E. If all sections 1A-1F are now checked, add a completion note
at the top: "Wave 1 complete — [date]".
### PR and merge
```bash
cd ../core-wb-trace-route
git add workbench-ui/src/app/trace/
git add workbench-ui/src/types/api.ts
git add workbench-ui/src/api/client.ts
git add workbench-ui/src/api/queries.ts
git add workbench-ui/src/app/App.tsx
git add docs/workbench/wave-1-evidence-spine.md
git commit -m "feat(workbench): trace route — evidence spine projection (Wave 1E)
Replaces TraceRoutePlaceholder with real Trace route over the turn
evidence journal. Five-tab evidence panel: Surfaces (three surfaces
explicitly separated and labeled per api-contract-v1.md), Grounding,
Verdicts, Metadata, Raw JSON.
Integrates with evidence context — selecting a turn updates the
RightInspector. Walk surface labeled as telemetry/evidence."
```
Push and open PR:
```bash
git push -u origin feat/wb-trace-route
gh pr create --title "feat(workbench): trace route (Wave 1E)" \
--body "$(cat <<'EOF'
## Summary
- Real Trace route replacing placeholder
- Turn timeline + five-tab evidence panel
- Surface separation contract visually proven
- Evidence context integration with RightInspector
- Completes Wave 1 evidence spine
## Test plan
- [ ] `cd workbench-ui && pnpm test`
- [ ] `cd workbench-ui && pnpm build`
- [ ] Manual: start API (`core workbench api`), start frontend (`pnpm dev`),
chat a few turns, navigate to Trace, verify turns appear, select one,
verify all five tabs, verify inspector shows the same turn
- [ ] Verify walk_surface is labeled "telemetry/evidence" not "response"
EOF
)"
```
### Worktree cleanup
After merge:
```bash
cd /Users/kaizenpro/Projects/core
git worktree remove ../core-wb-trace-route
git branch -d feat/wb-trace-route
```
---
## Summary Card
| Brief | Agent | Branch | Wave | Depends on | Merge order |
|---|---|---|---|---|---|
| 1 — Backend Journal | GPT5.5 | `feat/wb-journal` | A | — | 2nd (or 1st) |
| 2 — Evidence Primitives | Claude | `feat/wb-primitives` | A | — | 1st (or 2nd) |
| 3 — Mutation Docs | GPT5.5 | `feat/wb-mutation-docs` | A | — | anytime |
| 4 — Evidence UI | Claude | `feat/wb-evidence-ui` | B | Brief 2 | 3rd |
| 5 — Trace Route | GPT5.5 | `feat/wb-trace-route` | C | Briefs 1+2+4 | 4th (last) |
**Wave A dispatch:** Briefs 1, 2, 3 — all start now, in parallel.
**Wave B dispatch:** Brief 4 — starts after Brief 2 merges.
**Wave C dispatch:** Brief 5 — starts after Briefs 1 and 4 merge.
**Wave 1 complete:** When Brief 5 merges.

View file

@ -0,0 +1,86 @@
# Brief R2-B — Workbench Backend Read Substrate (/runs, /audit, /packs, /vault)
Date: 2026-06-12
Plan: `docs/workbench/wave-R-mastery-revamp.md` § Wave R2 (the route specs);
this brief front-loads their BACKEND halves so R2 route briefs become
frontend-only.
**Agent:** GPT5.5-Thinking (XHIGH)
**Scope:** Python ONLY — `workbench/` + its tests. **Do not touch
`workbench-ui/` at all** (R0d is concurrently changing it; zero-overlap is
the parallel-safety guarantee). TS types land with each R2 route later.
**Parallel-safe with:** R0d, R1. Merges independently of both.
### Worktree
```bash
cd /Users/kaizenpro/Projects/core
git fetch origin
git worktree add ../core-wb-r2-backend origin/main -b feat/wb-r2-backend-reads
cd ../core-wb-r2-backend
```
### Read first
- `workbench/api.py`, `workbench/readers.py`, `workbench/schemas.py`,
`workbench/journal.py` — the existing read-only patterns (envelope,
pagination, error codes, size ceilings)
- `docs/workbench/api-contract-v1.md`, `docs/workbench/data-shapes-v1.md`
- `docs/workbench/wave-R-mastery-revamp.md` § Wave R2
- CLAUDE.md "Security and Trust Boundaries" (path traversal, pack IDs)
### Deliverables — read-only endpoints, `{ ok, generated_at, data/error }` envelope
For each endpoint: deterministic ordering, `?limit=&offset=` pagination on
lists, 404 (not synthetic data) for unknown ids, no mutation anywhere, and
the W-026 16 MiB read ceiling.
1. **`GET /packs`, `GET /packs/{pack_id}`** — sources: `language_packs/data/*`
manifests (and identity packs if cleanly readable). Surface manifest
checksums VERBATIM (the UI's DigestBadge verify affordance depends on
byte-honesty). **Trust boundary (mandatory):** validate `pack_id` against
a safe pattern BEFORE any filesystem access; reject path traversal with a
test proving it.
2. **`GET /audit/events`** — merged read-only view over the deterministic
audit sources that already exist on disk (reboot-event audit trail per
ADR-0158, teaching review/ratification records, workbench operator
telemetry). Stable total ordering (timestamp + tiebreak), each event
tagged with `source` and `mutation_boundary: bool`. Do NOT invent an
event store — this is a reader over existing artifacts.
3. **`GET /runs`, `GET /runs/{session_id}`** — **investigate first**: derive
"run/session" only from artifacts that already exist (engine_state
manifests/checkpoints, turn journal grouping). If no deterministic
on-disk session boundary exists, ship what IS derivable and document the
gap in the PR body — never synthesize.
4. **`GET /vault/summary`, `GET /vault/entries`** — **investigate first,
optional**: only if Shape B+ persisted engine_state provides a
deterministic on-disk vault source (persistence is OPT-IN, often absent).
If absent: return a typed `evidence_unavailable` error (the 501/unsupported
pattern already used for `/replay/`), document the gap, and stop. Do NOT
reach into live runtime memory; do NOT fake entries.
5. **Schemas** in `workbench/schemas.py` for every new shape (dataclasses,
same style as existing) — these become the source for the R1
schema-drift gate.
### Tests (pytest, alongside the existing workbench API tests)
Per endpoint: list + get + 404 + pagination + deterministic ordering;
path-traversal rejection for packs; a read-only proof (no writes outside
nothing — assert no dirty tree after requests); envelope conformance.
### Verification before push
```bash
uv run python -m pytest tests/ -k "workbench" -q # adjust to the real test path
core test --suite smoke -q
git diff --stat origin/main # workbench/ + tests + docs ONLY — no workbench-ui/
```
PR title: `feat(workbench): R2 backend read substrate — packs, audit, runs (+vault investigate)`
PR body must state, per CLAUDE.md: the trust boundary enforced (pack_id
validation, path confinement), and that no mutation path was added.

View file

@ -0,0 +1,177 @@
# Wave R2 Route Briefs — R2-T (Trace) then R2-A (Audit)
Date: 2026-06-12
Plan: `docs/workbench/wave-R-mastery-revamp.md` § Wave R2.
Execution: **strictly sequential** (R2-T merges before R2-A starts — they
share `App.tsx`, `types/api.ts`, and `routeConformance.test.tsx`).
**Agent:** GPT5.5-Thinking (XHIGH)
## Standing constraints (both briefs)
- Worktree off fresh `origin/main`; verify the gate lines before any code.
- Pre-push verification is the gate (the lane runs in CI, but green-local
first). After pushing and confirming checks green: **STOP and report.
Shay merges.**
- Token-only styling — `src/design/doctrine/hexScan.test.ts` enforces it.
- TS mirrors: when you add an interface for a schema listed in
`NOT_YET_MIRRORED` (`src/design/doctrine/schemaDrift.test.ts`), you MUST
remove its entry — the drift gate fails in both directions by design.
- Surfaces stay distinct: `surface``articulation_surface`
`walk_surface`. Trace is the canonical proof of this contract.
- Selected rows use `--color-selected-bg` / `--color-selected-border`;
focus ring is keyboard position only.
- Every route state honors ADR-0162 §6 — and adds itself to
`routeConformance.test.tsx` (`MOUNT_ROUTES` table).
- No new mutation endpoints. No invented data — absent evidence renders
honest absence states.
---
## Brief R2-T — Trace Route (the flagship)
### Worktree + gates
```bash
cd /Users/kaizenpro/Projects/core
git fetch origin
git worktree add ../core-wb-r2-trace origin/main -b feat/wb-r2-trace-route
cd ../core-wb-r2-trace
ls workbench-ui/src/app/EvidenceChainRail.tsx || echo "STOP: R1 (#713) not merged"
ls workbench/journal.py || echo "STOP: journal missing"
cd workbench-ui && pnpm install --frozen-lockfile
```
### Read first
- `docs/workbench/wave-1-evidence-spine.md` § 1E (the original spec — this
brief upgrades it onto the landed substrate)
- `docs/workbench/api-contract-v1.md` (surface separation, line ~231)
- `workbench/journal.py` + `workbench/schemas.py` (TurnJournalEntrySchema /
TurnJournalSummarySchema — your TS mirror source)
- `src/app/evidenceAddress.ts` (the codec already speaks `/trace/<turnId>`)
- `src/app/proposals/ProposalsRoute.tsx` (the reference implementation of
the triad: window-scope list nav + URL selection + subject publication)
- `src/design/components/` — Panel, VirtualizedList, TabBar, DigestBadge,
Timestamp, SearchInput, StableJsonViewer
- `src/app/routeConformance.test.tsx` (the contract your route joins)
### Deliverables
1. **TS mirrors** in `src/types/api.ts`: `TurnJournalEntry`,
`TurnJournalSummary` (field-exact per `schema-snapshot.json`). Remove
both `Schema` entries from `NOT_YET_MIRRORED` — the drift gate now
proves the mirror.
2. **Client + hooks**: `fetchTraceTurns(limit?, offset?)`,
`fetchTraceTurn(turnId)` in `client.ts`; `useTraceTurns()`,
`useTraceTurn(turnId)` (enabled only with a turnId) in `queries.ts`.
3. **`src/app/trace/TraceRoute.tsx`** replacing the placeholder
(delete `src/routes/TraceRoutePlaceholder.tsx`, update `App.tsx` — the
`/trace/:turnId?` param path already exists from R0c):
- `SplitPane` (horizontal, `id="trace"`): timeline left, evidence right.
- **Left**: `SearchInput` (filter by prompt text or trace-hash prefix) +
`VirtualizedList` of summaries — each row: `Timestamp`, prompt excerpt
(first line, truncated), `DigestBadge` (strip `sha256:`, default
12-char). List nav via the VirtualizedList's built-in keyboard spine.
Selected row uses the selected tokens.
- **Right**: `Panel` + `TabBar`, five tabs:
- **Surfaces** — all three, explicitly labeled: `surface` "User
Surface (response)", `articulation_surface` "Articulation Surface
(realizer)", `walk_surface` "Walk Surface (telemetry/evidence)".
Each in its own bordered card, mono. THIS is the canonical proof of
the surface-separation contract — a test must assert all three
labels render distinctly.
- **Grounding**`MetadataTable` + GroundingSource/EpistemicState/
NormativeClearance badges.
- **Verdicts** — identity/safety/ethics verdicts, refusal_emitted,
hedge_injected.
- **Metadata** — turn_id, turn_cost_ms, checkpoint_emitted, proposal
candidates, journal_digest (`DigestBadge`).
- **Raw**`StableJsonViewer`, collapsed behind an explicit expand
(calm default, infinite depth).
- **Selection**: writes `/trace/<turnId>` via the codec (`replace`, not
`push`), publishes `setSubject({ kind: "turn", turnId, data })` — the
inspector and EvidenceChainRail light from real fields. Deep link
restores selection (param read on load). `pushRecentItem` with
`Turn #<id>` so the palette's Recent section learns turns.
- **Empty state**: "No turns recorded yet. Use Chat to create
evidence." + next action `{ kind: "cli", command: "core chat" }`.
- **versor_condition: DO NOT add.** The journal does not carry it;
rendering it would require backend changes (investigate-first item,
deferred). Render nothing rather than synthesize.
4. **Conformance row**: add Trace to `MOUNT_ROUTES`
(loading "Loading trace…", the standard error contract via your
ErrorState props, the empty statement + command above). The `/trace/turns`
endpoint returns the envelope with `{ items: [...] }`.
5. **Route tests** (`src/app/trace/TraceRoute.test.tsx`): mocked fetch;
timeline renders; selecting shows the evidence panel; the three surface
labels render distinctly; raw JSON is collapsed by default; deep link
`/trace/3` restores selection; `j/k` moves the timeline.
### Verification before push
```bash
cd workbench-ui && pnpm build && pnpm test && pnpm test:e2e
```
PR title: `feat(workbench): Trace route — turn evidence timeline over the journal (Wave R2-T)`
---
## Brief R2-A — Audit Route (AFTER R2-T merges)
### Worktree + gates
```bash
cd /Users/kaizenpro/Projects/core
git fetch origin
git worktree add ../core-wb-r2-audit origin/main -b feat/wb-r2-audit-route
cd ../core-wb-r2-audit
ls workbench-ui/src/app/trace/TraceRoute.tsx || echo "STOP: R2-T not merged"
cd workbench-ui && pnpm install --frozen-lockfile
```
### Read first
- `workbench/schemas.py` (`AuditEvent`) + `workbench/api.py`
(`GET /audit/events` — landed in #712, pagination + stable ordering)
- `docs/workbench/wave-R-mastery-revamp.md` § R2 Audit
### Deliverables
1. **TS mirror**: `AuditEvent` in `types/api.ts`; remove from
`NOT_YET_MIRRORED`.
2. **`Timeline` primitive** (`src/design/components/Timeline/`): vertical,
deterministic ordering as delivered by the API (never re-sort
client-side), each entry shows `Timestamp`, source tag, summary;
entries with `mutation_boundary: true` get visual weight (selected-token
border, NOT color-only — include a label). Unit tested; preview entry.
3. **`src/app/audit/AuditRoute.tsx`** replacing the placeholder:
`Panel` + `SearchInput` (filter by source/summary) + `VirtualizedList`-
backed `Timeline`; pagination via the API's limit/offset (a quiet
"Load more", no infinite scroll).
4. **Scope boundary — explicitly deferred**: audit events do NOT become
evidence subjects in this PR. Extending the `EvidenceSubject` union
touches the codec/inspector/rail and is a separate, small, reviewed PR
(it will be spec'd by Claude). Selection here is visual focus only.
5. **Conformance row** + route tests (mocked fetch: list, filter,
mutation-boundary weighting, pagination, empty "No audit events
recorded." + next action, error contract, loading "Loading audit
events…").
### Verification before push
```bash
cd workbench-ui && pnpm build && pnpm test && pnpm test:e2e
```
PR title: `feat(workbench): Audit route — event timeline with mutation-boundary weighting (Wave R2-A)`
---
## After this pack
Remaining R2: Runs, Packs, Vault (fail-closed until persisted evidence
exists), Settings — briefs authored after R2-T/R2-A land. Then R3 theater:
`/replay` backend + the Replay Moment, deterministic DAG viewer, Demo
Theater, wrong=0 ledger.

View file

@ -0,0 +1,272 @@
# Wave R2 Route Briefs — Part 2 (R2-S, Runs, Packs, Vault, Settings)
Date: 2026-06-12
Plan: `docs/workbench/wave-R-mastery-revamp.md` § Wave R2.
Predecessor pack: `docs/handoff/wave-R2-route-briefs-2026-06-12.md` (R2-T/R2-A).
Execution: **strictly sequential merges** — every brief touches `App.tsx`,
`types/api.ts`, `routeConformance.test.tsx`, and the `NOT_YET_MIRRORED`
allowlist, so each PR rebases on the previous merge. Order:
```
R2-T → R2-A → R2-S → R2-R (Runs) → R2-P (Packs) → R2-V (Vault) → R2-X (Settings)
```
R2-S gates the rest: it adds the EvidenceSubject kinds the route briefs
publish. Do not start R2-R before R2-S is on main.
## Standing constraints (all briefs)
Identical to the predecessor pack's standing-constraints block — worktree off
fresh `origin/main`, green-local before push, **STOP after checks green;
Shay merges**, token-only styling (hexScan), shrink-only `NOT_YET_MIRRORED`
(remove the entry when you mirror a schema), ADR-0162 §6 conformance rows in
`MOUNT_ROUTES`, selected-vs-focused token discipline, no new mutation
endpoints, no invented data — absent evidence renders honest absence.
Backend ground truth (all already on main via #712, `workbench/api.py`):
list endpoints return `{items, limit, offset}` envelopes with `?limit=&offset=`
pagination; detail endpoints return the object directly. Schemas in
`workbench/schemas.py` are the TS-mirror source — mirror field-for-field.
---
## Brief R2-S — Evidence Subject Extension + Proposals Wrinkles (small, gating)
### Worktree + gates
```bash
cd /Users/kaizenpro/Projects/core
git fetch origin
git worktree add ../core-wb-r2-subjects origin/main -b feat/wb-r2-subjects
cd ../core-wb-r2-subjects
grep -q "audit_event" workbench-ui/src/app/evidenceContext.tsx && echo "STOP: already extended"
cd workbench-ui && pnpm install --frozen-lockfile
```
### Read first
- `src/app/evidenceContext.tsx` (EvidenceSubject union)
- `src/app/evidenceAddress.ts` (codec — `<kind>:<id>` inspect param + route-primary forms)
- `src/app/EvidenceChainRail.tsx` + its test (per-kind `deriveStages`; the
"MEANINGFULLY FAILS" pattern: removing one fixture field hollows exactly
its stage)
- `src/app/proposals/ProposalsRoute.tsx` (math-domain selection paths)
- `workbench/schemas.py` lines 285380 (field names the rail derives from)
### Deliverables
1. Four new subject kinds: `run { sessionId }`, `pack { packId }`,
`vault_entry { entryIndex }`, `audit_event { eventId }`. Codec forms
`run:<sessionId>`, `pack:<packId>`, `vault:<entryIndex>`,
`audit:<eventId>`; route-primary URL forms for `/runs/:sessionId` and
`/packs/:packId`; vault/audit are inspect-param-only. Update
`subjectEquals`, encode/decode round-trip tests.
2. `EvidenceChainRail.deriveStages` for each kind, derived from real fields
(pack: `checksum`/`manifest_digest` → provenance, `determinism_class`
admissibility; run: `checkpoint_present`/`checkpoint_revision` → replay,
`evidence_gap` non-null dims the chain; vault_entry: `epistemic_state`
admissibility, `versor_digest` → provenance; audit_event:
`mutation_boundary` → action, `payload_digest` → provenance). Absent
field → hollow, never lit. One MEANINGFULLY-FAILS test per kind.
3. Proposals wrinkles (recorded in the R0c report): (a) math-domain
proposals are not published as inspector subjects — publish on selection
exactly like the default domain; (b) the copied evidence link for a
math proposal omits `?domain=math` — the codec/copy path must include it
so the address round-trips to the same view. Regression tests for both.
No route work. No backend work. Small PR.
### Verification before push
```bash
cd workbench-ui && pnpm build && pnpm test
```
---
## Brief R2-R — Runs Route
### Worktree + gates
```bash
cd /Users/kaizenpro/Projects/core
git fetch origin
git worktree add ../core-wb-r2-runs origin/main -b feat/wb-r2-runs-route
cd ../core-wb-r2-runs
grep -q '"run"' workbench-ui/src/app/evidenceContext.tsx || echo "STOP: R2-S not merged"
cd workbench-ui && pnpm install --frozen-lockfile
```
### Read first
- `workbench/schemas.py` (RunSummary / RunTurnRef / RunDetail) and
`workbench/readers.py::list_runs` / `read_run` (sources:
engine_state_manifest + turn_journal; `evidence_gap` semantics)
- The Trace route as merged by R2-T (cross-link target + triad reference)
- `src/app/proposals/ProposalsRoute.tsx` (triad reference implementation)
### Deliverables
1. TS mirrors RunSummary/RunTurnRef/RunDetail; remove all three from
`NOT_YET_MIRRORED`.
2. `GET /runs` list (VirtualizedList + `useListNavigation` window scope +
SearchInput): session rows with checkpoint badge
(`checkpoint_present`/`checkpoint_revision`), turn count, timestamps
(Timestamp component). `evidence_gap` non-null renders the gap text
honestly in-row — never hidden.
3. `GET /runs/{session_id}` detail in Panel + TabBar (Turns / Manifest /
Raw). Every turn row shows `trace_hash` as DigestBadge and links to
`/trace/<turn_id>` — the cross-link is the point of this route.
Turn list paginates via `turn_limit`/`turn_offset`.
4. Selection publishes the `run` subject (R2-S) and the URL records it;
`pushRecentItem` on visit.
5. Conformance rows in `MOUNT_ROUTES` (loading "Loading runs…", error,
empty: "No runs recorded yet." + `core chat`).
### Verification before push
```bash
cd workbench-ui && pnpm build && pnpm test
```
---
## Brief R2-P — Packs Route
### Worktree + gates
```bash
cd /Users/kaizenpro/Projects/core
git fetch origin
git worktree add ../core-wb-r2-packs origin/main -b feat/wb-r2-packs-route
cd ../core-wb-r2-packs
grep -q '"pack"' workbench-ui/src/app/evidenceContext.tsx || echo "STOP: R2-S not merged"
cd workbench-ui && pnpm install --frozen-lockfile
```
### Read first
- `workbench/schemas.py` (PackSummary / PackDetail — note `checksums` dict
and `manifest_digest`), `workbench/readers.py::list_packs` / `read_pack`
- `src/design/components/` inventory — TreeView does NOT exist yet; you add it
### Deliverables
1. TS mirrors PackSummary/PackDetail; shrink `NOT_YET_MIRRORED`.
2. New design primitive `TreeView` (`src/design/components/TreeView/`):
deterministic expansion (no animation), keyboard-driven (arrows expand/
collapse/traverse), small (~150 lines), own test. Registers its
shortcuts via `useRegisterShortcuts` only while focused.
3. List: packs with `source`, `version`, `language`/`modality`, and a
`determinism_class` badge. Detail in Panel + TabBar (Manifest tree via
TreeView / Checksums / Raw). `checksum` and `manifest_digest` as
DigestBadge; per-file `checksums` table with DigestBadges (this is the
verify affordance — the manifest-checksum doctrine made visible).
4. Selection publishes the `pack` subject; URL `/packs/:packId`.
5. Conformance rows (empty state: "No packs discovered." +
`core pack validate <path>`).
### Verification before push
```bash
cd workbench-ui && pnpm build && pnpm test
```
---
## Brief R2-V — Vault Route (fail-closed)
### Worktree + gates
```bash
cd /Users/kaizenpro/Projects/core
git fetch origin
git worktree add ../core-wb-r2-vault origin/main -b feat/wb-r2-vault-route
cd ../core-wb-r2-vault
grep -q '"vault_entry"' workbench-ui/src/app/evidenceContext.tsx || echo "STOP: R2-S not merged"
cd workbench-ui && pnpm install --frozen-lockfile
```
### Read first
- `workbench/schemas.py` (VaultSummary / VaultEntry) and
`workbench/readers.py::read_vault_summary` / `list_vault_entries`
especially behavior when no persisted vault exists (`persisted: false`)
- `docs/runtime_contracts.md` (memory contract; exact-recall doctrine)
### Deliverables
1. TS mirrors VaultSummary/VaultEntry; shrink `NOT_YET_MIRRORED`.
2. **Fail-closed is the design**: when `persisted` is false (or entries
empty), the route's PRIMARY state is the honest absence card — vault
persistence is opt-in via `RuntimeConfig.persist_session_state`; say so,
with the config pointer. No skeleton theater pretending data is coming.
3. With data: summary strip (entry_count, store_count, reproject_interval,
max_entries) + VirtualizedList of entries with `epistemic_status` /
`epistemic_state` badges and `versor_digest` DigestBadge.
4. **Doctrine line**: render only fields the backend returns. Do NOT
compute, estimate, or display any similarity/relevance score — runtime
recall is exact CGA (`cga_inner`) and the UI must not invent an
approximate proxy for it.
5. Selection publishes `vault_entry` subject (inspect-param only).
6. Conformance rows (empty/fail-closed state asserted as the primary
contract, not an afterthought).
### Verification before push
```bash
cd workbench-ui && pnpm build && pnpm test
```
---
## Brief R2-X — Settings Route
### Worktree + gates
```bash
cd /Users/kaizenpro/Projects/core
git fetch origin
git worktree add ../core-wb-r2-settings origin/main -b feat/wb-r2-settings-route
cd ../core-wb-r2-settings
cd workbench-ui && pnpm install --frozen-lockfile
```
### Read first
- `workbench/schemas.py::RuntimeStatus` + `GET /runtime/status` (already
mirrored in `types/api.ts` — no NOT_YET_MIRRORED change expected)
- Existing localStorage usage (recent items) for the prefs pattern
### Deliverables
1. Two panels. **Workbench preferences** (localStorage only): default
landing route, inspector open-by-default, list density
(comfortable/compact). Each pref takes effect immediately and survives
reload (test via storage mock).
2. **Runtime (read-only)**: render `/runtime/status`
backend, git_revision (DigestBadge), engine_state_present,
checkpoint_revision, revision_warning (warning styling via tokens),
active_session_id, mutation_mode. Plus the explicit statement:
"Engine configuration is CLI-only. This page mutates nothing on the
server." That sentence is asserted in the conformance test.
3. No backend changes; no new subject kind; no engine mutation of any kind.
4. Conformance rows (loading/error for the status fetch; the prefs panel
has no empty state — it always renders).
### Verification before push
```bash
cd workbench-ui && pnpm build && pnpm test
```
---
## After this pack
R2 complete → R3 theater briefs: `/replay/{id}` backend + the Replay
Moment, deterministic DAG viewer (hand-rolled layered layout, golden-file
tests), Demo Theater (`GET /demos`, `POST /demos/{id}/run`), wrong=0 ledger
view in Evals. The `/replay` backend is Python-only and parallel-safe with
any in-flight R2 route PR.