Commit graph

20 commits

Author SHA1 Message Date
Shay
0ff3e3b2c8 feat(workbench): Settings route — wired prefs + read-only runtime status (Wave R2, final route)
Replaces SettingsRoutePlaceholder, completing all six placeholder routes.

- workbenchPrefs.ts: typed localStorage prefs with safe parse + live
  same-tab sync (useWorkbenchPrefs). Every pref has a real consumer — no
  toggle that does nothing:
    * landingRoute -> consumed by the App index redirect (Navigate)
    * inspectorDefaultOpen -> consumed by EvidenceProvider initial state
      (EvidenceUrlSync still force-opens on a ?inspect= deep link)
- SettingsRoute: two panels. Preferences (landing route select + inspector
  default switch, applied on next load, survive reload). Runtime (read-only
  /runtime/status: backend, git_revision DigestBadge, engine_state_present,
  checkpoint_revision, revision_warning with warning token, active_session_id,
  mutation_mode) under the standing statement 'Engine configuration is
  CLI-only. This page mutates nothing on the server.'
- no backend change, no new schema/subject, no NOT_YET_MIRRORED change, no
  engine mutation of any kind
- bespoke conformance block (Settings has no empty state — the prefs panel
  always renders); asserts the loading label + CLI-only statement + the
  error contract over the status fetch
- tests: workbenchPrefs (defaults / persist+reload / invalid+malformed
  fallback / partial merge) + SettingsRoute (both panels, landing-route
  persistence, inspector toggle, error-without-mutation)

DEFERRED (flagged, not shipped as theater): list-density pref — honest
wiring requires threading a density token through every list row, a
separate change; shipping a control that does nothing would violate
ADR-0160 (audit-native, not analytics theater). Token-only styling
(hexScan green); full vitest 363 green.
2026-06-12 21:39:41 -07:00
Shay
ebb95a0541 feat(workbench): Vault route — fail-closed epistemic memory evidence (Wave R2)
Replaces VaultRoutePlaceholder with the real route over the R2-B
/vault read substrate (#712).

- TS mirrors VaultSummary/VaultEntry; both removed from NOT_YET_MIRRORED.
  Also fixes a latent ErrorCode drift: adds 'evidence_unavailable' to the
  TS union (present in Python schemas, missing in TS) — the route branches
  on it.
- fail-closed is the design: a missing/unpersisted vault (the 501
  evidence_unavailable signal, or persisted=false / entry_count=0) renders
  the honest absence card as the PRIMARY state, naming the opt-in
  RuntimeConfig.persist_session_state pointer — not a generic error, no
  skeleton theater
- with data: summary strip (entry_count, store_count, reproject_interval,
  max_entries, source_path) + VirtualizedList of entries with
  epistemic_status / epistemic_state pills and versor_digest DigestBadge
- exact-recall doctrine: renders only backend fields; never computes or
  displays a similarity/relevance proxy (runtime recall is exact cga_inner)
- selection publishes the vault_entry subject (inspect-param only, via
  setSubject + setInspectorOpen; EvidenceUrlSync writes ?inspect=vault:N);
  a URL-restored identity-only subject is hydrated once entries load
- Vault row added to routeConformance MOUNT_ROUTES — the fail-closed
  absence asserted as the primary contract
- tests: fail-closed card (not error), summary+entries, no-similarity-score
  doctrine, vault_entry subject publication, j/k spine

Token-only styling (hexScan green).
2026-06-12 21:29:18 -07:00
Shay
cfcd7dc9ec feat(workbench): Packs route + TreeView primitive — manifest & checksum evidence (Wave R2)
Replaces PacksRoutePlaceholder with the real triad route over the R2-B
/packs read substrate (#712).

- TS mirrors PackSource/PackSummary/PackDetail; PackSummary/PackDetail
  removed from NOT_YET_MIRRORED (drift gate now enforces them)
- new design primitive TreeView (src/design/components/TreeView): a
  deterministic, keyboard-driven JSON tree — object keys sorted, array
  indices in order, no animation; ←/→ collapse/expand, ↑/↓ + j/k move,
  Home/End jump, Enter toggles; registers its shortcuts only while focused
  (KeyboardHelp never advertises tree controls when no tree is on screen)
- list pane: VirtualizedList + useListNavigation (j/k) + SearchInput; rows
  show pack_id, source, version, language/modality, determinism_class pill
- detail pane: Panel + TabBar (Manifest / Checksums / Raw); Manifest renders
  via TreeView; Checksums is the verify affordance — manifest_digest and
  declared checksum as DigestBadges plus the per-field manifest checksums
  table (the checksum-hashes-the-bytes-on-disk doctrine made visible); Raw
  collapsed by default
- selection publishes the pack subject (R2-S), records /packs/<packId>
  (replace), pushRecentItem on visit
- Packs row added to routeConformance MOUNT_ROUTES (loading 'Loading
  packs...', error 'No packs mutation occurred.', empty 'No packs
  discovered.' + core pack validate <path>)
- tests: TreeView (sorted/collapsed/expand/collapse/focus/click/empty) +
  PacksRoute (list, replace-mode select + manifest tree, checksum verify
  affordance, j/k spine)

Token-only styling (hexScan green).
2026-06-12 21:14:13 -07:00
Shay
2b13feab24 feat(workbench): Runs route — session evidence with trace cross-links (Wave R2)
Replaces the RunsRoutePlaceholder with the real triad route over the
R2-B /runs read substrate (#712).

- TS mirrors RunSource/RunSummary/RunTurnRef/RunDetail; all three classes
  removed from NOT_YET_MIRRORED (drift gate now enforces them)
- list pane: VirtualizedList + useListNavigation (j/k) + SearchInput;
  rows show source, session_id, turn_count, relative timestamp, a
  checkpoint badge (present + revision vs 'no checkpoint'), and any
  evidence_gap rendered honestly in-row (never hidden)
- detail pane: Panel + TabBar (Turns / Manifest / Raw); every turn row
  is an anchor to /trace/<turn_id> (backend-provided trace_path) with the
  trace_hash as a DigestBadge — the cross-link is the point of this route;
  turn list pages via turn_limit ('Load more turns')
- selection publishes the run subject (R2-S) and records /runs/<sessionId>
  in the URL (replace), pushRecentItem on visit
- Runs row added to routeConformance MOUNT_ROUTES (loading 'Loading
  runs...', error 'No runs mutation occurred.', empty 'No runs recorded
  yet.' + core chat)
- RunsRoute.test.tsx: list evidence, in-row gap, replace-mode URL select,
  trace cross-link hrefs, manifest tab, j/k keyboard spine

Token-only styling (hexScan green); full vitest 339 green.
2026-06-12 20:58:55 -07:00
Shay
f6a03a8c14 feat(workbench): evidence subject extension and proposals wrinkles (Wave R2-S) 2026-06-12 17:13:59 -07:00
Shay
d48d040467 feat(workbench): Audit route — event timeline with mutation-boundary weighting (Wave R2-A) 2026-06-12 16:53:03 -07:00
Shay
aa6fb8ba90 feat(workbench): add trace route journal timeline 2026-06-12 16:30:34 -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
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
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
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