Commit Graph
62 Commits
Author SHA1 Message Date
marcopanandClaude Opus 4.8 e4ada07dab feat(viewer): erDiagram shows real column types when present; ArtifactView accepts a {content} sql wrapper
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>
2026-07-03 14:13:02 +02:00
marcopan 3f852fd817 feat(workflow-bar): total elapsed timer after the phase circles 2026-07-03 13:26:25 +02:00
marcopan e630ac98f9 feat(workflow-bar): optimistic F1 at start; all-green when session finalized 2026-07-03 13:22:09 +02:00
marcopan 37b3eb45e9 feat(gate): render reviewer artifacts in a 90% modal (fix empty artifact.data gate) 2026-07-03 13:17:54 +02:00
marcopan 0975a8a72b feat(viewer): ArtifactView routes artifact.data by kind (schema/sql/cte/question) 2026-07-03 13:13:51 +02:00
marcopan 893e9e4059 feat(viewer): schema linking renders a Mermaid erDiagram (tables + relations) 2026-07-03 13:09:09 +02:00
marcopanandClaude Opus 4.8 25ab37d0fe fix(frontend): disable 'Altro' Send until non-whitespace text (no empty freetext)
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>
2026-07-02 18:52:12 +02:00
marcopan 541df01684 fix(frontend): 'Other — specify' opens a text field and emits control:freetext 2026-07-02 18:24:02 +02:00
marcopan eacc441a4f fix(frontend): render Back/Exit/Other controls on the multiselect widget 2026-07-02 18:20:25 +02:00
marcopanandClaude Sonnet 5 023c69d752 fix(frontend,harness): phase-stepper wiring, central tail, activity-panel markdown
- 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>
2026-07-02 15:53:31 +02:00
marcopanandClaude Opus 4.8 0a13f71b4d feat(resume): Resume in the kebab menu (A1) + harden the resume kickoff (A2)
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>
2026-06-30 18:19:10 +02:00
marcopanandClaude Opus 4.8 b056ff334f feat(frontend): phase progress dots (B) + compact sidebar redesign (C)
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>
2026-06-30 17:53:29 +02:00
marcopanandClaude Opus 4.8 0eeb3f7522 feat(frontend): rotating activity icon + 5-line expandable activity panel
- CentralStatus: replace the red pulsing dot at the start of the "working" line
  with the rotating WorkingSpinner, now a button that opens the model-activity
  panel (onOpenActivity). WorkingSpinner is extracted to its own module (was
  local to AppShell).
- ModelActivityPanel: show the last 5 lines of the model-stream tail (refreshing
  as the stream grows) with an expand toggle to the full stream, replacing the
  unbounded full transcript.
- AppShell: drop the separate spinner button (the inline icon is now the single
  trigger) and the local WorkingSpinner def.
- Remove the now-orphaned Transcript.tsx (its only consumer was the panel).

TDD: CentralStatus + ModelActivityPanel tests RED->GREEN; frontend suite 87/87;
tsc clean. Live visual verification pending (to do with the B/C frontend pass).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 16:26:43 +02:00
marcopanandClaude Opus 4.8 4f60b38ca2 feat(frontend): central 'working' indicator — elapsed time + model-stream tail
While the model works (active session, no pending widget), CentralStatus shows
a Claude-Code-style live status: a pulsing dot, elapsed seconds (ticking), and a
short tail of the model's current output — so the centre no longer looks stuck
during the long F1 loop. Verbose stream stays in the left panel. No tokens yet
(would need the SessionBridge to forward Pi usage).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 22:15:34 +02:00
marcopanandClaude Opus 4.8 9eae655453 style(frontend): move AppShell test imports to top (merge act, add useSessionStore)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 21:52:37 +02:00
marcopanandClaude Sonnet 4.6 feda972a08 feat(frontend): minimal central area; WIP icon above composer toggles model-activity panel
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 21:47:50 +02:00
marcopan b391101a5d feat(frontend): CentralStatus + ModelActivityPanel components 2026-06-29 21:42:46 +02:00
marcopan 068c6f197d feat(frontend): store lastUserEntry + stepMessages; record user input/choice 2026-06-29 21:39:02 +02:00
marcopanandClaude Opus 4.8 3a97ab3b2f fix(frontend): configurable RenameDialog title; 'Rename group' for group rename
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 21:36:14 +02:00
marcopanandClaude Sonnet 4.6 84db1e1085 feat(frontend): rename group (reassign members via setSessionGroup)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 21:32:32 +02:00
marcopan 5442a07ced feat(frontend): Active/Archive as independent accordions (replaces toggle) 2026-06-29 21:26:52 +02:00
marcopanandClaude Sonnet 4.6 2c21e4659f fix(frontend): surface session mutation failures via toast; doResume only activates on success
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 13:30:46 +02:00
marcopanandClaude Sonnet 4.6 aa378291f9 feat(frontend): collapsible group headers; resume/delete integration tests; English test name
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 13:22:09 +02:00
marcopanandClaude Sonnet 4.6 f8be6613a5 feat(frontend): AppShell session management — grouping, archive view, docs panel, mutations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 13:13:22 +02:00
marcopan 910928471a fix(frontend): wrap schema-linking fallback pre; test archived hides Resume 2026-06-29 13:09:56 +02:00
marcopanandClaude Sonnet 4.6 964bfa39ea feat(shell): add SessionDocumentsPanel read-only left drawer (Task 11)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 13:05:40 +02:00
marcopanandClaude Opus 4.8 4f97ea9fc2 fix(frontend): English UI strings for session menu/dialogs; rename empty-guard + cancel test
- SessionMenu: anglicize all Italian labels (Vista divisa → Split view, etc.)
- RenameDialog: add empty-name guard + change title to "Rename session"
- DeleteConfirmDialog: translate title and description to English
- SessionActions.test: add 2 new tests (empty name guard, cancel on DeleteConfirmDialog)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 13:01:54 +02:00
marcopan 013a84ed4b feat(frontend): RenameDialog and DeleteConfirmDialog 2026-06-29 12:55:08 +02:00
marcopan 50025c809e fix(frontend): make NavSessions rows keyboard-accessible (role/tabIndex/Enter+Space) 2026-06-29 12:53:42 +02:00
marcopanandClaude Sonnet 4.6 d5f82b70a4 feat(frontend): SessionMenu kebab + NavSessions prop-driven; AppShell owns sessions query
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 12:50:24 +02:00
marcopanandClaude Opus 4.8 8243ce6446 feat(frontend): session mutation + documents API client and types
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 12:45:09 +02:00
marcopanandClaude Opus 4.8 20bf181e34 feat(frontend): move settings into composer footer as live selectors
- Remove the microphone icon from the composer footer
- Left: workspace selector in its place
- Right: model + thinking-level selectors (persist via PUT /settings) next to
  the context-usage gauge
- Delete the Settings dialog (form + button); only "New session" remains

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 20:50:51 +02:00
marcopanandClaude Opus 4.8 d67409ff16 feat(frontend): rework chat composer (sticky input, inline new session, status footer)
- Center ThothII logo + tagline; break "Human In The Loop" onto a second line
- Rename "New question" → "New session"; the button now clears the screen and
  focuses the sticky bottom composer instead of opening a dialog
- Composer is always rendered and pinned at the bottom; typing the first
  message there creates the session (no modal)
- Add inline stop control (square + inner circle) that interrupts and saves
  the session via /close
- Always-present send affordance (CornerDownLeft ↵) on the right of the box
- Status footer under the box: mic (inert), model name, thinking level, and a
  context-usage gauge (placeholder)
- Brand-tinted spinner beside the logo while the harness/backend holds the
  ball; hidden once a widget needs human input

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 20:36:08 +02:00
marcopanandClaude Opus 4.8 bcaf6708de feat(frontend): English UI + 'Datamart Builder with Human In The Loop' tagline
The interface is now fully English regardless of the chat language (session
titles stay in whatever language the user typed). Changed the right-rail
subtitle to 'Datamart Builder with Human In The Loop'. Translated all chrome:
new-question/settings dialogs, workflow phase strip (F1-F8), session rail,
steer input, widgets (select/multiselect/freetext/fallback/reserved controls)
and viewers (SQL, results, schema linking). Test matchers updated in lockstep.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 18:27:11 +02:00
marcopanandClaude Opus 4.8 acb674b4bc fix(frontend): only send JSON content-type when a body is present
Body-less POSTs (resume, close) sent content-type: application/json with no
body, so Fastify rejected them with FST_ERR_CTP_EMPTY_JSON_BODY (400) and
clicking an existing session never reopened it. apiFetch now adds the header
only when init.body is set.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 18:05:23 +02:00
marcopanandClaude Opus 4.8 4e6d4a4cea feat(frontend): restyle ThothII to the Omics Portal (GSD) design system
Adopt the portal's design language so the app embeds seamlessly into its
header/sidebar chrome later:
- Token layer rewritten as raw OKLCH triplets (the previous oklch()-wrapped
  vars produced invalid colors; the UI was effectively unstyled). Values mirror
  GSD: Manrope + Fraunces fonts, #cb333b red brand, warm off-white surfaces,
  pill buttons, brand focus rings. Dark tokens follow .dark or the portal's
  [data-bs-theme=dark].
- Layout: no left sidebar, no header (the portal supplies them). Conversation
  column flush-left with a slim F1-F8 phase strip; session rail moved to the
  RIGHT, mirroring the portal's left sidebar (red section label, red-tint active
  item). Editorial empty state.
- Transcript component now actually renders the streamed assistant turns (the
  store accumulated them but nothing displayed them). Toaster mounted for errors
  (matchMedia polyfill added to the jsdom test setup).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 18:05:23 +02:00
marcopanandClaude Opus 4.8 abfee5a6fe feat(frontend): add Settings menu item under Nuova domanda
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:30:40 +02:00
marcopanandClaude Opus 4.8 5d79437daf fix(frontend): read MSW request body once in SettingsDialog test (pristine output)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:29:01 +02:00
marcopanandClaude Opus 4.8 29b7e85510 feat(frontend): SettingsDialog with workspace/provider/model/thinking selects
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:26:32 +02:00
marcopanandClaude Opus 4.8 32d963b43d feat(frontend): NewSessionDialog reduced to question-only
Removes workspace/model/provider/thinking fields. Dialog now posts only { question } to createSession and calls onCreated on success.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:22:57 +02:00
marcopanandClaude Opus 4.8 fa0f878c97 feat(frontend): settings api, typed PiModel, question-only createSession
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:19:51 +02:00
marcopanandClaude Opus 4.8 5295e34898 fix(frontend): wire currentPhase to WorkflowBar (normalized) + WidgetHost error handling
- sessionStore.applyEvent: on ui_request set currentPhase normalized from the
  descriptor's phase to its short id (e.g. "F4_schema_linking" -> "F4"), so
  WorkflowBar actually highlights the active phase; falls back to the existing
  phase when the descriptor has none. setPhase kept for resume/getSession.
- Add pushToast store action; WidgetHost wraps postResponse in try/catch,
  pushes an error toast and keeps the widget pending on failure (clearPending
  only on success) so the user can retry.
- Tests: store currentPhase normalization + no-phase passthrough + pushToast;
  new WorkflowBar.test.tsx asserting the matching phase is highlighted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 13:40:33 +02:00
marcopanandClaude Opus 4.8 ddbbe4d1ac test(frontend): unit tests exercise named SSE events (fidelity) + cleanup
FakeEventSource.addEventListener was a no-op, so emit() only drove onmessage.
Production relies on addEventListener for the backend's NAMED events
(event: ui_request), so the unit tests could pass while prod silently broke.

- fakeEventSource: store named handlers in a Map<string,Set>; add emitNamed()
  that dispatches to them; keep emit() for the unnamed/default onmessage path
- useSessionStream.test: ui_request now driven via emitNamed (production path);
  add a separate test for the unnamed text_delta path via plain emit
- f1-loop.test: widget emission switched to emitNamed("ui_request", ...)
- verified: tests FAIL if addEventListener wiring is removed from
  useSessionStream (then restored)
- cleanup: fake-pi.mjs drops unused execFileSync import, uses static spawnSync

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 13:33:50 +02:00
marcopanandClaude Sonnet 4.6 1171181f9a test(frontend): Playwright e2e F1 vs backend+fake-pi (hermetic)
Adds a real-browser Playwright e2e of the full F1 disambiguation loop,
driven against the real backend + fake binaries (no VPN, no real Pi, no Python).

- frontend/e2e/fixtures/fake-tht.mjs: stubs tht CLI (session new/list/show)
- frontend/e2e/fixtures/fake-pi.mjs: wraps harness fake-pi-rpc with f1_disambiguation.json
- frontend/playwright.config.ts: two webServer entries (backend:8799, frontend:5199)
- frontend/e2e/f1.spec.ts: open app → create session → wait for SelectWidget → respond

Bug fixes discovered during e2e:
- useSessionStream: add addEventListener for named SSE events (backend sends
  'event: ui_request' etc.; onmessage only fires for unnamed 'event: message')
- FakeEventSource: add no-op addEventListener/removeEventListener stubs
- backend SSE route: add CORS headers manually in writeHead() since
  reply.raw bypasses the @fastify/cors onSend hook
- backend: install and register @fastify/cors for all non-SSE routes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 13:27:00 +02:00
marcopanandClaude Opus 4.8 3dec4c3af0 test(frontend): cover NewSessionDialog model degradation + invalidate sessions on resume
- Add NewSessionDialog.test.tsx covering §9 graceful degradation:
  empty models -> free-text input; non-empty models -> dropdown; plus a
  createSession body-keys assertion
- Normalize model entries to {value,label} so object-shaped models
  ({provider,id}) render correctly in the dropdown (was assuming strings)
- NavSessions: invalidate the ["sessions"] query after a successful
  resumeSession so the list/status refreshes immediately

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 13:11:40 +02:00
marcopanandClaude Sonnet 4.6 544882f95b feat(frontend): sessions list/create + steering + resume + selectors
- NavSessions: left nav listing sessions via listSessions (TanStack Query),
  clicking resumes via resumeSession and sets active; active item highlighted
- NewSessionDialog: ShadCn dialog with question textarea, workspace native
  select (from listWorkspaces), graceful model field (dropdown when models
  list is non-empty, free-text input when empty per §9 degradation),
  optional thinking/provider fields; try/catch surfaces errors inline
- SteerInput: text input + button POSTs to postSteer, clears on send,
  supports Enter key; only shown when a session is active
- WorkflowBar: renders 8 phases (F1..F8) with data-active highlighting
  driven by useSessionStore.currentPhase (new store field + setPhase action)
- AppShell: wires all four components; right artifact sidebar now collapsible
  via a toggle button (useState sidebarOpen); replaces old "Nuova domanda"
  button with NewSessionDialog trigger
- f1-loop.test: updated to drive through the new dialog flow (open → fill
  question → submit → wait for SSE → respond)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 13:06:01 +02:00
marcopanandClaude Opus 4.8 3ecfed5d3d fix(frontend): valid scalar markup + selector/truncated coverage in ResultsPanel
- Replace invalid role="strong" with data-testid="scalar-value"
- Add test (d): selecting "tutti" re-fetches preview with the large limit
- Render "(risultati troncati)" indicator when truncated; assert in test (a)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 13:00:50 +02:00
marcopanandClaude Sonnet 4.6 b4ab7b005a feat(frontend): results panel (AGGrid + preview/export)
Add ResultsPanel component with AGGrid v36 for tabular preview results,
bold scalar display for 1×1 results, limit selector (10 / tutti), and
Esporta CSV button delegating export to the backend. MSW-tested with 3
passing scenarios (grid cells, scalar, export endpoint call).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 12:57:08 +02:00
marcopanandClaude Sonnet 4.6 38032877e9 test(frontend): settle async highlight in SqlViewer test (pristine output) + assert toggle flip
Test (a) now drains pending highlightSql microtasks inside act() via
waitFor before synchronous header assertions, eliminating the React
act() warning. Test (c) asserts the layout toggle label flips
Orizzontale -> Verticale rather than merely existing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 12:53:59 +02:00
marcopanandClaude Sonnet 4.6 954f701c3f feat(frontend): SQL/CTE viewer with shiki highlighting
TDD: SqlViewer renders collapsible CTE blocks with name/field-count/
test-status badges, shiki-highlighted SQL body (dangerouslySetInnerHTML),
per-field comments, and a vertical/horizontal layout toggle.
highlight.ts wraps shiki as a lazy singleton; tests mock it for
determinism (pattern mirrors mermaid.ts).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 12:49:58 +02:00