Commit Graph
94 Commits
Author SHA1 Message Date
marcopanandClaude Opus 4.8 a166d9bc4e fix(gate): guard the steering-during-lock hint on ctx.mode, not ctx.hasUI
The "respond with gate widgets; use '!' for free text" notice is terminal-
flavored. Like the reLoop Esc guard, it used ctx.hasUI, which on pi >=0.80 is
true in RPC too, so it leaked to the browser. Now guarded on ctx.mode === "tui".
The plain text is still swallowed by the `handled` return in all modes; only the
warning is dropped in RPC. No functional ctx.hasUI guard remains in the gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 12:41:39 +02:00
marcopanandClaude Opus 4.8 d6b4729ddf feat(shell): yellow active session, growing composer, phase title, refined activity log, arrow panel toggle
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>
2026-07-04 14:32:01 +02:00
marcopanandClaude Opus 4.8 1f9818c50f fix(gate): guard the TUI-only "Esc" notice on ctx.mode, not ctx.hasUI
reLoop's "Esc non chiude il gate..." warning is terminal-specific. On pi 0.73
ctx.hasUI was false in RPC, so `if (ctx.hasUI)` effectively meant "TUI only". On
pi >=0.80 hasUI is true in RPC too (dialog-capable UI via the bridge), so the
notice leaked to the browser on any invalid gate response. Guard on
ctx.mode === "tui" to restore the original intent. The fake pi runtime gains
mode:"tui" so the roundtrip test still exercises the notice.

Audit of the other 5 ctx.ui.notify: left as-is. They are valid in both live
modes (TUI and RPC, both hasUI=true) and the gate cannot run headless
(emitAndWait needs a UI), so a guard would be dead code. Their dual-mode
rationalization belongs with the future present() work (tracked in the eval doc).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 14:21:57 +02:00
marcopanandClaude Opus 4.8 be02e96f9e chore(pi): consolidate 0.80.3 migration — align extension + docs to @earendil-works
- aritmolab-provider.js: import pi-ai from @earendil-works (was @mariozechner).
  Verified live that the aritmolab/qwen provider still registers under pi 0.80.3
  (get_available_models -> aritmolab, deepseek, zai). Removes the dual-package
  reliance on the frozen @mariozechner install still on disk for rollback.
- docs/general/pi-configuration.md: built-in models package is @earendil-works/pi-ai

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 13:23:46 +02:00
marcopanandClaude Opus 4.8 a0a503b721 fix(gate): coerce stringified object params; block model edits to gate code
Some models (GLM 5.2) send object params (reviewer_confirm's artifact,
write_schema_linking's schema_linking) as JSON-encoded strings. These failed
TypeBox validation before execute(), making the model retry in an unbounded loop
(observed ~2100s hang). jsonObjectOrSelf() coerces them back to objects in
prepareReviewerArguments and write_schema_linking.

Also close an anti-bypass gap: pi's write/edit tools were unrestricted on the gate
code, so a looping model actually patched tht-gate.js. GATE_CODE_FILES now blocks
any write under .pi/extensions. Requires a pi restart to take effect.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 18:28:45 +02:00
marcopanandClaude Opus 4.8 c3e8f14ad9 test(gate): clarify the 'altrove' first-token guard comment
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 14:13:02 +02:00
marcopan 168a72372b fix(gate): robust isReserved strips model Altro/other variants (no duplicate free-text option) 2026-07-03 13:05:21 +02:00
marcopanandClaude Opus 4.8 b043293baf fix(gate): stop injecting the redundant Altro option on select gates
buildSelectRequest injected an `altroOption` ({id:"other", opens:freetext}) into the
select descriptor, but SelectWidget/MultiselectWidget never route option.opens — so it
rendered as an inert "Altro — specifica…" button next to the working reserved
"Other — specify" control (which now owns free-text since the reviewer-gate-ux fix).
Remove the injection, the now-unused altroOption()/ALTRO_LABEL, and the unused
allow_other flag from both builders (no frontend consumer). Free-text stays offered on
every gate via the reserved "Other" control. The ArtifactGateWidget opens/LinkageHost
linkage (reject-with-reason capability) is intentionally kept — it is a separate,
tested feature, not the injected duplicate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 19:10:01 +02:00
marcopan 78518210a0 docs(skill): document empty-memory auto-advance (no empty checklist) in Phase 2 2026-07-02 18:39:12 +02:00
marcopan 0a76103c35 fix(gate): empty memory phase shows a notice and auto-advances instead of an empty checklist 2026-07-02 18:34:17 +02:00
marcopan 644634d492 fix(gate): reviewer_confirm requires explicit approve; free-text is actionable, not accidental approve 2026-07-02 18:30:14 +02:00
marcopan 2633b46199 fix(gate): buildArtifactGate emits approve/reject options so the gate renders a forward button 2026-07-02 18:27:11 +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 Fable 5 4479683cf4 chore(pi): scope AritmoLab provider to project, drop Gemma model
Move the AritmoLab provider (Qwen) into harness/.pi/extensions/ so it is
auto-discovered only when Pi runs with cwd=harness, keeping it project-local.
GLM (~/.pi/agent/models.json) and DeepSeek (built-in) stay user-wide. File is
.js (not .mjs) because Pi extension auto-discovery matches only /\.(ts|js)$/.

Removes the gemma4-26b-a4b model (404 at the endpoint) from both the provider
and the model-matrix default list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 11:49:11 +02:00
marcopanandClaude Opus 4.8 3dadc6fbb5 docs(skill): F7 is two-step — kind:"sql" records, kind:"phase" advances
The cheat-sheet, Discipline 2, and Phase 7 said F7 closes with
reviewer_confirm kind:"sql" — but the gate's kind:"sql" only records
sql_approved:phase:7; F7 is not in _AUTO_ADVANCE_PHASES, so advancing to
F8 still needs reviewer_confirm kind:"phase" (mirrors F6). Same
record-vs-advance trap this branch removes elsewhere.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 19:55:23 +02:00
marcopanandClaude Opus 4.8 01de6f330f docs(skill): correct phase-advance contract + add phase cheat-sheet
F3/F4 close with reviewer_confirm kind:'phase' (they do NOT auto-advance);
advance:true only auto-advances F2-empty/F6-skip; rewrite_question belongs
to F3 not F1; F4 uses the new write_schema_linking tool with the documented
SchemaLinking shape. Adds a per-phase artifact/close cheat-sheet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 16:55:49 +02:00
marcopanandClaude Opus 4.8 20bc3de950 feat(gate): write_schema_linking tool (validated F4 artifact via CLI)
tht() gains an optional stdin arg; the tool pipes the schema-linking
object to 'tht session set-schema-linking --file -', which validates
against SchemaLinking and returns the exact error on failure — so the
model stops hand-writing the artifact and validating with ad-hoc python.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 16:49:18 +02:00
marcopanandClaude Opus 4.8 00777922d8 feat(cli): 'tht session set-schema-linking' (file/stdin, validated)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 16:44:36 +02:00
marcopanandClaude Opus 4.8 92caaac18d feat(store): set_schema_linking validates then writes the F4 artifact
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 16:40:04 +02:00
marcopanandClaude Opus 4.8 160d7f075e fix(gate): F6 approves each CTE by name, not 'phase:6'
reviewer_confirm kind:'cte_result' registered cte_approved --subject
phase:6, which decision_cmd rejects (exit 5) and next_cte never
recognizes — dead-ending F6. Derive the CTE name from 'tht cte next'
(plan-order single source of truth) and approve by name. sql path
(sql_approved:phase:N) unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 16:34:54 +02:00
marcopanandClaude Opus 4.8 47a52170ce feat(cte): add 'tht cte next' — first unapproved plan CTE
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 16:29:52 +02:00
marcopanandClaude Opus 4.8 cbb8e184f1 feat(harness): cross-model behavior matrix (G) — harness + results
Tier 1 (clean-room first-turn harness, harness/scripts/model-matrix.mjs):
kickoff + resume chain in-turn on ALL available models — zai/glm-5.2,
deepseek/deepseek-v4-{pro,flash}, aritmolab/qwen3.6-35b-a3b, zai/glm-4.5-air.
The resume cold-start stall recurs on none (closes A's cross-model robustness).
aritmolab/gemma4-26b-a4b is a 404 at the endpoint (listed but not served) — an
availability gap classified as MODEL_ERROR, not a workflow issue.

Tier 2 (live, baseline zai/glm-5.2): F single-select auto-confirm verified
end-to-end — answering the first reviewer_select persisted a concept_clarified
decision (review_decisions.jsonl 0->1) with no follow-up confirmation gate.
Closes F's deferred live check.

No prompt hardening needed. Results in the G plan doc + memory. Throwaway psd
sessions used and deleted; real sessions untouched.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 18:46:55 +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 cef9ae4368 feat(harness): single-select answers auto-confirm (reviewer_select persists)
F — reviewer_select options may now carry a `decision` payload {type, subject,
detail?, rationale?} plus an optional `advance`. Picking such an option IS the
confirmation: the gate persists it directly (tht decision add) and optionally
advances, with no redundant reviewer_decide/reviewer_confirm follow-up gate.
Options without a payload stay ask-only; back/exit/Other never persist.

Pure logic extracted + exported for unit tests: resolveSelectOutcome (classifies
the response) and decisionAddArgs (shared with reviewer_decide, DRY). Gate JS
suite 33/33 (gate_select_decision.test.js, +5); harness pytest 269 unchanged.

Contract docs updated together: reviewer_select tool description, SKILL.md
(widget summary, disciplines 2-3, Phase-1 single-pick), and the CLAUDE.md gate
note. Live verification (model truly emits reviewer_select+decision, decision in
review_decisions.jsonl, no follow-up gate) deferred to workstream G — it is
model-behavior-dependent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 18:01:50 +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
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
marcopan f8c246a1b6 fix(harness): English document section titles in build_documents 2026-06-29 13:02:51 +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 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 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 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 Sonnet 4.6 d630cac3ab feat(task-10): SQL routes + /workspaces + /models + fix sql preview path bug
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>
2026-06-27 21:31:35 +02:00
marcopanandClaude Sonnet 4.6 17c277bb99 test(harness): fake-pi-rpc protocol double + F1 widget contract golden (D10)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 20:36:23 +02:00
marcopanandClaude Opus 4.8 ae9308470b chore(harness): quietStartup + project-local trust for clean RPC spawn
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>
2026-06-27 20:33:03 +02:00
marcopanandClaude Sonnet 4.6 5d9a0bb548 feat(harness): manifest provider/model/thinking/name + session new options (BE-6/7)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 20:29:40 +02:00
marcopanandClaude Sonnet 4.6 5e0e1cee03 test(harness): pin session list/show json keys + schema alias
- 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>
2026-06-27 20:27:18 +02:00
marcopanandClaude Sonnet 4.6 a10a872a99 feat(cli): tht session list --json + session show --json (Task 7)
- Add _list_sessions(sessions_root) pure helper: scans sessions dir,
  returns list[dict] with id/status/question/summary/created_at/
  updated_at/author, sorted by created_at desc.
- Add `tht session list` command: --json emits pristine JSON array,
  human mode prints one line per session.
- Add --json flag to `tht session show`: emits manifest
  (model_dump by_alias) + phase (current_phase) + has_schema_linking.
- Tests: 8 tests in test_session_list_json.py (TDD red→green).
- Full suite: 230 passed (--ignore=tests/l2).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 20:23:46 +02:00
marcopanandClaude Opus 4.8 85e2fdc00a fix(harness): correct truncated signalling for sql preview offset>0
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>
2026-06-27 20:18:58 +02:00
marcopanandClaude Sonnet 4.6 7d9769cfff feat(harness): tht sql preview --json + --offset for AGGrid paging (BE-2)
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>
2026-06-27 20:13:20 +02:00
marcopanandClaude Sonnet 4.6 0240242f5c feat(harness): gate uses externally-provided THT_SESSION id (BE-5)
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>
2026-06-27 20:07:25 +02:00
marcopan 8c4a1798da fix(harness): gate widget round-trip via native ctx.ui.input (no sendRaw); no-limbo 2026-06-27 20:02:19 +02:00