5.6 KiB
AGENTS.md
This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
Start here
Read 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 local Docker stack with ./scripts/run-stack.sh after creating deploy/env/local.env; it starts the base+local Compose profile with frontend, core, qdrant, embedding, and the one-shot embedding-model-init. The core image contains Pi. Qdrant and Ollama are internal Compose services; DWH and LLM remain external configuration endpoints.
Native host CLI tht (tools/tht/)
- Operator surface:
setup,start,stop,status,doctor,auth,workspace, andpi. - Use
tht --installation <absolute-path>/thothii-installation.yaml <command>for installation, authentication, diagnostics, lifecycle, and workspace operations.
harness/ (Python workflow tht CLI + Pi gate extension)
- Install:
cd harness && python -m venv .venv && pip install -e ".[dev]"(putsthton PATH) - Test:
.venv/bin/pytest -q—l2(real GLM + remote DB) is opt-in viaaddopts = -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 watchsrc/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; setVITE_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. The Python workflow CLI
thtinsidecoreis deterministic;harness/.pi/extensions/tht-gate.jsis a Pi extension that drives an 8-phase NL→SQL workflow. The single source of workflow truth isharness/workflow.yaml; the orchestration rules the model must follow areharness/.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 fromtht session show <id>+ the on-disk artifacts. -
The backend is a thin bridge with no database.
ThtRunnershells the Python workflowthtsubcommands insidecore;PiProcessManagerruns one Pi child per session and bridges its RPC stream;SessionBridgemaps Pi RPC events → client events (ui_request/text_delta/info);SseHubfans them out over SSE to the browser. App settings live in a JSON file (backend/data/settings.json), not a DB. -
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 adecisionpayload 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/--configis a PER-COMMAND option — it must follow the subcommand, never precede it (ThtRunner.buildArgvenforces this; prepending caused live 500s).--jsonoutput 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 absolutepaths.sessions/artifacts/indexes— forpsdthese point at a separate, uncommitted repo (tht-workspace-psd/). Secrets live ONLY inharness/.env(gitignored). - Settings are global (
backend/data/settings.json: workspace/provider/model/thinking); 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
finalizedorarchived, andPiProcessManager.spawnFormust send/riprendi-sessione <id>(resume mode) vs/nuova-domanda(new) — sending the wrong prompt silently turns a resume into a new question.