Add catalog-owned logical relationships and runtime snapshots, extend the database-management UI and validation coverage, and document the updated operational workflow.
Keep active sensitive-generation status in a tooltip and indicator, and update the layout E2E to follow the history action in its new database-scoped location.
Keep upstream identity visible while limiting logout to local auth. Inject the restore privilege gate so the deterministic core tests do not depend on the host OS, and confine descriptor-backed projection tests to Linux. Accept the real remaining Pi timeout budget instead of an exact millisecond.
Backend: GET /health/dwh (unauthenticated) calls tht db ping with a 5s
timeout. Frontend: checkDwhHealth() races a 5s timer against the fetch;
on failure a non-dismissable Dialog with Retry appears immediately.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Embedded in the Omics Portal the SPA sits to the right of the portal sidebar,
but dialogs used `fixed left-1/2` — centered on the whole browser window, so
gates rendered visibly off-center (overlapping the sidebar).
- AppShell publishes the app area's horizontal geometry (--app-area-left/width)
as CSS vars on <html> via ResizeObserver + window resize.
- DialogContent centers on those vars (falls back to the viewport when the app
is standalone, where the two coincide).
- ArtifactGateWidget: narrower — cap at 52rem instead of 70vw.
- tools/replay: resume now returns {id, alreadyActive} JSON — the post-f979ada
frontend reads result.alreadyActive and a bare 204 broke replay resume.
Verified on the replay harness: with a simulated 300px portal sidebar the gate
centers on the app area (center 790 = 300 + 980/2); standalone unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Give live feedback that something is happening the moment a session starts
processing: the red stop dot in the composer beats with a soft heartbeat + halo
while the harness is actively working (running — not waiting at a gate or
finalized), and settles the instant the model hands control back to the reviewer
or the session ends. Ring/scale only (no layout properties animated); honors
prefers-reduced-motion.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New session now refuses to spawn a Pi runtime that would only die in bootstrap
retrieval when the DWH/vector host is unreachable (e.g. a dropped VPN). Before
`session new`, POST /sessions probes the DWH via `tht db ping`; if it is down it
returns 503 {code:"dwh_unreachable"} with a clear message and creates nothing.
- Gated behind the THT_DWH_PRECHECK flag (default off), enabled only by the local
dev launcher (run-stack.sh) — containers/CI never pay the probe, and existing
tests that don't set it are unaffected.
- ThtRunner.dbPing() runs `tht db ping` with a 10s timeout (run() gains an optional
timeout that SIGKILLs a hung child).
- Frontend: apiFetch throws a typed ApiError (status + parsed payload); the new-
session composer shows the specific alert on `dwh_unreachable` instead of the
generic retry hint, keeping the question for retry.
Verified live on an isolated backend (precheck on + broken DWH host → 503
dwh_unreachable, no session created) and via unit tests (backend 228, frontend 308).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Opening an in-progress session that has a live Pi runtime now reconnects to
its pending gate instead of the empty landing screen that read as "stopped".
Cold/completed sessions keep the read-only documents panel with its explicit
Resume, so a mere click never spawns a runtime. Backend GET /sessions now
reports a per-session `active` flag (live runtime bound) to drive this.
Also:
- "New session" now closes any open session detail panel (left box).
- The model-activity separator can be dragged to a full 50/50 split
(was capped at 576px); central-min still guards narrow viewports.
Test fixes uncovered along the way:
- Node 25 ships an experimental global localStorage that shadows jsdom's and
lacks clear(), failing every jsdom test at setup; install a spec-compliant
in-memory Storage (feature-detected, inert on CI/LTS).
- Fix 4 pre-existing session-mgmt tests that used an ambiguous getByText for a
session shown in both nav and header; target the nav item by test id.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The central column was top-anchored with no fixed header, so the user's
question was never shown while the model worked and the activity/gate
content drifted upward. Render the active session's question as a sticky,
always-present first row of the central area so it stays evident and
anchored at the top of the form.
Co-Authored-By: Claude Opus 4.8 <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>
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.
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>
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>
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>