Expand the Trace Construction panel from a summary scaffold into a read-only evidence inspection surface.
Adds detail sections for construction proposals, mentions, mention bindings, bound relations, contract assessments, and source-span exactness rows. Preserves the proposal/contract authority boundary: proposals remain diagnostic and contract assessments determine runnable/blocker disposition.
Validation: workbench-ui, smoke, and lane-shas GitHub Actions passed on PR head d20d143b5744d15d73d07a515b466143af6a2d52.
* feat(workbench-ui): add construction evidence panel model
* test(workbench-ui): cover construction evidence panel model
* feat(workbench-ui): add construction evidence panel component
* fix(workbench-ui): address construction panel review nits
* fix(workbench-ui): type injected construction fetcher test
Close the deferred Vault item 4: a read-only endpoint proving a selected
vault entry is recallable by CORE's actual exact CGA machinery, surfaced
in the entry inspector.
Backend (read-only, cold-persisted only):
- GET /vault/entries/{index}/recall rehydrates the persisted VaultStore
(VaultStore.from_dict — bit-exact versors, no reprojection) and runs the
real VaultStore.recall using the entry's own stored versor as the query.
Exact cga_inner scan — never ANN / cosine / approximate.
- recall's +inf exact-self-match sentinel never crosses the boundary: the
genuine finite cga_inner is reported plus an exact_self_match flag. The
raw versor never leaves the engine — only content-addressed digests.
- Trust boundary: caller-controlled index -> 404 (out of range / non-int);
absent persisted snapshot -> 501. The file is never written; the live
runtime is never touched; recall is deterministic over persisted bytes.
Frontend:
- "Exact CGA Recall" inspector panel, collapsed by default so the read is
opt-in (the query hook only mounts on expand). Copy says "exact CGA
recall" / cga_inner — never similarity / relevance / score / ANN /
cosine. Honestly surfaces that a byte-identical self-match is promoted
ahead of metric ranking (CGA null-vector self inner-product ~0).
INV-24: register workbench/readers.py in VAULT_RECALL_SITES as
EVIDENCE_TELEMETRY (operator inspection evidence, not claim-shaping), and
tighten the recall-site detector to recognise VaultStore.from_dict factory
bindings so the obligation is real rather than silently bypassed.
Tests: backend 501/404/self-recall/determinism/no-mutation/JSON-safety +
API status codes; frontend collapsed-doctrine + expanded self-recall
evidence. Full workbench suite (192) + architectural invariants (61) +
vault-touching vitest (70) all green.
Stop the rail reading as a wall of 'not applicable to vault entries':
- each dim stage now carries a specific honest reason (intent: a stored
field has no originating user intent; authority: vault recall is
read-only; action: no emission)
- replay lights on the recorded versor_digest exactly as a turn lights
on its trace_hash — a content-addressable fingerprint, NOT a verified
replay claim (honesty contract sanctioned)
- provenance/admissibility derivations enriched (versor_digest + metadata;
epistemic_state / epistemic_status)
Fixed 7-stage spine and honesty contract preserved; labels unchanged.
Strengthened the meaningfully-fail test to cover replay + dim stages.
Make a populated vault navigable beyond opaque digests:
- status facet: 'all' + the distinct epistemic_status values present in
the loaded entries (derived, never a hardcoded closed set — the status
vocabulary is engine-owned)
- text search now reaches metadata keys/values, not just the epistemic
labels + index
- boolean facets: 'Has proposition' (propositional_form) and 'Has
promotion digest' (promotion_certificate_digest); dropped 'has replay
hash' — no such field exists
- filter-empty stays filter-empty (not persistence guidance)
No new endpoint; filters operate on already-loaded entries.
Surface a selected vault entry's real evidence instead of just
epistemic_state + a digest:
- widen VaultEntrySubjectData to carry epistemic_status + the full
metadata dict (data already reaches the inspector at runtime; this is
a type-only unlock, no new fetch/endpoint)
- inspector shows epistemic_status and epistemic_state distinctly, core
identity rows (turn, role) with honest 'not recorded' when absent,
present-only rows (corrected, energy_*, promotion_certificate_digest),
a propositional_form headline when present, copyable vault:<index> and
versor_digest handles, and a collapsible key-sorted raw metadata drawer
- curated rows key off fields that actually exist on vault entries; the
raw drawer is the catch-all as the open metadata schema grows
Exact-recall doctrine preserved: no similarity/relevance/score text.
The L10 heartbeat loop (run_continuous) had no process to drive it; `core always-on`
is that process — the T-experience spine made runnable. It ticks idle_tick on a wall-clock
cadence so the engine LIVES and LEARNS with no user turn, persists lived_life.json (the
workbench Lived Life surface) + the checkpoint, and resumes the SAME life on restart.
- chat/always_on.py: run_continuous gains unbounded operation (heartbeats=None, runs until
stop) + an interruptible inter-beat wait (_sleep_until_stop) so shutdown latency is one
slice, not the cadence interval. Persists the run's identity pack ids (resume-verdict
faithfulness). on_heartbeat is now best-effort (a broken log pipe can't kill the life).
- chat/always_on_daemon.py (new): the daemon shell — a single-instance OS lock (fcntl.flock:
kernel-held, atomic, auto-released on death — no stale window, no PID-reuse, no half-written
race), SIGINT/SIGTERM -> graceful stop (handlers saved/restored), the continuous-life config
FORCED on, ephemeral (--no-load-state) writes no durable artifact. Foreground + explicit:
no hidden background execution (CLAUDE.md); only writes the engine-state dir it was given.
- core/cli.py: `core always-on [--interval --max-beats --no-load-state --quiet]` with per-beat
+ summary logging; validates --interval; reports IdentityContinuityError / IncompatibleEngine
StateError (the "different life / newer build" cases) as clean refusals, not tracebacks.
- workbench/readers.py: ENGINE_STATE_ROOT now honors CORE_ENGINE_STATE_DIR (= the daemon's
resolved dir), so the workbench can't be split-brained (reading REPO_ROOT/engine_state while
the daemon writes elsewhere); the Lived Life resume verdict recomputes from the persisted
pack config, not a default config (no false substrate_changed for a non-default-pack life).
- Lived Life absence state now points at the real `core always-on` command (loop closed).
Adversarial 4-lens review (lock/concurrency, signals/shutdown, invariants/trust-boundary,
test-vacuity/CLI) caught 16 findings; this fixes all real ones — the HIGH lock races (two
daemons over one life), the env split-brain, the IO-kill, the uncaught identity/schema errors,
the unvalidated interval, the ephemeral-artifact shadow, and the resume-verdict pack-id bug —
and closes the two test-coverage gaps it flagged (real SIGTERM path + config-forcing-at-the-
runtime boundary).
Tests (non-vacuous): 11 daemon (flock live-holder refusal, leftover-lock reclaim, unbounded+
stop, interruptible sleep, forced-config-at-boundary, no-load-state guard, REAL SIGTERM
subprocess) + the reader pack-id discrimination test (fails under the old default-config bug).
245 workbench+invariants+always-on Python green; frontend tsc + vitest green; `core always-on`
verified end-to-end (bounded, real SIGTERM graceful stop, interval rejection).
engine_state/always_on.lock is runtime state (gitignored, ADR-0146 pattern).
The /health endpoint was the only W-026 API route with no UI consumer.
Surface it as a liveness indicator (colored dot + label) in the status
footer, isolated from /runtime/status so a live server still reads
"Healthy" even when runtime status is degraded.
- types: HealthStatus matching the server's {"status":"ok"} payload
- client: fetchHealth() with a short 3s timeout for fast failure
- queries: useHealth() polling every 15s, retry disabled
- footer: HealthIndicator rendered in both normal and degraded branches;
any non-ok status fails safe to "Unhealthy"
- tests: healthy / errored / non-ok / checking / survives-runtime-failure
Follow-up to #749, which deliberately scoped TruncatedCell to the
non-virtualized columnar tables and left trace edges + single-column
selection rails as-is. Wire it into those too so every truncated data
cell in the workbench has the same full-content reveal (popover +
copy + modal-for-long).
Component:
- Add `align` prop ("start" | "end"). `end` hugs the right edge and
right-justifies the display, for right-aligned columns like the trace
`to_stage` cell. Defaults to "start" (unchanged).
Wiring:
- Trace propagation edges: from_stage / to_stage (to_stage align=end).
- Trace pipeline stage rail: stage label + summary.
- Trace turns rail: prompt + surface excerpts.
- Runs rail: session_id. Replay rail: prompt excerpt. Packs rail:
pack_id. CORE-Logos pack rail: pack_id. Logos alignment rail: edge
(rich source→target display, plain-text value for copy/reveal).
- Contemplation run rail: run_id; stage-evidence ids.
- Demos rail: title; scenario trace hash.
- Proposal detail: replay-equivalence hash, evidence hashes;
proposal chain provenance.
Two row elements that were real <button>s (trace stage rail,
contemplation run row) are converted to <div role="button"> with an
Enter/Space onKeyDown handler — the codebase's established selectable-row
pattern (ProposalTable, RowShell) — so nesting TruncatedCell's button
trigger is valid HTML rather than a button-in-button.
Intentionally left as-is (documented):
- RunsRoute TurnRefRow is an <a>/<Link> row; nesting an interactive
trigger inside an anchor is invalid HTML. The surface excerpt is
reachable by following the link.
- Digests everywhere keep DigestBadge (already copy + full-value title).
- Section headings (e.g. the trace stage-detail <h3>) are not data cells.
Evidence: workbench-ui `pnpm build` (tsc -b) green; full vitest suite
535 passed / 60 files, incl. a new align assertion on TruncatedCell.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Workbench grid tables truncate dense cells (proposal ids, source kinds,
case ids/reasons, lexicon/morphology rows, artifact paths) with `...`,
leaving the full value unreachable except via a native title tooltip.
Add a reusable `TruncatedCell` (design/components): keeps the compact
display, attaches one hover/focus-revealed trigger that opens an
accessible Radix popover with the complete value (selectable, scrollable)
plus one-click copy, and — for long/structured values — an "Open full
view" button into a dynamic Radix Dialog modal. One component delivers
both the lightweight reveal and the interactive modal.
The reveal trigger calls stopPropagation, so it never steals a row's
click/select behaviour — revealing a value and selecting the row stay
independent. Digests keep `DigestBadge` (already copy + full-value title).
Wired into the non-virtualized + contents grid tables: Proposal queue
(id, source), eval wrong=0 case ledger (case id, reason), CORE-Logos
lexicon/glosses/morphology rows, and proposal artifact paths. Trace
propagation edges (short, right-aligned stage names) and single-column
selection rails / detail cards keep current behaviour.
Evidence: workbench-ui `pnpm build` (tsc -b) green; full vitest suite
525 passed / 59 files, including 5 new TruncatedCell tests (display +
title, popover reveal + copy, modal-only-for-long, no row-click bubble).
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
The L10 heartbeat (chat/always_on, #747) holds CORE alive over uptime but had no
surface. This adds the read-only Lived Life route that renders the continuous-life
evidence: the heartbeat over uptime + the resume-as-same-life verdict — both halves
of "one continuous life" on one surface.
Persist-first, never recompute engine-owned values:
- chat/always_on: serialize_report + write_lived_life persist a run deterministically
(sorted keys) to engine_state/lived_life.json; run_continuous gains an opt-in
report_path so a real continuous-life run leaves its evidence where the workbench
reads it. closure_ceiling is persisted so the artifact is self-describing.
- workbench/lived_life.py: projection + a fail-closed validate() gate — the wrong=0
analogue for the continuity surface: closure_held/closure_observed/totals/converged
are each re-checked against the per-beat measurements, and resume_status is re-checked
against identity-vs-current-substrate, so a tampered artifact RAISES rather than
rendering a false claim (a beat lying about a breached ceiling, an inflated closure,
a miscounted total, a falsely-claimed resume).
- workbench/readers.lived_life(): reads the artifact, computes the resume verdict from
the persisted identity vs the canonical engine_identity_for_config (fail-soft ->
"unknown", like IdentityContinuity). Honest absence when no run has been persisted.
- GET /lived-life + LivedLife/LivedLifeHeartbeat schemas (snapshot regenerated; the
schema-drift gate proves every field is mirrored in src/types/api.ts).
The resume verdict made felt: would_resume / substrate_changed / unknown IS the L11
reboot guarantee (a reboot recomputes identity and refuses if it differs) — so the
surface shows this life wakes up as ITSELF, not a copy. The per-reboot lineage chain
stays owned by Runs > Identity (honest cross-link).
Frontend: /lived-life route (Evidence section, route count 15 -> 16). Renders the
headline (heartbeats, closure-held-by-construction, learned-while-idle, at-rest, resume
pill), the summary, the heartbeat timeline (closure flat below the ceiling — read, never
repaired), and the resume verdict. Honest absence + ADR-0162 route conformance
(loading/error/empty), fail-closed like Vault/Calibration.
Tests (non-vacuous): 12 backend (every tamper case raises; resume tracks identity vs
substrate; run_continuous(report_path) round-trips a readable+valid artifact) + 4
frontend render tests (recorded life renders; a breached beat shows the warning, never a
false "held"; a changed substrate shows would-refuse, never a false resume; absent shows
honest absence). All workbench Python (167) + affected vitest (167) + tsc + vite build
green; architectural invariants green.
engine_state/lived_life.json is runtime state (gitignored, ADR-0146 pattern).
A1 (substrate fix, not UI): en_collapse_anchors_v1 declared only love/peace/justice,
but the logos packs reference covenant_love/shalom/tzedek + heart/soul/breath/holy/time.
Declare all 8 with their real ADR-0073 provenance (source-concept names where they
exist) and recompute the lexicon checksum. Now every cross_lang.no_english_collapse
edge resolves to a real declared entry — grc_logos_cognition_v1 and he_core_cognition_v1
go from 7 invalid targets to 0 (verdict warning -> unknown; never CLEAR while holonomy
proof is absent). The geometry resolves; the check is NOT suppressed (the LG-1
relation/prefix carve-out stays removed). Test updated to assert real resolution;
invalid-detection branch still covered by the ghost-target fixture.
A2: Identity tab shows the real pack manifest (now fetched via /contents) instead of
an overview projection — the actual passport, data already present.
NOTE (curation follow-up): love (Greek agape collapse) and covenant_love (Hebrew chesed
collapse) may be intentionally distinct per-language anchors or duplicates of one
concept — left both declared rather than gamble on consolidating curated theology.
Backend 25 + frontend 520 tests pass; tsc + build clean.
Two UX wrinkles surfaced while exercising the CORE-Logos Studio:
- DAG viewport: tall, few-layer graphs (e.g. the 408x1890, 2-layer/55-node
alignment fan) were squished by viewBox-meet into an unreadable sliver. Render
at natural height (scale ~1) inside a bounded, scrollable box; small graphs are
unaffected (>= the caller height). Golden layout unchanged (layoutDag untouched).
- Click-to-copy: new shared useCopyToClipboard hook gives every copy affordance a
tooltip + transient confirmation. StatusFooter SHA now confirms 'Copied' (was a
silent copy that read as 'does nothing'); 'Read Only' is labeled a non-interactive
status (not a toggle); checkpoint-revision gains an explanatory tooltip.
DigestBadge + MetadataTable copy buttons route through the hook + gain titles.
Full workbench-ui vitest: 520 passed (+4), clean exit; tsc + build clean.
The trilingual resonance centerpiece, read-only over GET /logos/packs/{id}/alignment:
- deterministic DagViewer (he -> grc -> en longest-path) + selectable edge list;
alignmentToDag is a pure projection pinned by a golden-file layout test
- invalid alignment targets surfaced honestly (the undeclared en-collapse-*
anchors render a warning, never smoothed over)
- logos_alignment_edge evidence subject (logos_alignment_edge:<packId>/<edgeId>)
with RightInspector projection + EvidenceChainRail derivation
- holonomy stays absent: no tab, no proof card, no success element (guard test)
Full workbench-ui vitest: 516 passed, clean exit; build clean.
Read-only contents tabs over GET /logos/packs/{id}/contents:
- Lexicon (VirtualizedList + SearchInput + status/domain filters; dangling-link
flag cross-referenced from the safety report, never recomputed)
- Glosses (linked entry ids) and Morphology (operator chain rendered in schema
order — root/prefix/stem/suffix never re-sorted)
- three pack-scoped evidence subjects (logos_entry/gloss/morphology) with
logos_*:<packId>/<subId> address grammar, RightInspector projections, and
EvidenceChainRail derivations
- contents fetched eagerly on pack-select; /alignment stays deferred to LG-4
Full workbench-ui vitest: 506 passed, clean exit; build clean.
Read-only /logos frontend route (Wave M CORE-Logos read-only wave, LG-2):
- route registered via WORKBENCH_ROUTES + lazy ROUTE_ELEMENTS (Substrate section)
- Overview / Identity / Safety tabs over live /logos/packs, /{id}, /safety only
(no /contents or /alignment — those are LG-3 / LG-4)
- 10 Logos* TS interfaces mirrored; NOT_YET_MIRRORED shrunk back to empty
- SafetyVerdict UI badge + dump-enums.py source + enum-snapshot + coverage test
- logos_pack evidence subject (logos:<pack_id>) + round-trip
- bottom strip "proposal mode: none — read-only"; holonomy rendered as
missing_evidence (no tab, no proof card); invalid alignment targets surfaced
honestly in Safety; verdict never mapped to "clear" for warning/unknown
Depends on LG-1 (#737). No mutation endpoints, no engine math in the UI.
The workbench READMEs were frozen in the W-026..W-031 planning era. Bring them
current after the Wave 1 / R / M arc:
- docs/workbench/README.md (index): add the 5 unindexed docs (wave-1-evidence-
spine, wave-R-mastery-revamp, wave-M-worthiness, b4-leeway-producer-scope,
design-system); replace the false "Current Status" (which still pointed at the
superseded feat/w026 prototype) with the shipped state + a 14-route surface
table; mark the W-026..W-031 queue delivered; note Wave M complete (B+C+D+B4),
Phase E + parallel tracks remaining.
- workbench-ui/README.md: fix the stale one-line surface list and the "src/routes
placeholder components" description (routes are registry-driven in src/app/;
routes.ts is the single source). Left the dump-api-schemas.py reference intact
(it exists, distinct from dump-schemas.py).
- README.md (top level): the workbench section said "eleven routes" — now
fourteen, naming Tour, Contemplation, and Calibration and the per-turn
pipeline/field/leeway/bundle evidence.
Docs-only; no code or gate touched. UI-UX-GUIDE (the routes.docs.test-gated
route table) is unchanged and already at 14.
The "they'd want to use it" first-run surface: a curated narrative that makes
the engine's discipline legible to a newcomer — bring a claim from any model and
watch it get decided, refused, or replayed. Proposer authority ignored.
Honest by construction (the risk with a narrative surface is theater):
- workbench/tour.py determinism_tour() is a curated ordered narrative BOUND to
the real demo registry. Intro (provider-agnostic thesis) → three demo steps
(deductive entailment decides only on engine+oracle agreement; epistemic
truth-state refuses a wrong proposer; proof-carrying promotion ignores
proposer authority) → payoff (replay-to-same-hash + the citable evidence
bundle).
- Each demo step's what_this_proves / what_this_does_not_prove cards and the
demo title are PULLED FROM THE REAL DEMO SPEC — never re-authored — so the
tour can never claim more than the demo it points at.
- A step referencing a missing demo FAILS CLOSED (KeyError), not a dead link.
Tests assert both (cards == spec; phantom demo raises).
Backend: schemas.py DeterminismTour/TourStep; GET /tour. schema-snapshot regen.
Frontend: /tour route registered in the registry (Determinism section, 14
routes; nav/palette/guide counts updated); TourRoute — thesis hero + ordered
step cards with the honesty cards + links to the real demos / replay.
Validation: 140 workbench Python tests incl. tour drift guards; 472/472 frontend
incl. routes/routeConformance/routes.docs (guide↔registry), Shell+palette counts
(14), schemaDrift, and the tour UI test; pnpm build clean; git diff --check
clean. No serving-path imports.
The "they'd want to use it" deliverable: a turn's evidence exported as ONE
deterministic, content-addressed, citable artifact — composing the Phase-C
evidence (pipeline + field) with the trace and the calibration leeway verdict.
Backend (read-only, no engine execution):
- schemas.py: EvidenceBundle.
- workbench/evidence_bundle.py: build_evidence_bundle(entry) assembles a turn
journal entry into the bundle and computes bundle_digest. The digest
content-addresses the DETERMINISTIC cognitive evidence only — journal
position + wall-clock (turn_id, journal_digest, replay_reproducer,
generated_from) are carried for provenance but EXCLUDED, so the same turn
content reproduces the same digest regardless of journal position.
- api.py: GET /trace/{turn_id}/bundle. schema-snapshot.json regenerated.
The bundle carries a replay *reproducer* command (how to verify) rather than a
live-run replay, so the artifact itself stays deterministic — verification is
the consumer's step: re-run the prompt sealed, confirm trace_hash, recompute the
bundle, check the digest.
Frontend:
- types/api.ts EvidenceBundle; client/query hook; Trace route **Bundle** tab —
citable digest, "what this proves / does not prove" honesty note, the
reproducer, and a deterministic JSON download (Blob anchor, leak-safe).
Validation: 135 workbench Python tests incl. non-vacuous bundle guards (digest
reproducible, journal-position/wall-clock excluded, any evidence change flips
the digest, missing Phase-C evidence honest); 468/468 frontend incl. schemaDrift
+ the citable-download bundle test; pnpm build clean; git diff --check clean. No
serving-path imports.
C2-a surfaced contemplation runs but rendered each scene as a flat JSON dump —
outputs, not process. C2-b makes the loop legible: every scene is now a typed
stage in the canonical ADR-0172 arc.
Reader-first (no theater):
- schemas.py: ContemplationScene gains a typed loop projection — stage_role
(cold_attempt | engine_enrichment | engine_proposal | operator_ratifies |
grounded | other) plus the connective ids (proposal_id, candidate_id,
proposal_state, grounding_source). New ContemplationStageRole Literal.
- readers.py: _contemplation_scenes derives stage_role from the scene id
(closed set, "other" fallback) and pulls the connective ids out of the raw
detail. schema-snapshot.json regenerated.
UI:
- ContemplationRoute: the run detail now renders the arc as a staged process —
"attempt → enrich → propose → ratify → grounded" with named stage roles,
cold→grounded bookends (surfaces pulled out of before/after), the connective
ids as evidence, and the raw detail tucked into a collapsible (not lost).
Honest wrinkle (surfaced, not faked): the fixture proposals do NOT resolve in
the live proposal log (source_kinds exemplar_corpus/operator, none
contemplation), so the proposal id is shown as evidence but is intentionally
NOT a clickable cross-route link — a dead link would be theater. Live
Proposals/Calibration navigation is deferred to reader-verified linking once
real contemplation proposals reach the log.
Validation: 128 workbench Python tests + new reader-projection tests (canonical
arc → roles, unknown → "other", id extraction, detail preserved); 466/466
frontend incl. schemaDrift + the staged-process / no-dead-link test; pnpm build
clean; git diff --check clean. No serving-path imports.
Makes the CL(4,1) field geometry legible, honestly. The engine owns the field;
this surfaces ONLY the exact scalar invariants it computes — never the raw
multivector — so the workbench shows "this is the geometry, it's exact, it
can't fake coherence" without a decorative blob or any motion.
Persist-first (the C3 gating work was Python, not React): the honest scalars
were computed live per turn but discarded. Now captured per turn into the
journal so a read-only surface has real evidence.
Backend:
- workbench/field_evidence.py: FieldEvidence computed from the engine result —
exact versor_condition, field_valid (vs the 1e-6 ceiling), a content-
addressed field_digest (sha256 of the engine-canonical array bytes), and
cga_inner(before, after) as the exact transition value. Raw field bytes
never cross the boundary: only floats + digests. validate() is fail-closed —
field_valid can never disagree with versor_condition vs the ceiling (the
wrong=0 analogue for the geometry). No engine math re-implemented
(versor_condition / cga_inner imported from algebra; bytes via array_codec).
- workbench/schemas.py: FieldEvidence dataclass; field_evidence on
ChatTurnResult + TurnJournalEntrySchema. schema-snapshot.json regenerated.
- workbench/journal.py + api.py: persisted at from_chat_turn; first-class read
endpoint GET /trace/{turn_id}/field (trace facet, consistent with /pipeline).
- workbench/replay.py: field_evidence classified CRITICAL — replay now also
proves field determinism (digest + scalars must match on re-execution).
Frontend:
- types/api.ts FieldEvidence + field_evidence passthrough; client/query hook;
FieldInvariantCard (measured value vs ceiling, cga_inner transition, digests;
honest missing_evidence; no blob, no motion); Trace route Field tab.
Honest-empty for pre-widening journal rows (missing_evidence). Deferred:
cross-turn field-coherence trends, session-level field persistence.
Validation: 138 workbench/practice Python tests (incl. non-vacuous field guards
+ replay field-determinism); 465/465 frontend incl. schemaDrift; pnpm build
clean; git diff --check clean. No generate.derivation / reliability_gate /
stream / field.propagate / vault.store imports.
Lands the Phase C "make cognition legible" slice plus Phase A residue, all
backend-reader-first over real engine data (no theater, read-only doctrine
intact, zero serving-path imports).
C1-a — Cognitive pipeline record (persistence-first, per #729 worthiness edit):
- workbench/pipeline_record.py: curated CognitivePipelineRecord over the real
CognitiveTurnResult (input → intent → proposition_graph → articulation_target
→ realizer → walk_telemetry → trace_hash). Raw field multivectors are
DELIBERATELY excluded; _assert_no_raw_field_payload recursively rejects raw
field keys, and validate_pipeline_record fails closed on missing/duplicate
stages, non-recorded status, or dangling edges — the UI can never receive a
partial record that claims to be complete.
- test_workbench_pipeline_record.py: non-vacuous guards — missing stage,
monkeypatched new required stage, and injected raw {"F": [...]} each raise.
C2-a — Contemplation as a process: /contemplation route over real persisted
contemplation/runs/*.json (glob reader; honest-empty when absent).
C4-a — Identity continuity (L10/L11): RunDetail.identity_continuity + Runs
Identity tab, sourced from the real core.engine_identity (engine_identity /
parent_engine_identity lineage relation, re-derived to verify).
Demo Theater: renders backend-owned proof-promotion + entailment DAGs.
Phase A residue: density preference wired end-to-end (settings → shell → tokens);
cross-route consistency touch-ups.
Infra: local API CORS now echoes only validated 127.0.0.1/localhost origins
(hostname-checked, not arbitrary reflection) so Vite fallback ports work.
Route chunk-split keeps the build warning-free.
Cleanup: corrected the stale ADR-0175 practice-lane assertions (build_report is
6 correct / 0 wrong / 44 refused after the current serving lane; wrong=0 held)
and the two registry-derived count tests (LeftNav + CommandPalette 12 → 13 for
the new Contemplation route).
Docs: runtime_contracts.md (pipeline-record contract), UI-UX-GUIDE,
api-contract-v1, data-shapes-v1, wave-M-worthiness, phase-a-residue-ledger.
Validation: 106 workbench/practice Python tests green (incl. wrong=0 lane +
pipeline-record fail-closed guards); 459/459 frontend; pnpm build clean;
git diff --check clean. No generate.derivation / reliability_gate / stream /
field.propagate / vault.store imports.
Completes the Wave M B3.5 consolidation slice (b–e), built on #728.
B3.5-b — calibration as a first-class evidence subject (`calibration_class`,
address `calibration:<class_name>`): RightInspector projection + Evidence
Chain Rail semantics (serving-discipline evidence, not runtime truth).
B3.5-c / B4a — nullable `LeewayEvidence` read model threaded through turn,
replay, cognition-proposal, and math-proposal surfaces, with a shared
absence-honest card. B4 is gated correctly: the tuple exists in typed data but
no producer populates it, so the card renders absence (verified: no non-null
producer in workbench/core/chat).
B3.5-d/e — UI-UX-GUIDE.md, b4-leeway-feasibility-gate.md, phase-a-residue-ledger.md.
Practice artifact — earn-it-for-real (runner-reproducible). The committed
`report.json` (additive earns PROPOSE @0.861, 95/5/50) is now emitted by a
deterministic runner rather than copied from the queue. `propose_runner`
gains `regenerate_practice_artifacts()`, which runs ONE sealed `resolve_pooled`
practice pass and writes BOTH report.json (the per-class ledger the calibration
reader consumes) and ratification_queue.json — two projections of one ledger,
coherent by construction and byte-reproducible. `runner.main()` delegates to
it (lazy import, no cycle), so both entry points produce the identical pair.
This closes the gap where a hand-copied report.json agreed with the queue but
no runner produced it. `resolve_pooled` is the aggressive sealed PROPOSE-regime
scorer (proposal-only/HITL, unsafe for serving, legitimate for
attempt-and-eliminate); wrong=5 is the sealed-practice learning signal, NOT the
serving wrong=0. No serving/derivation/reliability_gate source touched; the
practice lane is not in the serving-frozen SHA gate.
Validated:
- python -m pytest tests/test_workbench_{calibration,journal,replay,schemas}.py -> 31 passed
- python -m pytest tests/ -k "workbench or propose or learning_arena or practice"
-> 190 passed (3 failing tests in test_adr_0175_phase2_practice_lane.py are
PRE-EXISTING reds on clean origin/main: stale 4/0/46 assertions on build_report,
which this change does not touch)
- report.json + ratification_queue.json: deterministic (run1==run2) and
reproduced byte-identically by both `python -m ...runner` and `...propose_runner`
- pnpm build green; 144 UI tests across calibration/leeway/evidence/replay/
doctrine-gates/routes-docs-drift all pass
Adds `workbench-ui/src/app/routes.ts` as the single source of truth for
navigation routes. App, LeftNav, the command palette, ⌘-digit shortcuts,
and the landing-route preference now all derive from `WORKBENCH_ROUTES`.
Fixes the live palette-drift bug: the command palette's hand-maintained
NAV_COMMANDS listed 10 routes and silently dropped Demos and Calibration,
so they were unreachable via ⌘K. The landing-route tuple was likewise
missing Replay and Calibration. Deriving every list from one registry
makes that drift structurally impossible.
- routes.ts carries a `section` field for grouped wayfinding nav
(Converse · Cognition · Determinism · Evidence · Discipline ·
Substrate · Settings) — a display skin only; one workbench, one
address space, one Evidence Chain Rail.
- App is data-driven via ROUTE_ELEMENTS (route id → element); a registry
route without an element fails routes.test rather than rendering undefined.
- Honest keyboard model: ten routes pin ⌘1–0; Demos and Calibration are
palette-only (no chord advertised).
- routes.test.tsx guards the contract: element-map parity, unique
ids/paths/digits, landing-route coverage (Replay + Calibration), and
palette reachability of every command-visible route.
Verified: pnpm build green; routes/Shell/CommandPalette/routeConformance/
prefs/Settings + hexScan/schemaDrift/enumCoverage suites all pass (108 tests).
An always-present invariant in the TopBar chrome: the live serving triplet
(N correct · N refused · 0 wrong) aggregated across lanes from
/serving/metrics, the wrong count load-bearing — 0 in the verified token,
>0 in the contradicted token. It is a MIRROR, never a hard-coded zero:
renders a non-zero wrong honestly, and an honest 'metrics unavailable' when
the reports can't be read. Links to the Calibration route (the discipline
behind the number).
Tests: aggregate triplet + 0-wrong, non-zero-wrong-honest (not hard-coded),
metrics-unavailable-not-fake-zero. (Adds fetchServingMetrics/useServingMetrics
locally; unioned with B2 on rebase.)
Consumes the B1 readers (#724). The route where you SEE 'the engine earns
the right to guess' (ADR-0175).
- list (failures-first, server-ordered): per class a reliability bar
(Wilson floor vs the PROPOSE/SERVE θ markers — a class earns the right
when its fill crosses the marker), correct/refused/wrong counts (wrong in
the contradicted token), and a verdict pill (earned SERVE / earned
PROPOSE / not yet licensed)
- serving strip: the discipline's RESULT — live correct/refused/wrong per
lane, wrong=0 in the verified token, with source DigestBadges
- detail (Panel + TabBar): Counts / License math / Raw. The License-math
tab shows the honest derivation (measured ≥ θ → licensed) and states
the numbers come from core.reliability_gate, not the workbench
- fail-closed: an empty/absent arena ledger (501 evidence_unavailable)
renders the honest absence card pointing at the practice lane, not an
error
- nav entry (12th) + routeConformance row + Shell nav assertion updated
Token-only (hexScan green); VirtualizedList + j/k; full vitest 398 green.
First Wave M / Phase B piece (GATING): read-only backend that makes the
calibrated-learning / serving-discipline loop inspectable — 'the engine
earns the right to guess', ADR-0175.
The workbench computes NONE of these numbers:
- GET /calibration/classes — per-class gold-tether view from the persisted
practice arena ledger (evals/gsm8k_math/practice/v1/report.json per_class).
Each class's reliability_floor is the engine's own one-sided Wilson
conservative_floor (via ClassTally.reliability); PROPOSE (θ=0.85) / SERVE
(θ=0.99) license verdicts come from core.reliability_gate.license_for.
Failures-first ordering. A test proves the reader's floor equals a direct
conservative_floor() call — no re-implementation.
- GET /serving/metrics — the live correct/refused/wrong counts read unchanged
from the committed train_sample + holdout_dev report.json (currently
4/46/0 and 5/495/0 — wrong=0). Never re-runs a lane.
Honest current state: the committed practice ledger's three classes
(additive/divisive/multiplicative) are all below N_MIN=10, so none has
earned a license yet — the reader shows exactly that, no fake green light.
- workbench/calibration.py: pure readers; imports core.reliability_gate;
EvidenceUnavailableError -> 501 (fail-closed) when the artifact is absent.
- schemas + TS mirrors (CalibrationClass, ServingMetrics); both snapshots
regenerated (deterministic); both drift gates pass.
- trust boundary: read-only over committed artifacts + engine-owned
derivation; no execution, no mutation, no license ever changed.
Verified: 30 Python tests (incl. the no-reimplementation proof + fail-closed),
390 vitest, both schema drift gates, snapshots deterministic.
First R3 (Theater) piece. Reworks the stale artifact-keyed ReplayRoute into
the turn-keyed hero over the #716 sealed-replay backend, and fully retires
the dead W-026 artifact-keyed machinery on both sides (the NOT_YET_MIRRORED
comment anticipated this).
The hero makes determinism *felt*: pick a journaled turn, CORE re-executes
its prompt in a sealed fresh runtime, and the result renders as a hash
verdict (≡ equivalent / ≠ diverged) + the original/replay DigestBadges + a
leaf diff (critical weighted above informational, each with a ≠ glyph). The
'What this proves' card surfaces comparison_basis + origin_state and states
the honest limit: a divergence means nondeterminism OR origin-state
influence, never rendered on its own as a determinism failure.
Retirement (verified zero serving uses):
- Python schemas.py: removed ReplayComparison/ReplayDivergence/ReplayStatus/
ReplayDivergenceSeverity; scripts/dump-enums.py drops the two replay enums;
both snapshots regenerated (deterministic: dump == committed)
- TS: removed ReplayComparison/ReplayDivergence/ReplayDivergenceSeverity;
added TurnReplayComparison/TurnReplayDivergence (+ basis/origin/severity
unions); NOT_YET_MIRRORED now empty (every engine schema mirrored)
- badges: removed ReplayStatusBadge + ReplayDivergenceSeverityBadge +
meta/enums + their enumCoverage cases (hero renders severity inline)
- api: fetchReplayComparison/useReplayComparison -> fetchTurnReplay/
useTurnReplay (/replay/<turnId>)
- deleted ArtifactList, ReplayComparisonPanel, ReplayDiffViewer,
ReplayMetadataTable, old replay.test.tsx
- App route /replay/:artifactId? -> /replay/:turnId?; conformance row
turn-keyed (loading 'Loading turns...', empty 'No turns recorded yet.')
Tests: ReplayRoute.test.tsx (equivalence hero + honesty card, diverged
critical leaf, informational-only label, replace-mode select, j/k spine).
Full vitest 358 green; workbench Python 34 green; both snapshots deterministic.
Follow-up flagged (not in this PR to keep it focused): the artifact query
hooks (useArtifacts/useArtifactDetail/fetchArtifacts*) are now orphaned but
entangled with the still-live artifact EvidenceSubject kind — a separate
cleanup once an Artifacts route or that subject's fate is decided.
Brief: docs/handoff/wave-R3-briefs-2026-06-13.md (all four R3 pieces).
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.
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).
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).
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.
Replaces the W-026 501 stub. Re-executes a journaled prompt in a sealed
fresh runtime (ChatRuntime(no_load_state=True): no checkpoint load, no
checkpoint write, no proposal lineage) and compares the envelope
leaf-by-leaf against the recorded TurnJournalEntry.
Design correction vs the scoping doc (amended in-doc): journaled turns
each ran in a fresh ChatRuntime(), never one continuous session, so
genesis-PREFIX replay would manufacture spurious divergence; shipped
basis is sealed_fresh_runtime_single_turn (O(1)). origin_state:
"unrecorded" — the journal does not record whether the original turn's
runtime loaded a checkpoint, so divergence is reported as nondeterminism
OR origin-state influence, never disambiguated.
- workbench/replay.py: pure comparison + injected executor; every
TurnJournalEntry field classified critical/informational exactly once
(exhaustiveness enforced by test)
- api.py: route wiring under _CHAT_TURN_LOCK; runtime failure -> 500
runtime_unavailable, no comparison may be fabricated
- schemas: additive TurnReplayComparison/TurnReplayDivergence (W-026
artifact-keyed pair retires with the frontend Replay Moment PR)
- tests: 10 obligations incl. tamper-prompt, tamper-leaf precision,
no-execution-no-comparison, no-trace (journal + engine_state bytes),
wall-clock tolerance, sealed-construction proof
- snapshot regenerated; NOT_YET_MIRRORED debt entries for the two new
classes (mirrors land with the frontend PR)
- api-contract-v1.md § Replay rewritten for the turn-keyed shape