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>
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>
- 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>
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>
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>
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>
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>
- 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>
- Add PiProcessManager.resume(sessionId, tht): reads provider/model/thinking
from tht.sessionShow() and calls spawnFor with those values
- Add POST /sessions/:id/resume route: calls mgr.resume then re-wires
bridge.onClientEvent → hub.publish
- Confirm venv PATH already present in real spawn (no change needed)
- Add e2e test: POST /sessions → SSE receives ui_request via pendingWidget
re-emit → POST /sessions/s1/response → 204 (all over real HTTP against
fake-pi-rpc)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Harness:
- preview_cmd FILE positional arg made optional; when omitted with --session,
path is derived via _session_sql_file (mirrors export_cmd) — fixes the
deferred Task-5 bug where the backend passed sessions/<id>/sql_final.sql
relative to harnessDir, which broke for workspace-dependent paths.
- New pytest: test_preview_session_no_file_resolves_sql_final
Backend:
- ThtRunner.sqlPreview: drop positional file arg; use --session only
- New routes/sql.ts: POST /sessions/:id/sql/preview + /export
- New routes/meta.ts: GET /workspaces (yaml scan) + GET /models (injectable
seam + graceful fallback to {models:[]})
- app.ts: register sqlRoutes + metaRoutes; add listModels to BuildAppDeps
- tht-runner.test.ts: add sqlPreview argv assertion (no file path)
- test/routes-sql-meta.test.ts: 9 tests (sql preview/export + meta routes)
Tests: harness 233 passed; backend 29 passed; build clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
POST /sessions no longer silently drops `workspace`. ThtRunner.run/json
take an optional workspace; configArg() selects workspaces/<ws>.yaml when
it exists under harnessDir, else falls back to default configPath.
sessionNew threads workspace through. Route forwards b.workspace with an
MVP note (gate/Pi side still single-workspace via symlinked config/tht.yaml).
19/19 tests pass, tsc clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Make buildApp injectable (deps.thtRunner + deps.spawnFn); create
src/routes/sessions.ts with all 7 session routes under authPreHandler;
wire bridge.onClientEvent→hub.publish before returning {id} from POST
/sessions; SSE subscribes with pendingWidget safety net.
18/18 tests pass, tsc clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Decodes native extension_ui_request (method:input, title=JSON) into
ui_request ClientEvent; encodes respond() as extension_ui_response with
value payload; tracks pending widget; handles notify→info and steer.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implement attachJsonlReader to split streams on \n only, handling
trailing \r, and reassembling lines split across chunk boundaries.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Set quietStartup:true in .pi/settings.json to suppress Pi banner on RPC stdout.
Add test pinning both quietStartup and theme values. Document project-local trust
requirement (--approve) so no trust prompt blocks RPC loop iteration.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- test_list_sessions_required_keys: assert full spec key set
(adds summary/updated_at/author) so dropping any goes caught.
- test_cli_show_json_valid: assert "schema" in / "db_schema" not in
data to pin by_alias=True on the alias-sensitive field.
Production code unchanged. 8 passed; full suite 230 passed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When offset>0 the wrapper added an outer LIMIT N, so run_controlled's
_inject_limit bailed (a LIMIT IS present) and truncated was always False —
AGGrid could never detect more rows. Fix: for offset>0 probe with LIMIT (N+1)
OFFSET M, then compute truncated = len(rows) > N in do_run and slice back to N.
offset==0 path unchanged (delegates to extracted _run_transport helper). JSON
still reports the user's requested limit N and correct truncated. Adds 3 tests
exercising the real do_run offset>0 path (N+1 -> truncated True, N -> False,
offset==0 verbatim). 222/222 passing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add inject_limit_offset (tht/execute/limit.py) — pure subquery wrapper that
applies LIMIT/OFFSET non-destructively without clobbering user-supplied LIMITs.
Wire offset param into do_run (pre-processing when offset>0) and add --offset /
--json flags to preview_cmd; JSON mode emits pristine stdout with columns, rows,
execution_ms, truncated, limit, offset. 5 new tests (4 unit + 1 JSON-purity),
219/219 total passing (no regressions).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When THT_SESSION env var is set, /nuova-domanda injects a kickoff that
tells the model to use the pre-created session id instead of running
`tht session new`. /riprendi-sessione and no-env-var paths unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Removed event.source === "interactive" guard from entry detection so
/nuova-domanda and /riprendi-sessione activate lockActive+pendingKickoff
regardless of source (TUI or RPC prompt).
- Removed event.source !== "interactive" from free-input filter; lock now
blocks/steers all user input when active, not only interactive keystrokes.
- Added typebox@1.1.38 devDep + fake_pi_runtime.registerCommand (gap from Task 2).
- New test: gate_entry.test.js (2 tests: lock activates on RPC; !-steer passes).
- Full suite: 19/19 pass, zero regressions.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Lo spike Task 1 ha provato che ctx.sendRaw non esiste e pi.on(extension_ui_response)
non e' dispatchato. Aggiornati: Piano1 Task2 (mock ctx.ui), Task4 (rewrite gate a
ctx.ui.input con descriptor in title), Task10 (fake-pi-rpc shape nativa); Piano2
Task3/Task4 (SessionBridge decodifica title<->value). Contratto FE invariato.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Due piani separati (decomposizione concordata): il backend dipende da lavoro
harness non testato (gate RPC-ready, id injection, prereq CLI --json/--offset,
fake-Pi). Piano 1 rende l'harness pilotabile via RPC; Piano 2 costruisce il
backend Node/Fastify testato contro il fake-pi-rpc.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Decisioni BE-1..BE-7: un Pi per sessione attiva, SQL finale delegato a tht
(codepath unico, rischio D7 eliminato), resilienza via ricostruzione da disco +
re-emit del widget pendente, test con fake-Pi condiviso, backend pre-crea la
sessione, model/thinking/provider per-sessione persistiti, settings Pi MVP.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implementazione del piano di remediation progressiva sui difetti emersi
dall'analisi dell'harness. Tutto verificato: 214 test Python (incl. L0 su
Postgres reale), 14 test JS del gate, ruff pulito.
Blocco 1 (CRITICA, integrazione gate↔CLI):
- phase advance: gate usa --auto + exit 6; reviewer_confirm kind:phase fa
advance esplicito che applica i prerequisiti (prima non avanzava per le
fasi a conferma umana).
- cte plan riceve i --name dal gate (param names); set-question con id
posizionale; skill `tht search find`; nuovo comando `tht memory save-one`
con dedup hash client-side in save_one_memory.
Blocco 2 (D15, stato post-rollback):
- campo `phase` su DecisionRecord + effective_decisions phase-aware per i
subject "a nome" (cte_approved ecc.); _compute_promotions e finalize sulla
vista effective; finalize confronta col piano CTE effettivo, non glob;
`decision add --retracts` + comando `decision retract`.
Blocco 3 (D7 read-only + D6 manifest):
- assert_read_only su tutti e quattro i codepath (direct + REST);
- manifest author/summary/updated_at/updated_by/schema_version popolati +
helper touch_manifest sulle mutazioni.
Blocco 4-5 (D14a/D14b):
- decision_min_phase data-driven via `emits:` in workflow.yaml;
- formula evidence: status auto, search_formulas, gruppo CLI `tht formula`,
`search find --kind formula`, load_evidence_dir salta i .sql.md.
Blocco 6 (robustezza):
- taskdoc slice promoted_tables + bound enforced; report escaping/bound +
rsplit note; filtro kind reader REST/direct; conteggio upserted robusto;
guard REST run_query non-list; LSH disallineato -> LshIndexError.
Blocco 7 (pulizia):
- dead code gate e KIND_TO_TABLE morto rimossi; doc Postgres-only
(README + connection.py).
Blocco 0 (parziale): test di compatibilità firma gate↔CLI
(tests/integration). Rinviati: fake-Pi runtime completo, artifact-gate da
disco (#23), parità eligibility REST/direct (#28), unificazione
reserved-labels (#30), memory_rejected da deselezione (#33).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>