The embedded mode sets VITE_BACKEND_URL=/datamart-builder/api (the nginx
proxy path), but the runtime-config policy only allowed /api. This caused
resolveBackendUrl() to throw at app startup, resulting in a blank SPA.
Add /datamart-builder/api and /datamart-builder/api/ to relativeBases and
test cases.
Unisce gli internals di Codex (secret-bundle, provider-credentials, auth upstream,
security hardening, CI multiarch) mantenendo le fix portal-specific:
- backend: configPath da THT_CONFIG (fix sessioni) + dataRoot di Codex; authMode 'upstream'
- Docker/compose: TENUTO il mio (verificato live: omics_network+alias, env_file, pi npm-g)
perche' il compose/Dockerfile/entrypoint di Codex sono accoppiati al suo modello
secret-bundle (tht doctor inesistente, secret-policy.sh). Adottabile in futuro.
- config.test.ts: preso Codex (superset)
Verificato: tsc clean, 132/132 vitest.
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>
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>
- 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>
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.
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>
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>
Left panels 30vw / central 55% / right rail 15vw, minimal padding. Model-activity
log and documents render via .thot-prose; decisions become hairline rows with
semantic type chips. Resume switches the view optimistically (instant feedback).
The Stop button now asks for confirmation before interrupting.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ArtifactView's fallback (StructuredValue) turns any object/array into labelled
sections, bullet lists and paragraphs (strings as markdown). Verified against the
real F1 gate (kind:"text", {recap, chiarimenti_risolti[], ...}). MarkdownView
uses the real .thot-prose class (Tailwind Typography is not installed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Titles + intro read as a clear prompt (comfortable line length), options as
spaced choice rows, Other/Back/Exit as discreet secondary controls under a
divider. Multiselect gets a prominent Select all/Deselect all at the head of the
list with a live count.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Timer now accumulates only while the harness works, pausing while a gate awaits
the reviewer and freezing when finalized (keyed per session, in-memory). Resume
now colours the re-entry phase immediately from the manifest's phase (1..8),
mirroring the F1 paint for new questions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
erDiagram reads data_type/type from the candidate (top-level or signals),
falling back to the generic 'col' token Mermaid needs — real types render as
soon as schema_linking.json carries them. ArtifactView gains a defensive
{content} fallback for sql-kind artifacts, verified against real session shapes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Backlog T2 from the reviewer-gate-ux review: the Send button on the reserved
"Other — specify" textarea emitted control:freetext with an empty payload when
clicked on an empty/whitespace field. Guard with disabled={!text.trim()}.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- WorkflowBar: center the phase pills (add justify-center).
- tht-gate.js: the gate was sending the descriptive phase name (e.g.
"chiarimento") instead of the workflow.yaml id ("F1") in the widget's
`phase` field, so the frontend's F1..F8 match never hit and pills never
lit up. Rename phaseName -> phaseId, return p.id.
- CentralStatus: expand the live "working" tail from a single truncated
line to up to 5 lines (line-clamp-5 safety net for unbroken paragraphs).
- ModelActivityPanel: render the streamed tail through react-markdown +
remark-gfm instead of raw per-line <p> tags, so emphasis/headings render
and blank-line paragraph breaks (previously stripped) are preserved;
tail-cut now operates on paragraphs instead of physical lines.
Updated tests accordingly (WorkflowBar/CentralStatus/ModelActivityPanel/
AppShell.session-mgmt); full frontend suite (98/98) + harness gate node
tests (34/34) + tsc -b pass.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
A1 — SessionMenu gains a Resume item, gated to status!=="finalized" && !archived
(matching the backend's 409 read-only guard), wired in AppShell to doResume ->
POST /sessions/:id/resume. SessionMenu.test.tsx (3 tests); frontend 96/96, tsc clean.
A2 — diagnosis-first clean-room repro driving `pi --mode rpc` with the backend's
exact resume handshake shows the cold-start stall NO LONGER reproduces on pi
0.79.4 (8/8 chained into `tht session show` + `read SKILL.md` in-turn, fresh and
partway sessions). The earlier narrate-and-stop predates the pi upgrade.
Defense-in-depth anyway: RIPRENDI_KICKOFF hardened to force the in-turn tool call
(gate_resume_kickoff.test.js + live regression 2/2). Gate JS 34/34.
PROJECT_STATE open-item #1 (resume stall) flipped to RESOLVED; cross-model resume
robustness folded into workstream G.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
B — WorkflowBar renders F1..F8 as colored ring-dots (no phase-name text):
green=done, amber=running (subtle pulse), red=error, gray=pending; green
connectors lead the active dot, each dot carries data-state. Lightweight
error signal: sessionStore gains `phaseError`, set when an info event has
level=error during the active phase, cleared on the next ui_request.
C — denser single-line session rows (inline status dot + name, py-1), a
3-level type hierarchy (L1 SESSIONS / L2 section+group headers / L3 names),
and the "No group" label removed (ungrouped render after the last group,
guarded so the empty-state still teaches when there are zero groups).
Live-verified with Playwright (all four dot states, sidebar hierarchy, and
E's deferred activity-panel check). Frontend 93/93, tsc -b clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>