Commit Graph
100 Commits
Author SHA1 Message Date
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 2e09c88f78 docs(state): record UI-redesign progress (D+E done; B/C/F/A pending)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 16:46:50 +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 c12bdcd257 feat(harness): derive a 3-5 keyword session name (YAKE, no LLM)
New sessions get a concise Italian-keyword `name` instead of the truncated
question. `tht session new` (when no --name is given) derives it via a new
`_extract_name` helper using YAKE (pure-Python, unsupervised, Italian, no LLM),
dropping generic query verbs and keeping the top keywords in reading order;
falls back to `_summarize` if YAKE is unavailable. `create_session` core keeps
its `name=None` default — the policy lives at the CLI layer.

TDD: tests/test_session_name.py (unit + CliRunner integration). Full harness
suite 269 passed; ruff clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 15:54:54 +02:00
marcopanandClaude Opus 4.8 d27afd0896 fix(gate): read reviewer_select/confirm choice from the choices[] array
The frontend's reviewer widgets uniformly send the picked option in a `choices`
array (SelectWidget/ArtifactGateWidget: `choices: [optionId]`), but the gate's
reviewer_select and reviewer_confirm(reject) handlers read `resp.choice`
(singular). Result: every single-select gate saw an undefined choice, answered
"Nessuna scelta ricevuta", and re-presented forever — the workflow could never
pass F1. (reviewer_decide/multiselect already read `resp.choices`, so it worked.)

Add a shared selectedChoice(resp) helper reading choices[0] (falling back to the
legacy singular choice); both handlers use it.

TDD: gate/__tests__/gate_choice.test.js RED->GREEN; full gate suite 28/28.
Verified LIVE (Playwright -> real Pi -> GLM 5.2): a single-select F1 answer is
now accepted and the workflow advances (clarification 2/4 -> 3/4). The same run
also live-verified the F1 hang fix (418187a).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 12:48:56 +02:00
marcopanandClaude Opus 4.8 418187a4ad fix(backend): echo Pi's RPC id so reviewer gates unblock after answer
ctx.ui.input in `pi --mode rpc` correlates extension_ui_response on its own
top-level RPC id (crypto.randomUUID), not the descriptor id the gate carries
in `title`. SessionBridge replied with the descriptor id, so Pi silently
dropped the response and the model never resumed — every reviewer widget hung
after the human answered.

SessionBridge now stores Pi's top-level m.id (pendingPiId) and replies
extension_ui_response{ id: pendingPiId, value: <uiResponse> }; value still
carries the descriptor id so the gate's internal resp.id === descriptor.id
check still holds.

The fake-pi double had masked the bug by forcing m.id == descriptor.id; it now
mirrors real Pi (distinct randomUUID, correlate on it, drop unknown ids), with
a negative regression test. SKILL.md Phase 1 also now steers multi-answer
disambiguation to reviewer_decide (multiselect).

Tests: backend 67/67, tsc clean, fake-pi contract 2/2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 10:43:49 +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 Opus 4.8 a868fc0673 docs(plan): session UI refinements implementation plan (5 tasks, frontend, TDD)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 21:23:49 +02:00
marcopanandClaude Opus 4.8 3d06505819 docs(spec): session UI refinements design
Active/Archive accordions; rename group (client-side reassign); central
area shows only last user entry + gate notify/info + active widget; the
verbose model stream moves to a left on-demand panel toggled by the WIP
icon (moved above the composer). Frontend-only; fine thinking-separation
deferred.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 21:13:28 +02:00
marcopanandClaude Opus 4.8 37d40d6680 fix(backend): drop pi --approve flag (removed in pi 0.73 @mariozechner rebrand)
pi 0.73 rpc mode is headless and runs tools without an approval gate; the
removed --approve flag made pi exit with 'Unknown option: --approve', breaking
every session spawn. Spawn args are now just --mode rpc. +regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 21:03:48 +02:00
marcopan a791919925 fix(backend): check read-only 409 before the Ollama preflight on resume 2026-06-29 20:06:03 +02:00
marcopanandClaude Sonnet 4.6 90a26dafce feat(backend): Ollama embeddings preflight on session create/resume (503 hard-fail)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 19:37:39 +02:00
marcopan a0af089d00 feat(backend): ThtRunner.ollamaEnsure (parses tht ollama ensure --json) 2026-06-29 19:31:49 +02:00
marcopan deb8081bc2 test(harness): end-to-end tht ollama ensure via root app 2026-06-29 19:30:00 +02:00
marcopan 7e19f81173 feat(harness): tht ollama ensure CLI command (hard-fail preflight) 2026-06-29 19:25:23 +02:00
marcopan 9c39ea129e fix(harness): ensure_ollama never raises on start/probe failure (stage server) 2026-06-29 19:19:26 +02:00
marcopan 5f5cac05d5 feat(harness): EmbeddingsConfig bin/start_cmd + ensure_ollama preflight orchestration 2026-06-29 19:15:28 +02:00
marcopanandClaude Opus 4.8 f66e8e41d9 docs(plan): Ollama embeddings preflight implementation plan (4 tasks, TDD)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 19:00:04 +02:00
marcopanandClaude Opus 4.8 c2f8dd5af9 docs(spec): Ollama ensure (embeddings preflight) design
Hard-fail preflight at session create/restart: ensure Ollama up + warm the
configured embedding model, refuse the session if embeddings unavailable.
Parameterized ollama bin/start_cmd; tht ollama ensure command + backend 503.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 18:53:03 +02:00
marcopanandClaude Opus 4.8 1334c1d871 docs: add project CLAUDE.md (commands + architecture + gotchas)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 14:21:52 +02:00
marcopanandClaude Opus 4.8 d258a1ae4c docs: add PROJECT_STATE.md orientation snapshot
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 14:19:59 +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
marcopan f8c246a1b6 fix(harness): English document section titles in build_documents 2026-06-29 13:02:51 +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
marcopan 57580676a3 docs(harness): add Phase 0 Resume cold-start procedure to the orchestrator skill 2026-06-29 12:41:27 +02:00
marcopan 790ac71284 feat(backend): spawnFor new/resume prompt mode; resume sends /riprendi-sessione 2026-06-29 12:38:26 +02:00
marcopan 387ae56583 test(backend): make mgr injectable; assert teardown-before-delete ordering 2026-06-29 12:36:29 +02:00
marcopanandClaude Opus 4.8 bd29ac517c feat(backend): rename/group/archive/unarchive/delete/documents routes + resume read-only guard
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 12:32:52 +02:00
marcopan 4a63db51e8 feat(backend): ThtRunner session mutation + documents methods 2026-06-29 12:29:14 +02:00
marcopan 03053b0716 feat(harness): tht session documents --json read command 2026-06-29 12:25:40 +02:00
marcopan 38cd4889f4 style(harness): move test_session_mutations imports to top, drop unused json 2026-06-29 12:23:48 +02:00
marcopanandClaude Opus 4.8 26337d0e32 feat(harness): tht session set-name/set-group/archive/unarchive/delete commands
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 12:20:43 +02:00
marcopan 60fa665d73 feat(harness): manifest archived/group fields + session mutation helpers 2026-06-29 12:16:52 +02:00
marcopanandClaude Opus 4.8 8167d41621 docs(plan): session management implementation plan (12 tasks, TDD)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 12:05:19 +02:00
marcopanandClaude Opus 4.8 4364bdd4a1 docs(spec): frame cold-start resume correctness as a prerequisite
Add the SKILL.md design contract (persisted state is the truth) and a
dedicated Resume correctness section: backend new/resume prompt mode,
missing cold-start procedure in the skill, end-to-end verification gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 11:39:56 +02:00
marcopanandClaude Opus 4.8 916075329d docs(spec): phase-aware session management design
Read-only document panel, rename, groups, archive (filesystem flag),
delete. No verbatim chat persistence — phase documents are the storage.
pgvector/app-DB deferred.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 11:21:49 +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 79dc9b5ed7 chore(harness): add psd workspace config
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 20:37:19 +02:00
marcopanandClaude Opus 4.8 d999c0ca3a feat(harness): load .env, add reviewer arg prep, refactor gate option filtering
- loadEnvFromDotenv: read ctx.cwd/.env into the process environment
- prepareReviewerArguments: normalize/parse reviewer tool inputs
- tidy reserved-option filtering and tht command argument assembly

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 20:37:19 +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 f59d6d1478 fix(backend): allow PUT in CORS methods so browser can save settings
Found via live browser test: PUT /settings preflight was rejected because
@fastify/cors default methods omit PUT. GET/POST were unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:54:30 +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 ec36ee421a feat(backend): POST /sessions applies global settings, body is question-only
workspace/provider/model/thinking now come from getSettings() injected into
sessionRoutes; the request body supplies only question+name. Also teaches
fake_pi_rpc to respond to set_model and set_thinking_level RPC commands so
tests that pass real model settings don't hang.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:16:08 +02:00
marcopanandClaude Opus 4.8 098915515f fix(backend): deterministic /models fallback test + merge duplicate import
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:12:18 +02:00
marcopanandClaude Opus 4.8 bfbb017413 feat(backend): /settings GET+PUT, /models PiModel shape, app wiring
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:09:04 +02:00
marcopanandClaude Opus 4.8 c4b599ec00 feat(backend): ephemeral Pi model lister (get_available_models) with TTL cache
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:04:33 +02:00
marcopanandClaude Opus 4.8 6b2883a467 feat(backend): persistent settings store + settingsFile config
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 16:01:22 +02:00
marcopanandClaude Opus 4.8 0cd14b5b55 docs: settings-menu design spec + implementation plan
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 15:59:47 +02:00
marcopanandClaude Opus 4.8 a9b9ce297a chore: gitignore .playwright-mcp run artifacts
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 15:07:09 +02:00
marcopanandClaude Opus 4.8 091a055bf0 fix(backend): bridge maps real Pi message_update->text_delta for FE streaming
Live end-to-end against real pi --mode rpc revealed Pi streams assistant text as
top-level message_update events whose nested assistantMessageEvent carries the
incremental delta — not the top-level text_delta the fake-pi-rpc emits. The bridge
now maps message_update(assistantMessageEvent.text_delta).delta -> FE text_delta,
so the chat shows the model's output during a real session.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 15:03:13 +02:00
marcopanandClaude Opus 4.8 1ee692cdb1 fix(backend): tht --config is per-command, append after subcommand (not global -c)
Live end-to-end surfaced a 500 on every tht call: ThtRunner prepended
'-c <config>' before the subcommand, but tht has no global -c option
('No such option: -c'). --config/-c is a per-command option, so it must be
appended AFTER the subcommand. Extracted buildArgv() and fixed the unit test
that had codified the wrong (prepended) order.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 14:52:39 +02:00
marcopanandClaude Opus 4.8 18843d7421 chore: run-stack.sh — avvia i 3 layer reali per validazione end-to-end
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 14:45:07 +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
marcopanandClaude Opus 4.8 eb552331a6 fix(frontend): cap schema-linking graph on promoted count + signals in table Perché
- Cap counts promoted candidates only (was all candidates); reuse promoted list (DRY)
- New test: 10 promoted + 50 excluded must not cap, graph stays available
- Extract reasonFor() helper; table Perché falls back to signal keys like the flowchart

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 12:47:29 +02:00
marcopanandClaude Sonnet 4.6 2a23de6825 feat(frontend): schema-linking viewer (mermaid+table) + markdown view
TDD: SchemaLinkingViewer with mocked renderMermaid, toggle flowchart↔table,
≤45 element cap with Italian notice. MarkdownView renders mermaid fences.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 12:43:11 +02:00
marcopan f933b544ff test(frontend): cover ArtifactWidget view-only + multiselect allow_empty gate 2026-06-28 12:40:09 +02:00
marcopan be9e761e0a feat(frontend): multiselect/artifact-gate/artifact widgets + linkage + no-limbo 2026-06-28 12:36:13 +02:00
marcopanandClaude Sonnet 4.6 2b55e96608 feat(frontend): 4-zone shell + F1 loop end-to-end (MSW)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 12:30:35 +02:00
marcopan a267754f8f feat(frontend): F1 widgets (select/info/freetext) + reserved controls 2026-06-28 12:25:50 +02:00
marcopan e9aca179d7 feat(frontend): widget registry + universal fallback 2026-06-28 12:22:37 +02:00
marcopan d238ddd395 feat(frontend): useSessionStream (EventSource -> store) 2026-06-28 12:19:55 +02:00
marcopan ebd3988634 feat(frontend): Zustand session store + applyEvent 2026-06-28 12:17:16 +02:00
marcopanandClaude Sonnet 4.6 913ac30137 feat(frontend): contract types + REST client (MSW-tested)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 12:13:50 +02:00
marcopanandClaude Sonnet 4.6 7e42ac11ef chore(frontend): gitignore tsbuildinfo + shadcn to devDeps
Untrack tsconfig.tsbuildinfo (TS build artifact) and add *.tsbuildinfo
to .gitignore. Move shadcn from dependencies to devDependencies (CLI
generator, not runtime). npm test passes; npm run build clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 12:11:16 +02:00
marcopanandClaude Sonnet 4.6 662d944267 feat(frontend): scaffold Vite+React+TS+Tailwind/ShadCn + Vitest
TDD: App-renders-ThothII test RED then GREEN. ShadCn v4 (base-nova style)
init + button/checkbox/radio-group/textarea/card/dialog/badge/sonner added.
tailwind.config.ts extended with oklch CSS-variable color mapping required
by ShadCn v4 @apply directives. npm test passes; npm run build clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 12:08:22 +02:00
marcopanandClaude Opus 4.8 cabff8e77f docs(plans): piano di implementazione del frontend (13 task, F1 first)
Vite+React SPA: scaffold, api/types+client, store Zustand, useSessionStream (SSE),
widget registry+fallback, widget F1 (select/info/freetext), shell 4-zone + loop F1,
widget restanti+linkage, viewer (schema-linking/sql/results), sessioni+steering+resume,
Playwright e2e F1 vs backend+fake-pi.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 12:02:21 +02:00
marcopanandClaude Opus 4.8 0fe2f2139e docs(frontend): design del frontend ThothII (brainstorming)
Decisioni FE-1..FE-6: Vite+React SPA (no Next), TanStack Query+Zustand+hook SSE,
EventSource nativo (MVP auth=none), widget registry+fallback, test Vitest+RTL+MSW
+ Playwright e2e F1, build a slice con F1 come primo loop chiuso.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 11:51:14 +02:00
marcopanandClaude Opus 4.8 7efa88aaca docs: stato del progetto e guida alla ripresa (harness+backend su main)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 22:02:35 +02:00
marcopanandClaude Opus 4.8 b909f3e571 chore(backend): commit package-lock.json for reproducible installs
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 21:59:59 +02:00
marcopanandClaude Sonnet 4.6 c63b2bd126 fix(backend): idempotent spawnFor + identity-checked exit + unified SSE re-emit shape
- spawnFor now tears down any existing runtime for the same session id before
  the cap check, so resume/respawn neither leaks the old child nor falsely hits
  maxPiProcesses
- exit handler is identity-checked (captures rt) so a stale child's late exit
  cannot evict a newer runtime
- SSE pending re-emit now sends the full ClientEvent shape
  { type: "ui_request", ui_request } to match hub.publish live events
- tests: same-id respawn replaces runtime (count 1); old child exit does not
  evict new runtime; sse-hub re-emit asserts unified shape

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 21:53:12 +02:00