The original doc only covered search_similar; the write functions also need
solved_question in their kind whitelist for the memory table.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The FE derived 'working' purely as activeSession && !pendingWidget, so the
final workflow turn — the only one that ends without a follow-up gate —
left the spinner on forever (observed live: 21592s after F8 approve).
- SessionBridge maps Pi's agent_end -> SSE system_event {event: agent_end}
- PiProcessManager notifies the client (info error + synthetic agent_end)
when the child dies unexpectedly; expected teardowns stay silent
- sessionStore tracks agentActive (on: user entry/text_delta/ui_request,
off: agent_end); AppShell working now requires it; resume sets it
optimistically
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Extract shared statusBadgeClass() helper (src/viewers/statusBadge.ts) so
SqlViewer, CteResultViewer and PhaseSummaryViewer can't drift: ok/success/
passed/promoted render green (--success), warn renders amber (--warning),
error/failed stay destructive red, everything else stays neutral outline.
Previously CteResultViewer/PhaseSummaryViewer mapped "ok"/"promoted" to the
default badge variant, which is bg-primary (GSD red) — success states
rendered red.
- enrich.js: buildCteResultV2 now falls back preview.rows to [] instead of
null when last_test.preview_rows is missing (pre-upgrade ok records), and
CteResultViewer reads result.preview?.rows?.length with a null-safe
fallback so it degrades to the "No preview rows" empty state instead of
crashing.
- PhaseSummaryViewer: section.items is optional (model-authored sections can
be prose-only); render (section.items ?? []) instead of crashing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Regenerated demo fixture output with augment-schema-linking.mjs +
augment-review-gates.mjs applied, so the F6 cte_plan gate, CTE 1 cte_result
gate, and Fase 5 phase-summary gate carry v2 schema_version payloads and
render via CtePlanViewer/CteResultViewer/PhaseSummaryViewer in the replay
server. Regenerable; committed so the replay server serves it without a
build step.
Add cte-plan-fixture.json, cte-result-fixture.json, and
phase-summary-fixture.json — realistic Italian-language payloads for the
cardiology DWH replay session, conforming exactly to contracts.md (A/B/C).
Add augment-review-gates.mjs (pattern of augment-schema-linking.mjs): finds
the F6 cte_plan gate, first CTE 1 cte_result gate, and Fase 5 phase-summary
gate by title regex and swaps in the v2 artifact.data, so CtePlanViewer,
CteResultViewer, and PhaseSummaryViewer render in the offline replay.
Idempotent; exits 1 if an expected gate is missing.
Document the augment scripts and re-extract note in tools/replay/README.md.
Add CtePlanViewer, CteResultViewer, and PhaseSummaryViewer to render the
structured v2 payloads (schema_version: 2) the harness now emits for
artifact-gate widgets, per contracts.md. Extract PreviewGrid from
ResultsPanel as a reusable AG Grid component shared by CteResultViewer.
ArtifactView dispatches to the new viewers on a schema_version/shape
guard, falling back to the existing legacy renderers unchanged for
older sessions and replay fixtures.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The tht-gate.js reviewer_confirm now builds structured v2 artifacts before the
widget so the reviewer approves gate-derived data, not raw model text:
- new pure modules gate/artifact-contracts.js (soft validators, {ok,errors},
legacy-passthrough) and gate/enrich.js (index/description enrichment,
buildCteResultV2 fusing thin model data with `tht cte info`, phase enrichment)
- cte_plan v2: validate + enrich + persist via `tht cte plan --name … --doc -`
(names derived from data.ctes[]); legacy `names` param kept as fallback
- cte_result v2: rebuild from `tht cte next`/`tht cte info` (sql + preview from
the persisted test record); null/error last_test -> actionable textResult
- phase v2: soft-validate + fill phase from meta + catalog descriptions
- prepareReviewerArguments coerces artifact.data too (GLM double-stringify);
legacy markdown strings pass through unchanged
- SKILL.md: Phase 6 cte_plan payload A + thin cte_result guidance; Discipline 6
payload C example; Discipline 7 reworded for gate-rebuilt cte_result
Legacy (non-v2) paths unchanged. TypeBox stays Type.Any() for artifact.data;
validation is soft (textResult) so models self-correct instead of looping.
All 102 gate JS tests green.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
WS1 of review-gates-v2: gives the JS gate (WS2) deterministic data to build the
cte_result v2 payload.
- CteTestRecord gains optional preview_rows (JSON-coerced, truncated cells);
test_cmd populates it from the bounded result rows.
- New read-only `tht cte info <name> --session <id> [--json]`: plan
index/total, persisted .sql, cte_plan_doc.json entry (if any), last
CteTestRecord. Exits 1 with a clean stderr message on missing
session/plan/name/sql.
- `tht cte plan --doc -` validates a chain-doc JSON (ctes[].name must match
--name, same order) and writes it to cte_plan_doc.json; cte_plan.json stays
a plain list[str] (load-bearing for tht.phase.next_cte). --doc is optional.
Regenerated demo fixture output of extract.mjs (WorkflowBar phase tags) with the
F4 gate-7 augment applied (tools/replay/augment-schema-linking.mjs, on the F4
branch). Regenerable; committed so the replay server serves it without a build step.
- ArtifactGateWidget: gate modal 90%→70% of viewport, larger heading, more padding.
- ArtifactView StructuredValue: depth-aware hierarchy (top-level hairline dividers,
warm bg-muted card surfaces + shadow for object-array items).
- index.css: .thot-label micro-labels switched to the mono register (SF Mono) with
tightened tracking, distinguishing labels/meta from sans body prose.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Wide tables (e.g. 116 columns, 3 suggested) made curation a scroll hunt. Now the
modal lists suggested columns first (stable sort on the descriptor flag, so
toggling never reorders rows) and adds a filter box matching name + description,
with a "shown/total" count. Selection stays keyed by column name, so the response
column order is unchanged (catalog order). Applies to read-only (excluded) tables too.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bite-sized TDD tasks: schema-linking types + columns modal, gate widget with
staged per-table column selection, registry/summary wiring, and a replay
fixture generated from the real physical.yaml catalog. Offline-verifiable in
the replay server; harness wiring is Plan 2.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reviewer curates, per promoted table, which columns to use over the full
catalog list (suggested pre-selected + bold); selection persists into
schema_linking.json + the decision ledger and softly guides SQL generation
(Option 1). Dedicated structured schema-linking gate widget (Approach A),
staged commit, inline gate + single columns modal. Hard SQL enforcement is
an explicit follow-up.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Improves how reviewer gates render their content, and adds two small
hooks that make the frontend cooperate with the standalone replay server
(also useful as generic primitives).
Gate body rendering:
- IntroBody: gate intros now render paragraphs plus real bullet lists
with a hanging indent and emphasised bullet lead term, filling the
card width (no narrow measure cap). Backed by introFormat (parser) +
IntroBody (renderer), with tests.
- GateArtifactBody: artifact-review gates whose artifact is a bare file
reference (e.g. {kind:"phase", data:{file:"question.md"}}) now fetch
the referenced session document and render its full content, instead
of showing the file reference as text. Falls back to ArtifactView for
inline-content artifacts and unmapped files. Backed by gateArtifact
(resolver) with tests. ArtifactGateWidget passes sessionId through.
- SelectWidget renders intros via IntroBody.
Replay-friendly session exit:
- AppShell: a system_event {event:"session_exit"} now triggers
stopSession(), returning to the landing view. Generic primitive (the
real backend can emit it too); used by the replay server's "Esci".
- SessionMenu: the ⋮ trigger is now always visible (was opacity-0 /
group-hover only), so Resume is reachable without hovering.
Verified offline against the replay server (no VPN needed); tsc clean.
Reproduces the real reviewer UI for any recorded session, with no VPN/Pi/
Python/DWH. The server (node:http, zero deps) serves the built SPA and a
tiny SSE/REST shim that re-emits the reviewer gates captured in a Pi
transcript, in their original order, with the reviewer's real 3-Jul
choices shown as comparison badges.
- tools/replay/extract.mjs: extracts gates from one or many transcripts
(session-id, file, or directory). Handles sessions split across resume
re-entries by sorting on message timestamp and dropping unanswered
gates. Reads the question from session_manifest.yaml, resolving the
sessions dir from any workspace yaml (no hardcoded paths).
- tools/replay/server.mjs: same-origin :5333. SSE streams gates; POST
/response advances the cursor and pushes info badges (scelta reale).
POST /resume and the final "Ripeti/Esci" widget close the SSE so the
browser EventSource reconnects (cursor resets, gate 1 re-emitted) — the
replay is re-runnable any number of times. GET /sessions/:id/documents
reads the real session files so GateArtifactBody resolves file-reference
artifacts. Exit emits system_event {event:"session_exit"} to return to
the landing.
- scripts/replay.sh: launcher (extract / build / run / all).
- tools/replay/README.md: data flow, commands, fidelity notes.
- .gitignore: ignore tools/replay/web/ (built artifact, like dist/).
A render error in one viewer or gate widget unmounted the whole React root
(white screen). Add a reusable ErrorBoundary (class component, no new dep) with
resetKeys + an on-brand fallback, and wire it at two surfaces:
- WidgetHost: isolates the gate widget (reset on descriptor id) so a malformed
gate payload no longer blanks the conversation
- SessionDocumentsPanel: wraps each document (reset on doc key/content) so one
crashing viewer degrades only its section; siblings and the panel survive
The observed crash: a schema-linking doc that parses but lacks `candidates`
makes SchemaLinkingViewer throw. Verified live via Playwright against the mock
backend. TDD throughout; tsc clean, 123/123 vitest (+8 new tests).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- present() = presentBlockingWidget: transport + no-limbo loop + response
validation; both branches share validateUiResponse; the id invariant must be
validated explicitly in the TUI branch (emitAndWait no longer runs there)
- TUI renderer uses numbered options + index parsing, never label mapping
(frontend already answers with ids); artifact-gate editor is viewer-only,
returned content ignored
- guards resolved: both TUI-only notices now ctx.mode === "tui"
- interactive-render.js as two layers (renderTuiDescriptor +
validateSyntheticResponse); add negative test cases + a real TUI smoke
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The "respond with gate widgets; use '!' for free text" notice is terminal-
flavored. Like the reLoop Esc guard, it used ctx.hasUI, which on pi >=0.80 is
true in RPC too, so it leaked to the browser. Now guarded on ctx.mode === "tui".
The plain text is still swallowed by the `handled` return in all modes; only the
warning is dropped in RPC. No functional ctx.hasUI guard remains in the gate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fix six GUI defects, verified live against the real stack:
- NavSessions: mark the in-progress (active) session in yellow (warning
token) — both its status dot and its row background.
- SteerInput: the composer is now an auto-growing textarea that wraps and
grows vertically (caps at 160px, then scrolls); Enter sends, Shift+Enter
inserts a newline.
- WorkflowBar: show a synthetic title of the current phase under the 8 dots
from static EN/IT strings (no LLM); English is displayed to match the chrome.
- CentralStatus: reformat the 5-line system-message tail as a structured,
monospace list with per-line markers and an emphasized last line.
- AppShell + CentralStatus: move the left Model-activity panel toggle off the
working spinner (now a pure status indicator) onto a dedicated arrow button
(→ opens, ← closes).
- gate: rename the phase-confirm button "Conferma e prosegui" → "Salva e
procedi" (builders.js + its L1 test).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>