Audit findings 6.1-6.4 + the audit's remediation plan itself (docs/superpowers/plans/2026-07-20-full-audit-remediation-plan.md). - ruff: 34 → 0 (unused imports/f-strings auto-fixed; E702 semicolon lines split in test files; one unused local dropped). Suite still 819 green. - CLAUDE.md + PROJECT_STATE.md no longer claim "no database / settings in settings.json": the harness selects filesystem OR PostgreSQL session storage (repository.py, server mode), and settings flow through harness preferences with the JSON file as fallback only. - tools/replay: stub /me (SPA boot was parsing the SPA's own HTML as JSON) and /runtime/prewarm. - failSession best-effort persistence now logs its failure server-side instead of vanishing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
88 lines
5.4 KiB
Markdown
88 lines
5.4 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Start here
|
|
|
|
Read [PROJECT_STATE.md](PROJECT_STATE.md) for the current-state snapshot: what was last
|
|
built, pending manual gates, workspace/secret layout, and design-doc locations. This file
|
|
holds the stable commands + architecture mental model; PROJECT_STATE.md holds the evolving
|
|
detail. Design history lives in `docs/superpowers/specs/` and `docs/superpowers/plans/`.
|
|
|
|
## Commands
|
|
|
|
The repo has three independently-built layers. Run the **full stack** (real Pi + DWH, needs
|
|
VPN + `harness/.env` + `pi` on PATH) with `./scripts/run-stack.sh` (frontend :5173 → backend :8787).
|
|
|
|
**harness/** (Python `tht` CLI + Pi gate extension)
|
|
- Install: `cd harness && python -m venv .venv && pip install -e ".[dev]"` (puts `tht` on PATH)
|
|
- Test: `.venv/bin/pytest -q` — `l2` (real GLM + remote DB) is opt-in via `addopts = -m 'not l2'`; `l0` (testcontainers) needs Docker
|
|
- Single test: `.venv/bin/pytest tests/test_session_mutations.py::test_set_name -v` (or `-k <pattern>`); include e2e with `-m l2`
|
|
- Lint: `.venv/bin/ruff check .` (line-length 100)
|
|
|
|
**backend/** (Fastify + TypeScript, vitest)
|
|
- Dev: `npm run dev` (tsx watch `src/server.ts`) · Build: `npm run build` (tsc → `dist/`)
|
|
- Test: `npx vitest run` · Single: `npx vitest run test/routes-sessions.test.ts -t "rename"`
|
|
- Typecheck: `npx tsc --noEmit -p .` (vitest does NOT type-check — run this before committing)
|
|
|
|
**frontend/** (React 18 + Vite + vitest)
|
|
- Dev: `npm run dev` (Vite; set `VITE_BACKEND_URL`) · Build: `npm run build`
|
|
- Test: `npx vitest run` · Single: `npx vitest run src/shell/NavSessions.test.tsx`
|
|
- Typecheck: `npx tsc -b` · E2E: `npm run e2e` (Playwright)
|
|
|
|
No ESLint on the TS layers — `tsc` is the gate. Tests use vitest + MSW (no network).
|
|
|
|
## Architecture (the parts that need multiple files to see)
|
|
|
|
```
|
|
frontend (React/SSE) → backend (Fastify) → pi --mode rpc → tht/harness → DWH (read-only)
|
|
```
|
|
|
|
- **The harness owns the workflow and all persistence.** `tht` (Python) is a deterministic
|
|
CLI; `harness/.pi/extensions/tht-gate.js` is a Pi extension that drives an **8-phase
|
|
NL→SQL workflow**. The single source of workflow truth is `harness/workflow.yaml`; the
|
|
orchestration rules the model must follow are `harness/.pi/skills/tht-sessione/SKILL.md`.
|
|
"Current phase" is computed by folding the decision ledger (`harness/tht/phase.py`), not
|
|
stored — read it before reasoning about phase logic.
|
|
|
|
- **Persistence = phase documents, NOT chat.** A session is a directory under the workspace's
|
|
`sessions/` path: `session_manifest.yaml` + per-phase artifacts (`question.md`,
|
|
`schema_linking.json`, `sql_final.sql`, …) + `review_decisions.jsonl`. The contract
|
|
(SKILL.md): *"the persisted state is the truth — what is not recorded did not happen."*
|
|
There is no verbatim transcript store. A resumed Pi process rebuilds context from
|
|
`tht session show <id>` + the on-disk artifacts.
|
|
|
|
- **The backend is a thin bridge with no database of its own.** `ThtRunner` shells `tht`
|
|
subcommands; `PiProcessManager` runs one Pi child per session and bridges its RPC stream;
|
|
`SessionBridge` maps Pi RPC events → client events (`ui_request`/`text_delta`/`info`);
|
|
`SseHub` fans them out over SSE to the browser. Persistence belongs to the HARNESS, which
|
|
selects the session repository from the workspace config (`harness/tht/session/repository.py`):
|
|
filesystem by default, **PostgreSQL when `session_storage` is configured** (server/portable
|
|
deployment). Settings flow through harness preferences (`tht session preferences`) with
|
|
`backend/data/settings.json` only as the file fallback for injected runners/tests.
|
|
|
|
- **Human-in-the-loop gate contract.** The model proposes; a human reviewer decides at gates
|
|
via widgets (`reviewer_select` = single pick — a chosen option carrying a `decision` payload
|
|
auto-confirms/persists directly, an option without one only asks; `reviewer_decide` = multiselect,
|
|
each choice IS a decision; `reviewer_confirm` = artifact/phase gate). The frontend renders these
|
|
widget-descriptors (`src/widgets/` registry) and the live transcript is rebuilt in-memory
|
|
from the SSE stream (`src/store/sessionStore.ts`) — it is not persisted.
|
|
|
|
## Project-specific gotchas
|
|
|
|
- **`tht`'s `-c`/`--config` is a PER-COMMAND option** — it must follow the subcommand, never
|
|
precede it (`ThtRunner.buildArgv` enforces this; prepending caused live 500s).
|
|
- **`--json` output must be pristine** (only valid JSON on stdout) — used as a machine contract.
|
|
- **UI strings are English; document *content* stays the workspace language** (Italian for
|
|
`psd`) because it's the real data. Only chrome/labels are English.
|
|
- **Workspaces** (`harness/workspaces/*.yaml`) set the DB target and **absolute**
|
|
`paths.sessions/artifacts/indexes` — for `psd` these point at a *separate, uncommitted* repo
|
|
(`tht-workspace-psd/`). Secrets live ONLY in `harness/.env` (gitignored).
|
|
- **Settings are global** (workspace/provider/model/thinking, persisted via harness
|
|
preferences — `backend/data/settings.json` is only the fallback); the New-session form is
|
|
question-only.
|
|
- **Resume**: a resumable session re-enters at its last incomplete phase. The backend refuses
|
|
resume with 409 when `finalized` or `archived`, and `PiProcessManager.spawnFor` must send
|
|
`/riprendi-sessione <id>` (resume mode) vs `/nuova-domanda` (new) — sending the wrong prompt
|
|
silently turns a resume into a new question.
|