Commit graph

20 commits

Author SHA1 Message Date
Shay
8ce60cc196 fix(workbench): guard missing global navigator for node-env test files (Node 20) 2026-06-12 13:26:30 -07:00
Shay
9344f11415 feat(workbench): design mastery pass — chain rail, typography, doctrine-as-tests (Wave R R1)
- EvidenceChainRail: the spine's seven stages rendered per subject with
  the honesty contract — lit/hollow/dim derive ONLY from carried fields
  (a trace hash lights 'replay' as RECORDED, never as verified); pure
  deriveStages() with meaningfully-fail tests (remove one field -> exactly
  its stage hollows).
- Hash display standard: DigestBadge default 12-char + copy everywhere;
  CopyableHash consolidated away (2 call sites swapped, file deleted).
- Typography: tabular-nums on MetadataTable values + eval metrics;
  text-wrap balance on Panel titles + EmptyState statements.
- Selection unification: new --color-selected-bg/-border tokens; selected
  (persistent) vs focused (transient) now visually distinct across
  Proposals/Replay/Evals lists; focus-ring no longer doubles as selection.
- EmptyState gains a deterministic monochrome inline-SVG glyph.
- Panel adopted in Evals lane list + Proposals queue.
- Doctrine-as-tests: hexScan (no palette literals outside tokens.css/
  generated tokens.ts) + schemaDrift gate (scripts/dump-schemas.py AST
  walk -> schema-snapshot.json -> every dataclass field mirrored in
  types/api.ts; explicit shrink-only NOT_YET_MIRRORED debt list).
  The drift gate caught real drift on first run: ChatTurnResult.turn_id
  existed in Python, missing from TS — now mirrored.

Verified: build green; vitest 36 files / 279 tests EXIT=0; playwright
12/12; dump-schemas.py runs clean.
2026-06-12 13:18:44 -07:00
Shay
c54a942871 feat(workbench): interaction substrate — list nav, virtualization, panel chrome, inspector resize, palette verbs (Wave R R0d)
- useListNavigation: shared j/k/arrows/Home/End/Enter keyboard spine,
  container or window scope, input guard, roving tabindex. Replaces
  ProposalsRoute's bespoke window listener (one pattern app-wide).
- VirtualizedList over @tanstack/react-virtual composing the hook;
  evidence lists render O(viewport).
- Panel: standard header/toolbar/body chrome for routes.
- Inspector resize: Shell center cell becomes SplitPane(main, inspector)
  when open; width persists via guarded storage (SplitPane id).
- Palette action verbs: Command gains kind (navigate|action); Shell
  registers Toggle inspector + Copy evidence link; EvalsRoute registers
  'Run eval lane <lane>' per read-only lane (same POST /evals/run).
- shortcutRegistry: binding sites register what they handle while
  mounted; KeyboardHelp renders FROM the registry — advertising an
  unbound shortcut is structurally impossible. j/k, /, Enter rows
  (removed in R0a as false) return as live registry entries.
- Kbd primitive (front-loaded from R1); SearchInput registers '/';
  SearchInput wired into Proposals + Replay lists with client filters.
- Preview entries for Kbd/Panel/VirtualizedList.

Verified: build green; vitest 33 files / 245 tests EXIT=0 (~5.5s);
playwright 12/12.
2026-06-12 12:48:02 -07:00
Shay
922e700048 test(workbench): mount conformance routes with param paths + EvidenceProvider
Routes now publish selection as the evidence subject and read selection
from path params (R0c), so the ADR-0162 conformance harness mounts them
the way App.tsx declares them.
2026-06-12 12:17:57 -07:00
Shay
41f4435dc1 feat(workbench): evidence addresses — deep-linkable subjects + inspector URL state
URL = subject: one codec (evidenceAddress.ts) owns the address grammar for
every EvidenceSubject kind, including kinds whose routes are placeholders.
urlToSubject is a total inverse — malformed input yields null, never throws.

- Route params: /trace/:turnId?, /proposals/:proposalId?, /evals/:laneId?,
  /replay/:artifactId? (placeholders keep flat paths)
- ?inspect= carries inspector subject + open state; EvidenceUrlSync restores
  deep links and write-through syncs with replace (selection churn never
  pollutes history); malformed values are dropped from the URL
- Proposals/Evals/Replay restore selection from their param on load, write it
  on selection change (replace), and publish the selected subject to the
  evidence context (identity at once, detail when the query resolves)
- EvidenceSubject.data now optional; inspectors render an honest
  'detail not loaded' state for identity-only subjects
- Cmd+Shift+C copies origin + subjectToUrl(subject); no-op without subject;
  input-focus guard; transient inline 'Copied' on the inspector header
- Fix pre-existing unbounded re-render loop in RightInspector.test.tsx
  (render-phase setSubject); subjects now set from effects
2026-06-12 12:17:57 -07:00
Shay
d7236559da fix(workbench): exclude e2e/ from vitest collection
Reconciliation on rebase over R0a (#709): vitest's default include
pattern collects e2e/*.spec.ts and fails on Playwright imports.
e2e/ belongs to 'pnpm test:e2e'; vitest owns src/.
Verified post-rebase: vitest 27 files/181 tests EXIT=0 + playwright 12/12.
2026-06-12 12:07:57 -07:00
Shay
c356a13055 test(workbench): playwright smoke lane (ADR-0162 acceptance 5-7) 2026-06-12 12:05:28 -07:00
Shay
513f80d5fb fix(workbench): test-runner teardown hardening + frontend CI lane (Wave R R0a)
Full 'pnpm test' previously hung indefinitely (killed at CI walls,
misdiagnosed as teardown slowness). Two root causes found and fixed:

1. RightInspector.test.tsx called setSubject() during render with a fresh
   object every pass -> infinite synchronous render loop (100% CPU spin,
   blocks the event loop so no timeout can fire). Moved into useEffect.
2. Test QueryClients used retry:false but default gcTime (5 min) -> a live
   GC timer per cached query kept workers from exiting. New shared
   createTestQueryClient() with gcTime:0 adopted across all 7 test files.

Hardening so any future leak fails loudly instead of hanging:
- vite.config.ts: testTimeout/hookTimeout 10s, teardownTimeout 5s
- useManagedTimeout hook owns previously-bare setTimeouts in DigestBadge,
  MetadataTable, RatificationCommandPanel (4 sites), CommandPalette focus

First-ever frontend CI lane (.github/workflows/workbench-ui.yml):
path-filtered to workbench-ui/** AND the workflow file itself,
pnpm install --frozen-lockfile + build + vitest, timeout-minutes 15.

Honesty fix: KeyboardHelp no longer advertises j/k, /, Enter (unbuilt
until R0d restores them registry-backed).

Route conformance test (ADR-0162 §6, executable): empty/error/loading
contracts asserted for Chat/Proposals/Evals/Replay; fixed the two gaps
it caught (ReplayRoute bare-div error branch -> ErrorState contract;
ArtifactList empty state gained a next action).

Result: 27 files / 181 tests pass, suite EXITS in 4.9s wall-clock
(was: indefinite hang).
2026-06-12 11:45:23 -07:00
Shay
2d850c8d9b fix(workbench): preserve command section focus indices 2026-06-12 09:06:52 -07:00
Shay
94a122ba6e fix(workbench): guard recent-item persistence 2026-06-12 09:06:22 -07:00
Shay
2746655bb4 feat(workbench): evidence context, inspector drawer & command registry (Wave 1A/1B)
Brief 4 of the Wave 1 evidence spine — wire the evidence-context architecture
so every route can project into one shared evidence manifold.

Command registry (1A):
- commandRegistry.ts: useSyncExternalStore-backed command store with a cached
  snapshot (referentially stable to avoid render loops); useCommands /
  useCommandRegistry; recent-items in localStorage (last 10).
- CommandPalette.tsx: reads from the registry; all ten routes are navigation
  commands (was Chat/Proposals/Evals hardcoded); fuzzy filter, recent section,
  shortcut badges; Router/Fallback split preserved.
- useGlobalKeyboard.ts: Cmd+K palette, Cmd+I inspector, Cmd+1-0 routes, ? help.

Evidence drawer (1B):
- evidenceContext.tsx: EvidenceSubject union (turn|proposal|artifact|
  eval_result|none) + useEvidenceSubject hook + EvidenceProvider above the
  router Outlet so inspector state persists across route transitions.
- RightInspector.tsx: five evidence projections (was a null stub); reuses the
  Brief 2 primitives (MetadataTable, DigestBadge, Timestamp, badges).
- Shell.tsx: visibility driven by inspectorOpen (collapsed by default); no more
  hardcoded collapsed=true.
- KeyboardHelp.tsx: shortcut reference dialog.
- TopBar.tsx: palette open state lifted to Shell via props.

Tests: evidenceContext + RightInspector specs added; CommandPalette keyboard
contract updated for the ten-command navigation set (clamp at both ends).

Deferred (noted in spine doc): per-route register()/setSubject call sites,
action-commands (run eval / copy hash), resizable inspector width.

Verified: tsc -b + vite build green; affected specs pass per-file.
2026-06-12 07:44:19 -07:00
Shay
af8d4f75da fix(workbench-ui): guard SplitPane storage access
Handle environments where localStorage is unavailable and provide the Vitest browser-storage shim expected by SplitPane tests.
2026-06-12 07:08:22 -07:00
Shay
5b68e78ed2 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 — TabBar uses native ARIA
semantics instead of @radix-ui/react-tabs.

PreviewPage updated with all six components in representative states.
Section 1C checked off in wave-1-evidence-spine.md.
2026-06-12 06:45:20 -07:00
Shay
6d1f6bba0c feat: implement CORE Workbench W3 Ratification Corridor 2026-06-03 20:13:36 -07:00
Shay
30972e184e
feat(workbench/W-029): proposal queue (#329) 2026-05-26 21:08:36 -07:00
Shay
19506e9f60
feat(workbench/W-031): replay theater (ADR-0160 §Phase 6, deterministic-evidence UX) (#328) 2026-05-26 20:56:31 -07:00
Shay
00f5056209
feat(workbench/W-030): eval center (safe lanes only, ADR-0160 §Phase 5) (#327) 2026-05-26 20:39:30 -07:00
Shay
a612038d41
feat(W-028): chat surface + trace drawer (#303) 2026-05-26 13:22:11 -07:00
Shay
cdead696ed
feat(W-027): Workbench frontend shell — five-region grid + ten empty routes + live StatusFooter (ADR-0160 / ADR-0162) (#299)
- Five-region CSS grid shell (TopBar, LeftNav, Main, StatusFooter; Inspector collapsed)
- Ten placeholder routes with EmptyState cli/string variants and ApiErrorBoundary
- Extended EmptyState API: nextAction accepts string (button) | { kind: "cli", command }
- Updated CommandPalette: real fuzzy search over three commands (Chat, Proposals, Evals)
  with ↑/↓/Enter keyboard nav and useInRouterContext degradation for Branch 1 preview
- TanStack Query hooks for all W-026 endpoints; 30s polling on runtime/status
- TypeScript mirror of workbench/schemas.py at src/types/api.ts
- StatusFooter: mutation_mode badge, git_revision (copy on click), checkpoint_revision
  with amber warning + ADR-0157/ADR-0158 expansion note
- TopBar: CORE Workbench wordmark, ⌘K palette trigger, connection pill
- ApiErrorBoundary (class component) catches WorkbenchApiError → ErrorState
- scripts/dump-api-schemas.py: AST-based Python dataclass field extractor
- workbench-ui/api-schema-snapshot.json: checked-in drift sentinel
- 51 tests across 10 files (0 failures); enum-coverage 4/4; clean vite build
2026-05-26 12:09:12 -07:00
Shay
e89463a975
feat(workbench-ui): design system v1 scaffold (ADR-0162 Branch 1) (#295)
* feat(workbench-ui): design system v1 scaffold

* fix(workbench): close R1 (GroundingSource enum coverage) + R4 (digest test)

R1 — Promote GroundingSource to a typed Literal in core/epistemic_state.py
so it has the same single-source-of-truth shape as ReviewState.  The
existing epistemic_state_for_grounding_source() function already
enumerates the six labels (pack, teaching, vault, partial, oov, none);
this codifies them.

scripts/dump-enums.py now snapshots GroundingSource via the existing
literal_values helper.  workbench-ui's enumCoverage.test.ts gains a
fourth assertion that the badge mapping matches the Python source
1:1.  Adding a grounding-source value on the Python side without
updating the badge fails the build-time test loud — same discipline
as the other three enums.

R4 — Add an explicit DigestBadge test to StableJsonViewer.test.tsx:
asserts the badge text matches the SHA-256 prefix of the source bytes,
and clicking the badge copies the FULL digest (not the truncated
prefix).  Recomputes the expected digest via crypto.subtle to avoid
hard-coding a hex string that could drift.

R2 (component-level reduced-motion enforcement), R3 (EmptyState
copy-CLI affordance), and R5 (`uv run core` packaging paper cut) are
deferred — R2/R3 become meaningful with W-027/W-029, R5 is a
packaging-layer concern outside this PR's scope.

Validation:
- pnpm test: 19 passed (was 17, +1 enum coverage, +1 digest test)
- pnpm build: clean
- pnpm test:enum-coverage: 4 passed
- core test --suite smoke -q: 67 passed
2026-05-26 11:33:27 -07:00