docs: publish English public documentation
Publish documentation / publish (push) Successful in 43s

This commit is contained in:
Codex
2026-08-26 10:54:44 +02:00
parent b5db0cd3c1
commit 7d32bb1e74
21 changed files with 1378 additions and 1326 deletions
+55
View File
@@ -0,0 +1,55 @@
name: Publish documentation
on:
push:
branches:
- main
paths:
- "docs/**"
- "mkdocs.yml"
- "docs/requirements.txt"
- ".gitea/workflows/publish-docs.yml"
workflow_dispatch:
permissions:
contents: write
concurrency:
group: documentation
cancel-in-progress: true
jobs:
publish:
runs-on: ubuntu-latest
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
REPOSITORY_URL: ${{ gitea.server_url }}/${{ gitea.repository }}.git
steps:
- name: Checkout documentation source
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.x"
cache: pip
cache-dependency-path: docs/requirements.txt
- name: Install MkDocs dependencies
run: python -m pip install -r docs/requirements.txt
- name: Build documentation
# Some documented source files intentionally live outside docs/.
run: mkdocs build
- name: Publish generated site to the pages branch
working-directory: site
run: |
git init
git config user.name "Gitea Actions"
git config user.email "actions@${{ gitea.server_url }}"
git add --all
git commit --message "Publish documentation for ${{ gitea.sha }}"
git -c http.extraheader="Authorization: token ${GITEA_TOKEN}" \
push --force "${REPOSITORY_URL}" HEAD:pages
+16 -2
View File
@@ -5,6 +5,20 @@ surface is one CLI, `tht`; there is no separate authentication executable. The b
opaque browser sessions and authorization, while `tht` owns protected configuration and local-user
files.
```mermaid
flowchart TB
BROWSER["Browser"] --> BOUNDARY["Authentication boundary"]
BOUNDARY --> LOCAL["Local users\nArgon2id hashes"]
BOUNDARY --> OIDC["OIDC provider\nAuthorization Code PKCE"]
OIDC --> GROUPS["Groups claim\nexact mapping"]
LOCAL --> PRINCIPAL["Thoth principal"]
GROUPS --> PRINCIPAL
PRINCIPAL --> ROLES["Roles"]
ROLES --> PERMISSIONS["Permissions"]
PERMISSIONS --> ROUTES["Protected routes"]
SECRETS["Mounted secret bundle"] -.-> BOUNDARY
```
## Configuration and trust boundaries
The installation descriptor points to an operator-controlled authentication directory. It contains
@@ -70,8 +84,8 @@ Workspace Validate performs static authentication validation without provider co
`tht auth check` performs live, non-interactive diagnosis: static safety plus OIDC discovery,
issuer/JWKS, group-catalog authentication, and exact configured-group existence. Adding
`--interactive` runs that same live diagnosis and then validates a device-flow identity when the
provider supports Device Authorization. Workspace Test is the aggregate live workspace and
authentication validation.
provider supports Device Authorization. Aggregate live workspace and authentication validation is
available through the installation diagnostics.
The ordered `tht doctor` report is exactly: `descriptor`, `files`, `docker`, `compose`,
`configuration`, `authentication`, `services`, `core-http`, `frontend-http`,
+174
View File
@@ -0,0 +1,174 @@
# Components, modules, and flows
This page complements the [architecture overview](overview.md) with the module structure and flows through ThothII. The diagrams describe the current code, not a future architecture.
## Modules and dependencies
The frontend communicates with the backend through REST and SSE. The backend does not own session persistence: it starts Pi, invokes the `tht` CLI, and forwards events. The harness contains the workflow, the Python CLI, and adapters for the DWH and vector store.
```mermaid
flowchart LR
FE["frontend/\nReact + Vite"] -->|REST + SSE| BE["backend/\nFastify + TypeScript"]
BE -->|RPC stdin/stdout| PI["Pi\n--mode rpc"]
BE -->|subprocess\nJSON stdout| THT["harness/tht\nCLI Python"]
PI --> EXT["harness/.pi/extensions/\ntht-gate.js"]
EXT --> SKILL["harness/.pi/skills/\ntht-sessione"]
EXT --> THT
THT --> FS["Sessions and artifacts\nworkspace repository"]
THT --> DWH["DWH\nread-only"]
THT --> VDB["Qdrant / vector store"]
BE --> CFG["settings.json\nworkspace registry"]
FE -.->|renders widgets| EXT
```
Dipendenze principali:
| Module | Depends on | Responsibility |
| --- | --- | --- |
| `frontend/` | Backend REST and SSE APIs | UI, gate widgets, and in-memory transcript |
| `backend/src/` | Pi, `tht`, configuration, and workspace registry | Transport, session lifecycle, and APIs |
| `harness/.pi/` | Pi and `tht phase` | Workflow orchestration and human-in-the-loop gates |
| `harness/tht/` | Filesystem, DWH, and vector store | Persistence, CLI, Evidence, schema, and preprocessing |
| workspace repository | `source/`, `curated/`, manifest, and artifacts | Versioned Evidence source and session output |
## Session sequence
The main path starts with a user question and ends with an SSE event. Reviewer decisions use the same channel and are persisted by the harness.
```mermaid
sequenceDiagram
actor U as User or reviewer
participant FE as Frontend
participant BE as Backend
participant PI as Pi RPC
participant THT as CLI tht
participant WS as Workspace
participant DWH as DWH
U->>FE: Send question or gate decision
FE->>BE: POST session / risposta widget
BE->>PI: RPC input o prompt di resume
PI->>THT: phase/session/evidence commands
THT->>WS: Read and write phase artifacts
THT->>DWH: Introspection or read-only query
DWH-->>THT: Schema, results, or diagnostics
THT-->>PI: JSON and phase state
PI-->>BE: RPC events and widget descriptor
BE-->>FE: SSE text_delta, info, ui_request
FE-->>U: Text, artifact, or review request
```
The backend uses `ThtRunner` for CLI subprocesses, `PiProcessManager` for one Pi process per session, `SessionBridge` to adapt RPC events, and `SseHub` to distribute them to clients.
## Main backend classes
The diagram shows the classes that form the bridge between the browser, Pi, and `tht`. Fastify routes receive requests and delegate to these services.
```mermaid
classDiagram
class ThtRunner {
+buildArgv(command, args) string[]
+run(args) Promise~ThtResult~
+sessionShow(id) Promise~unknown~
}
class PiProcessManager {
-runtimes Map
+spawnFor(sessionId, mode) SessionRuntime
+resume(sessionId, tht) Promise~SessionRuntime~
+stop(sessionId) Promise~void~
}
class SessionBridge {
+handleRpcEvent(event) ClientEvent
+handleUiResponse(response) Promise~void~
}
class SseHub {
+subscribe(sessionId) AsyncIterable
+publish(sessionId, event) void
+close(sessionId) void
}
class SessionRoutes {
+createSession(request) Response
+resumeSession(id) Response
+postInput(id, input) Response
}
class WorkspaceRegistry {
+list() Workspace[]
+resolve(id) Workspace
}
class SettingsStore {
+get() Settings
+update(patch) Settings
}
class App {
+buildApp() FastifyInstance
}
App --> SessionRoutes
App --> WorkspaceRegistry
App --> SettingsStore
SessionRoutes --> PiProcessManager
SessionRoutes --> ThtRunner
SessionRoutes --> SseHub
PiProcessManager --> SessionBridge
PiProcessManager --> ThtRunner
SessionBridge --> SseHub
```
## Python modules in the `tht` CLI
The CLI consists of Typer commands and domain modules. `cli/` turns arguments into operations; `evidence/`, `session/`, `db/`, `adapters/`, and the other packages contain the application logic.
```mermaid
flowchart TB
MAIN["tht/cli/__init__.py"] --> CMD["tht/cli/*_cmd.py"]
CMD --> CONFIG["config.py\nworkspace.py\npaths.py"]
CMD --> SESSION["session_cmd.py\nsession/"]
CMD --> EVIDENCE["evidence_cmd.py\nevidence/"]
CMD --> PRE["preprocess_cmd.py\nevidence/corpus/"]
CMD --> PHASE["phase_cmd.py\nphase.py\nworkflow.py"]
CMD --> SQL["sql_cmd.py\ndb/\nrest/"]
EVIDENCE --> ACQ["evidence/acquisition.py\nadapters/ filesystem/http/s3"]
EVIDENCE --> CANON["evidence/canonical.py\ncontracts.py\nmodel.py"]
EVIDENCE --> AUTHOR["evidence/authoring.py"]
PRE --> PIPE["evidence/corpus/pipeline.py\nchunk.py normalize.py store.py"]
PRE --> VECTOR["adapters/vector/qdrant.py"]
PRE --> DWH["jobs/dwh_pipeline.py\nadapters/dwh/"]
SESSION --> REPO["session/filesystem_repository.py\npostgres_repository.py"]
PHASE --> LEDGER["decisions.py\nreview_decisions"]
```
The operator command `tht` in `tools/tht/` is separate from the harness Python CLI. The former handles installation, lifecycle, authentication, and workspaces; the latter runs the workflow and data operations.
## Eight-phase workflow and gates
The source of truth is `harness/workflow.yaml`. The current phase is computed from the decision ledger, not from a manually updated field.
```mermaid
flowchart LR
F1["F1\nChiarimento"] --> F2["F2\nMemoria"]
F2 --> F3["F3\nRiscrittura"]
F3 --> F4["F4\nSchema linking\nreviewer_decide"]
F4 --> F5["F5\nSintesi"]
F5 --> F6["F6\nCTE\nauto o skip"]
F6 --> F7["F7\nSQL finale\nreviewer_confirm"]
F7 --> F8["F8\nDatamart\nreviewer_decide"]
F1 -.->|reviewer_confirm| F1
F3 -.->|reviewer_confirm| F3
F4 -.->|decisioni su tabelle, colonne, evidence| F4
F6 -.->|cte_approved o cte_rejected| F6
F7 -.->|sql_approved o sql_rejected| F7
F8 -.->|datamart_requested o declined| F8
```
| Fase | Nome | Avanzamento | Artefatti principali |
| --- | --- | --- | --- |
| F1 | chiarimento | `kind:phase` | decisioni di chiarimento |
| F2 | memoria | automatico se vuota | decisioni memoria |
| F3 | riscrittura | `kind:phase` | `question.md` |
| F4 | schema linking | `reviewer_decide` | `schema_linking.json` |
| F5 | sintesi | `kind:phase` | verifica dello schema linking |
| F6 | CTE | automatico, oppure skip | `cte_plan.json`, `ctes/`, `cte_tests.json` |
| F7 | SQL finale | `kind:phase` dopo `sql_approved` | `sql_final.sql` |
| F8 | datamart | `reviewer_decide` | decisione su richiesta o rifiuto |
Un `reviewer_select` con decisione incorporata può confermare direttamente. Un `reviewer_decide` registra le scelte multiple. Un `reviewer_confirm` conferma un artefatto o la chiusura della fase. Il modello propone; il revisore decide e il ledger registrato è la fonte dello stato.
+63 -38
View File
@@ -1,64 +1,89 @@
# Panoramica dell'architettura
# Architecture overview
> Sintesi ad uso documentazione. Per il dettaglio storico delle decisioni di design vedi le [Specifiche di Design](../superpowers/specs/2026-06-25-thothii-architecture-design.md) e i [Piani di Implementazione](../superpowers/plans/2026-06-25-harness-implementation.md). Per lo stato corrente del progetto (gate manuali pendenti, layout workspace/secret) vedi `PROJECT_STATE.md` nella radice del repo.
> For details about modules and flows, see [Components, modules, and flows](components.md).
ThothII è un **datamart builder human-in-the-loop**: trasforma una domanda in linguaggio naturale in SQL validato (ed eventualmente un datamart dbt) attraverso un **workflow deterministico a 8 fasi NL→SQL**, in cui il modello *propone* e un revisore umano *decide* ai gate.
ThothII is a **human-in-the-loop datamart builder**. It turns a natural-language question into validated SQL, and optionally a dbt datamart, through a **deterministic eight-phase NL-to-SQL workflow** in which the model *proposes* and a human reviewer *decides* at gates.
L'autenticazione di produzione usa local oppure OIDC generico; il solo CLI operatore è tht.
Per sessioni, ruoli, gruppi, diagnostica e ripristino vedere la [documentazione autenticazione](authentication.md).
Production authentication uses local authentication or generic OIDC. `tht` is the only operator CLI.
For sessions, roles, groups, diagnostics, and recovery, see the [authentication documentation](authentication.md).
## I tre progetti indipendenti
```mermaid
flowchart LR
USER["Reviewer"] --> FE["Frontend\nReact and SSE"]
FE --> BE["Backend\nFastify"]
BE --> PI["Pi\nRPC per sessione"]
PI --> THT["tht and harness\nworkflow and persistence"]
THT --> DWH["DWH\nread only"]
THT --> EVIDENCE["Evidence\ncurated corpus"]
EVIDENCE --> THT
THT --> FE
```
## The three independent projects
```
frontend (React/SSE) → backend (Fastify) → pi --mode rpc → tht/harness → DWH (sola lettura)
frontend (React/SSE) → backend (Fastify) → pi --mode rpc → tht/harness → DWH (read-only)
```
| Layer | Stack | Ruolo |
|---|---|---|
| **harness/** | Python (`tht` CLI) + Pi gate extension (JS) | Possiede il workflow e **tutta** la persistenza |
| **backend/** | Fastify + TypeScript | Ponte sottile senza database proprio |
| **frontend/** | React 18 + Vite | UI che renderizza i widget di gate e ricostruisce il transcript live dallo stream SSE |
| **harness/** | Python (`tht` CLI) + Pi gate extension (JS) | Owns the workflow and **all** persistence |
| **backend/** | Fastify + TypeScript | Thin bridge with no database of its own |
| **frontend/** | React 18 + Vite | UI that renders gate widgets and rebuilds the live transcript from the SSE stream |
## L'harness possiede il workflow
## The harness owns the workflow
`tht` (Python) è una CLI deterministica; `harness/.pi/extensions/tht-gate.js` è un'estensione Pi che guida il workflow a 8 fasi. La fonte di verità unica del workflow è `harness/workflow.yaml`; le regole di orchestrazione che il modello deve seguire sono in `harness/.pi/skills/tht-sessione/SKILL.md`. La "fase corrente" **non è memorizzata**: viene calcolata piegando il decision ledger (`harness/tht/phase.py`) — va letta prima di ragionare sulla logica di fase.
`tht` (Python) is a deterministic CLI. `harness/.pi/extensions/tht-gate.js` is a Pi extension that guides the eight-phase workflow. `harness/workflow.yaml` is the single source of workflow truth, and `harness/.pi/skills/tht-sessione/SKILL.md` contains the orchestration rules the model must follow. The "current phase" is **not stored**. It is computed by folding the decision ledger (`harness/tht/phase.py`), which must be read before reasoning about phase logic.
## Persistenza = documenti di fase, non chat
## Persistence means phase documents, not chat
Una sessione è una directory sotto `sessions/` (path definito dal workspace): `session_manifest.yaml` + artefatti per fase (`question.md`, `schema_linking.json`, `sql_final.sql`, …) + `review_decisions.jsonl`. Il contratto (SKILL.md): *"lo stato persistito è la verità — ciò che non è registrato non è accaduto"*. Non esiste uno store di transcript verbatim. Un processo Pi ripreso ricostruisce il contesto da `tht session show <id>` + gli artefatti su disco.
A session is a directory under `sessions/` (the workspace defines the path): `session_manifest.yaml`, phase artifacts (`question.md`, `schema_linking.json`, `sql_final.sql`, and others), and `review_decisions.jsonl`. The contract says: *"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>` and the artifacts on disk.
## Il backend è un ponte sottile senza database
## The backend is a thin bridge with no database
- `ThtRunner` esegue subcommand `tht` in shell
- `PiProcessManager` esegue un processo Pi figlio per sessione e fa da bridge al suo stream RPC
- `SessionBridge` mappa eventi RPC di Pi → eventi client (`ui_request` / `text_delta` / `info`)
- `SseHub` distribuisce questi eventi via SSE al browser
- `ThtRunner` runs `tht` subcommands in a shell.
- `PiProcessManager` runs one Pi child process per session and bridges its RPC stream.
- `SessionBridge` maps Pi RPC events to client events (`ui_request` / `text_delta` / `info`).
- `SseHub` distributes these events to the browser over SSE.
Le impostazioni applicative vivono in un file JSON (`backend/data/settings.json`), non in un database.
Application settings live in a JSON file (`backend/data/settings.json`), not in a database.
## Contratto del gate human-in-the-loop
## Human-in-the-loop gate contract
Il modello propone; un revisore umano decide ai gate tramite widget:
The model proposes; a human reviewer decides at gates through widgets:
- **`reviewer_select`** — scelta singola: un'opzione con `decision` payload auto-conferma/persiste direttamente; un'opzione senza payload chiede soltanto
- **`reviewer_decide`** — multiselect: ogni scelta È una decisione
- **`reviewer_confirm`** — gate su artefatto/fase
- **`reviewer_select`**: single choice. An option with a `decision` payload confirms and persists directly; an option without a payload only asks.
- **`reviewer_decide`**: multiselect. Each choice is a decision.
- **`reviewer_confirm`**: artifact or phase gate.
Il frontend renderizza questi widget-descriptor (registro in `src/widgets/`); il transcript live viene ricostruito in memoria dallo stream SSE (`src/store/sessionStore.ts`) — **non è persistito**.
The frontend renders these widget descriptors (registry in `src/widgets/`). It rebuilds the live transcript in memory from the SSE stream (`src/store/sessionStore.ts`); it is **not persisted**.
## Punti di attenzione ricorrenti
## Curated and immutable Evidence
- `tht -c`/`--config` è un'opzione **per-comando**: deve seguire il subcommand, mai precederlo (`ThtRunner.buildArgv` lo impone).
- L'output `--json` deve essere JSON puro su stdout — è un contratto machine-readable.
- Le stringhe UI sono in inglese; il *contenuto* dei documenti resta nella lingua del workspace (italiano per `psd`), perché è il dato reale — solo chrome/label sono in inglese.
- I workspace (`harness/workspaces/*.yaml`) impostano il target DB e i path **assoluti** `paths.sessions/artifacts/indexes` — per `psd` puntano a un repo separato e non versionato (`tht-workspace-psd/`). I segreti vivono solo in `harness/.env` (gitignored).
- Le impostazioni sono globali (`backend/data/settings.json`: workspace/provider/modello/thinking); il form di nuova sessione richiede solo la domanda.
- **Resume**: una sessione riprendibile rientra all'ultima fase incompleta. Il backend rifiuta il resume con 409 se `finalized` o `archived`; `PiProcessManager.spawnFor` deve inviare `/riprendi-sessione <id>` (resume) vs `/nuova-domanda` (nuova) — il prompt sbagliato trasforma silenziosamente un resume in una nuova domanda.
The workspace repository is the publication boundary. The curator prepares `evidence/source/`,
reviews units in `evidence/curated/`, validates them, and merges them. With `evidence.schema_version: 2`,
the runtime materializes the full `evidence/` tree from the exact Git commit, but the renderer passes
only `curated/**/*.md` from the immutable revision root to preprocessing. Sources, manifests, and
evaluation data remain available for traceability. The runtime never modifies, stages, commits, or
publishes the authoring repository.
## Come si lancia lo stack
Before indexing, the curated corpus from the pinned revision is validated. The shared Qdrant
collection keeps the unnamed dense vector used by Schema and Memory. Evidence preprocessing may
add only the sparse `bm25` vector with `idf`, without deleting, renaming, or recreating the collection.
`workspace preprocess evidence` and the Evidence part of `workspace preprocess run` are the only public
operations that perform this upgrade.
Lo stack locale si avvia con `./scripts/run-stack.sh`, dopo aver creato
`deploy/env/local.env` da `deploy/env/local.env.example`. Il core Compose include Pi; DWH,
vector DB, embedding e LLM sono endpoint esterni configurati nel file locale.
## Recurring points of attention
Comandi per singolo layer, test, lint: vedi il file `CLAUDE.md` nella radice del repo (guida operativa per Claude Code, tenuta sincronizzata con questa pagina).
- `tht -c`/`--config` is a **per-command** option. It must follow the subcommand, never precede it (`ThtRunner.buildArgv` enforces this).
- `--json` output must be plain JSON on stdout. It is a machine-readable contract.
- UI strings are in English. Document *content* stays in the workspace language because it is the actual data; only chrome and labels are in English.
- Each workspace defines its DWH target and working directories. Secrets remain in protected installation files, not in the workspace repository.
- Settings are global (`backend/data/settings.json`: workspace/provider/model/thinking); the new-session form asks only for the question.
- **Resume**: a resumable session returns to its last incomplete phase. The backend rejects resume with 409 when `finalized` or `archived`; `PiProcessManager.spawnFor` must send `/riprendi-sessione <id>` for resume and `/nuova-domanda` for a new session. The wrong prompt silently turns a resume into a new question.
## Starting the stack
Start the local stack with `./scripts/run-stack.sh` after creating
`deploy/env/local.env` from `deploy/env/local.env.example`. The Compose core includes Pi; DWH,
the vector database, embeddings, and the LLM are external endpoints configured in the local file.
+44 -12
View File
@@ -5,21 +5,58 @@ workspace descriptor. Evidence is optional: a valid v3 descriptor without it rem
When present, `evidence` is strict: it contains `source` and a defaulted strict `policy`; every
source variant and the policy reject unknown keys.
```mermaid
flowchart LR
REGISTRY["Workspace registry"] --> DESCRIPTOR["Evidence descriptor"]
DESCRIPTOR --> FILESYSTEM["Filesystem adapter"]
DESCRIPTOR --> HTTP["HTTP adapter"]
DESCRIPTOR --> S3["S3 adapter"]
FILESYSTEM --> CURATED["Curated markdown"]
HTTP --> CURATED
S3 --> CURATED
CURATED --> VALIDATE["Validate schema\nand provenance"]
VALIDATE --> PREPROCESS["Preprocess pinned\nrevision"]
PREPROCESS --> GENERATION["Versioned generation"]
GENERATION --> ACTIVE["Active corpus"]
```
## Filesystem source
A filesystem source uses the exact URI `<workspace.id>/evidence`. `patterns` is a nonempty list of
unique, normalized relative POSIX globs. Its defaults are `patterns: ["**/*.md"]` and
unique, normalized relative POSIX globs. The Evidence-local `schema_version` defaults to `1` for
compatibility, where an omitted filesystem pattern defaults to `patterns: ["**/*.md"]` and
`max_bytes: 10485760`.
`evidence.schema_version: 2` declares the source/curated authoring layout. Its omitted filesystem
pattern defaults to `patterns: ["curated/**/*.md"]`; if declared, the only accepted v2 filesystem
pattern list is exactly `patterns: ["curated/**/*.md"]`. A v2 descriptor that selects `source/`,
spans both `source/` and `curated/`, uses a broader curated glob, or selects a non-Markdown file is
rejected. Explicit safe legacy filesystem patterns remain supported under Evidence version 1. HTTP
and S3 sources do not use filesystem layout patterns and retain their existing contracts.
The v2 authoring tree is:
```text
evidence/
├── source/ # preserved original material
├── curated/ # reviewed Evidence Units indexed at runtime
├── manifest.yaml
└── evaluation.yaml
```
`source/`, the manifest, the evaluation set, and other support files are materialized for
traceability but never acquired by v2 runtime preprocessing.
### Example: filesystem
```yaml
evidence:
schema_version: 2
source:
type: filesystem
uri: example/evidence
patterns:
- "**/*.md"
- "curated/**/*.md"
max_bytes: 10485760
policy:
max_chunk_chars: 4000
@@ -152,8 +189,10 @@ catalog metadata exactly. Every catalog entry must have its descriptor at that s
catalog-only entries are invalid and reject the complete candidate revision.
Workspace source changes only through curator Git commit/push in a separate authoring clone,
followed by an installation pull. The API never writes `thoth-workspaces.yaml`,
`<id>/workspace.yaml`, `<id>/schema/**`, or `<id>/evidence/**`.
followed by an installation pull. Curator validation occurs before merge; activation and
preprocessing consume only the merged, pinned commit. The API and runtime never write
`thoth-workspaces.yaml`, `<id>/workspace.yaml`, `<id>/schema/**`, or `<id>/evidence/**` in the
authoring repository.
## Registry revision and phase ownership
@@ -164,7 +203,7 @@ followed by an installation pull. The API never writes `thoth-workspaces.yaml`,
| Repository consumer | ThothII fetches and validates a complete candidate, atomically activates it only on success, and never edits, commits, or pushes repository content. |
| Runtime secrets | Workspace management returns configured/missing status only; decrypted values exist only for the lifetime of a diagnostic or runtime lease. |
| P1.1 | Validates the lexical URI `<id>/evidence` and proves the declared filesystem root object is a Git tree at that same commit; it does not recursively inspect nested symlinks. Evidence materialization stays out of scope for P1.1. |
| P6 | Owns commit-addressed materialization, realpath and recursive containment, nested-symlink checks, and race checks. |
| P6 | Owns commit-addressed materialization of the complete Evidence tree, realpath and recursive containment, nested-symlink checks, and race checks. |
P1.1 performs no acquisition, extraction, preprocessing/indexing, embeddings, Qdrant writes,
active-snapshot retention, or GC.
@@ -179,10 +218,3 @@ tht config check -c <path>
```
Stop after validation. P2/P6 later owns preprocessing and materialization.
## Acceptance states
These gates are independent and are not implied by this documentation contract.
automated integration: PENDING
manual acceptance: PENDING
+33 -1
View File
@@ -2,6 +2,18 @@
`tht` is the only supported host entrypoint for workspace preprocessing.
```mermaid
flowchart LR
OP["Operator"] --> INVOKE["tht workspace preprocess"]
INVOKE --> VALIDATE["Validate descriptor\nand paths"]
VALIDATE --> SOURCE["Read source and\ncurated workspace"]
SOURCE --> NORMALIZE["Normalize and chunk"]
NORMALIZE --> INDEX["Update vector and\nBM25 indexes"]
INDEX --> VERIFY["Verify collection\nand generation"]
VERIFY --> READY["Generation ready"]
VALIDATE -->|"invalid"| STOP["Exit with diagnostic"]
```
## Invocation
```text
@@ -58,6 +70,20 @@ tht --installation <absolute>/thothii-installation.yaml workspace vector rebuild
performs the guarded rebuild; rebuild state is written before deletion and the
collection is verified after recreation. No prefix matching or global Qdrant
mutation is performed.
## Additive BM25 for Evidence
- Only `workspace preprocess evidence` (and the Evidence portion of `workspace preprocess run`)
may add the named sparse vector `bm25` with Qdrant modifier `idf`.
- The upgrade uses Qdrant's additive named-vector operation. It preserves the existing unnamed
dense vector and never deletes, renames, or rebuilds the shared collection.
- Session readiness remains read-only with respect to BM25. Schema, Memory, and solved-question
records therefore continue to use their existing dense-only points during and after an Evidence
upgrade.
- A missing `bm25` is added and reread before Evidence preprocessing starts. An existing definition
other than `modifier: idf` fails as `semantic_index_incompatible` without any collection mutation.
If a later Evidence candidate fails, the compatible additive schema remains in place; it does not
make the dense-only records unavailable.
```
## Curated FK annotations (P5)
@@ -92,7 +118,13 @@ tht --installation <absolute>/thothii-installation.yaml workspace vector rebuild
before any bytes are written and no partial root is published.
- `preprocess evidence` and `preprocess run` operate directly on the materialized root; the
temporary `evidence_materialization_required` stop is retired (the code remains only for
pre-P6 compatibility). HTTP/S3 Evidence is unchanged.
pre-P6 compatibility). For `evidence.schema_version: 2`, runtime acquisition receives exactly
`curated/**/*.md`; `source/` and support files remain in the materialized tree for traceability.
HTTP/S3 Evidence is unchanged.
- The curator validates Evidence before merge. Preprocessing validates the pinned curated corpus
again before it constructs a candidate generation, so an invalid revision is never indexed.
- The runtime writes only its immutable materialized snapshot and derived index state. It never
writes, stages, commits, or pushes the workspace authoring repository.
- Materialized roots are retained with their commit-addressed snapshot directory and removed only
when the revision becomes unreferenced.
+155 -142
View File
@@ -1,110 +1,127 @@
# Disambiguazione nelle prime fasi del workflow
# Disambiguation in the early workflow phases
La disambiguazione è il processo con cui Thoth trasforma una domanda naturale ambigua in un significato verificato dal reviewer prima di costruire lo schema-linking e il SQL.
Disambiguation turns an ambiguous natural-language question into a meaning that the reviewer verifies before schema linking and SQL generation begin.
Il principio architetturale è **human-in-the-middle**: il modello propone interpretazioni motivate, il reviewer decide, il gate persiste la decisione nel ledger. Il modello non può scegliere autonomamente un significato solo perché è quello semanticamente più vicino.
The architectural principle is **human-in-the-middle**: the model proposes reasoned interpretations, the reviewer decides, and the gate persists the decision in the ledger. The model cannot choose a meaning on its own simply because it is the closest semantic match.
La procedura è definita nella skill canonica [tht-sessione](../harness/.pi/skills/tht-sessione/SKILL.md), soprattutto nelle sezioni F1 e F2, ed è applicata dai widget in [tht-gate.js](../harness/.pi/extensions/tht-gate.js).
The canonical [tht-sessione](../harness/.pi/skills/tht-sessione/SKILL.md) skill defines the procedure, especially its F1 and F2 sections. The widgets in [tht-gate.js](../harness/.pi/extensions/tht-gate.js) enforce it.
## Dove avviene la disambiguazione
La disambiguazione iniziale attraversa quattro passaggi distinti:
```text
F1 Chiarimento → significato della domanda
F2 Memory → eventuale conoscenza già chiarita e riusabile
F3 Riscrittura → domanda esplicita e non ambigua
F4 Schema-linking → traduzione del significato in tabelle, colonne e join
```mermaid
stateDiagram-v2
[*] --> DETECT
state "Detect ambiguity" as DETECT
state "Build reviewer options" as PROPOSE
state "Ask with reviewer_select" as ASK
state "Multiple valid answers" as MULTI
state "Record accepted decision" as ACCEPTED
DETECT --> PROPOSE: ambiguity found
DETECT --> ASK: safe default unavailable
PROPOSE --> ASK
ASK --> ACCEPTED: one option selected
ASK --> MULTI: multiple answers valid
MULTI --> ACCEPTED
ACCEPTED --> [*]
```
Questi passaggi non sono intercambiabili:
## Where disambiguation happens
- F1 stabilisce cosa significa la domanda;
- F2 propone conoscenza preesistente, senza applicarla automaticamente;
- F3 rende esplicito il significato concordato;
- F4 sceglie gli oggetti tecnici necessari per quel significato.
Initial disambiguation has four distinct steps:
In particolare, una tabella scelta in F4 non è una disambiguazione concettuale e non deve diventare una memory.
```text
F1 Clarification → meaning of the question
F2 Memory → previously clarified knowledge that may be reused
F3 Rewriting → explicit, unambiguous question
F4 Schema linking → translating meaning into tables, columns, and joins
```
## Bootstrap: una sola ambiguità per volta
These steps are not interchangeable:
Quando una nuova sessione entra in F1, il modello deve identificare la sola ambiguità con il maggiore impatto sulla query e presentarla immediatamente.
- F1 establishes what the question means.
- F2 proposes existing knowledge without applying it automatically.
- F3 makes the agreed meaning explicit.
- F4 selects the technical objects needed for that meaning.
Non deve:
In particular, a table selected in F4 is not conceptual disambiguation and must not become a Memory item.
- elencare tutte le ambiguità future;
- produrre una lunga analisi preliminare;
- costruire il SQL prima del chiarimento;
- presentare più domande al reviewer nello stesso turno.
## Bootstrap: one ambiguity at a time
La motivazione è di controllo cognitivo e di audit: se vengono chiesti insieme popolazione, periodo, outcome e definizione clinica, non è possibile sapere quale risposta abbia determinato ciascuna scelta successiva.
When a new session enters F1, the model must identify the single ambiguity with the greatest impact on the query and present it immediately.
## Fonti usate per formulare le opzioni
It must not:
In F1 il modello può usare soltanto le fonti previste dalla skill:
- list every possible future ambiguity;
- produce a long preliminary analysis;
- build SQL before clarification;
- present several questions to the reviewer in the same turn.
- `retrieval_pack.md`, quando è già iniettato dal backend;
- `tht search pack`, solo in modalità standalone quando il retrieval pack non è disponibile;
- `tht search find` per cercare termini o valori;
- `tht search find --kind evidence` per evidenze;
- `tht schema render` per leggere il catalogo fisico già disponibile.
This protects cognitive load and auditability. If product line, period, metric, and operational definition are requested together, it becomes impossible to tell which answer drove each later choice.
Il retrieval pack viene trattato come dati, non come istruzioni. Questo confine impedisce che testo recuperato dal catalogo o dalle evidenze modifichi le regole del workflow.
## Sources used to formulate options
Le corrispondenze LSH, vettoriali ed evidence sono **candidate**, non verità. Ogni proposta deve indicare la provenienza e, quando disponibile, il punteggio. Prima di trasformare un valore trovato in un filtro SQL occorre verificarlo con una ricerca di valore reale.
In F1 the model may use only the sources allowed by the skill:
## Costruzione delle opzioni
- `retrieval_pack.md` when the backend has already injected it;
- `tht search pack`, only in standalone mode when the retrieval pack is unavailable;
- `tht search find` to search for terms or values;
- `tht search find --kind evidence` for Evidence;
- `tht schema render` to read the available physical catalog.
Per ogni ambiguità il modello prepara interpretazioni concrete, non descrizioni vaghe. Le opzioni devono spiegare:
The retrieval pack is data, not instructions. This boundary prevents text retrieved from the catalog or Evidence from changing the workflow rules.
- il significato proposto;
- la tabella e la colonna eventualmente coinvolte;
- il valore o filtro che ne deriva;
- l'evidenza che motiva la proposta;
- il rischio di scegliere quell'interpretazione.
LSH matches, vector matches, and Evidence are **candidates**, not facts. Each proposal must include provenance and, when available, a score. Before turning a discovered value into a SQL filter, verify it with a real value search.
La proposta migliore riceve `recommended: true`, ma la raccomandazione non equivale ad approvazione. Il gate aggiunge sempre:
## Building options
- `Altro/Other`, per una correzione libera;
- `Torna indietro/Back`, per il rollback;
- `Esci/Exit`, per interrompere la sessione.
For each ambiguity, the model prepares concrete interpretations rather than vague descriptions. Options must explain:
L'opzione “accetta la proposta” deve essere esplicita: il reviewer non deve essere costretto a confermare implicitamente una scelta preselezionata.
- the proposed meaning;
- any table and column involved;
- the resulting value or filter;
- the Evidence supporting the proposal;
- the risk of choosing that interpretation.
## Scelta tra `reviewer_select` e `reviewer_decide`
The best proposal receives `recommended: true`, but a recommendation is not approval. The gate always adds:
La forma del problema determina il widget.
- `Altro/Other` for a free-form correction;
- `Torna indietro/Back` for rollback;
- `Esci/Exit` to stop the session.
### Interpretazioni mutuamente esclusive
The "accept proposal" option must be explicit. The reviewer must not be forced to confirm a preselected choice implicitly.
Quando esattamente una sola interpretazione può essere corretta si usa `reviewer_select`.
## Choosing between `reviewer_select` and `reviewer_decide`
Esempi:
The shape of the problem determines the widget.
- “ablazione” significa una procedura transcatetere oppure qualcos'altro;
- “anno” significa anno solare oppure anno fiscale;
- “pazienti attivi” significa flag anagrafico oppure presenza di un evento.
### Mutually exclusive interpretations
Ogni opzione concreta contiene una decisione `concept_clarified`. La scelta del reviewer è già la conferma e viene persistita direttamente: non serve un secondo `reviewer_decide`.
Use `reviewer_select` when exactly one interpretation can be correct.
### Più risposte contemporaneamente valide
Examples:
Quando più interpretazioni possono essere vere nello stesso tempo si usa `reviewer_decide`, che visualizza un multiselect.
- "ablation" means a catheter procedure or something else;
- "year" means calendar year or fiscal year;
- "active bicycles" means catalog models or units in current production.
Esempi:
Each concrete option contains a `concept_clarified` decision. The reviewer's choice is also the confirmation and is persisted directly. A second `reviewer_decide` is not needed.
- la domanda comprende più popolazioni valide;
- sono possibili più codici di procedura;
- devono essere considerate più finestre temporali;
- più condizioni sono indipendentemente applicabili.
### Several answers can be valid at once
Usare `reviewer_select` in questi casi sarebbe fuorviante perché obbligherebbe il reviewer a sceglierne una sola.
Use `reviewer_decide`, which displays a multiselect, when several interpretations can be true at the same time.
In F1 le scelte multiple producono più decisioni `concept_clarified`, mentre la fase viene chiusa in seguito con il gate di fase.
Examples:
## Persistenza delle decisioni
- the question includes several valid populations;
- several procedure codes are possible;
- several time windows must be considered;
- several conditions apply independently.
La scelta del reviewer non rimane soltanto nella UI. Il gate registra nel ledger:
Using `reviewer_select` in these cases would mislead the reviewer by forcing a single choice.
In F1, multiple choices produce several `concept_clarified` decisions. The phase is closed later through the phase gate.
## Persisting decisions
The reviewer's choice does not remain only in the UI. The gate records this in the ledger:
```text
type = concept_clarified
@@ -113,128 +130,124 @@ detail = definizione o regola operativa
rationale = motivazione, evidenza e/o testo del reviewer
```
La regola “una decisione, un comando” impedisce al modello di scrivere direttamente il ledger con shell, `tht decision add` o `tht phase advance`. Il gate è l'unico punto autorizzato a trasformare il widget in stato persistito.
The "one decision, one command" rule prevents the model from writing to the ledger through the shell, `tht decision add`, or `tht phase advance`. The gate is the only component allowed to turn a widget interaction into persisted state.
La decisione è quindi riutilizzabile come memory solo dopo la promozione esplicita di F8. Anche in quel caso viene conservato il contesto originale e non viene trasferita la scelta delle tabelle.
The decision can therefore be reused as Memory only after explicit promotion in F8. The original context is preserved, and table choices are not transferred.
## Gestione di “Altro” e testo libero
## Handling `Altro` and free text
`Altro/Other` non è una scelta neutra e non può essere ignorato.
`Altro/Other` is not a neutral choice and cannot be ignored.
Quando il reviewer inserisce testo libero, il modello deve:
When the reviewer enters free text, the model must:
1. interpretare il testo nel contesto della domanda;
2. incorporarlo nella proposta successiva;
3. registrare le parole del reviewer nel `rationale`;
4. chiedere nuovamente se il testo resta ambiguo.
1. interpret the text in the context of the question;
2. include it in the next proposal;
3. record the reviewer's words in `rationale`;
4. ask again if the text remains ambiguous.
Non è ammesso tornare automaticamente alla prima opzione consigliata o scegliere in silenzio una semantica plausibile.
The system must not automatically return to the first recommended option or silently choose a plausible meaning.
Questo comportamento consente di distinguere una correzione umana da una semplice deselezione e mantiene l'audit leggibile.
This distinguishes a human correction from a simple deselection and keeps the audit readable.
## Ambiguità non risolta
## Unresolved ambiguity
Un'ambiguità non può sparire perché il modello non sa risolverla. Deve essere resa esplicita con un'opzione del tipo:
An ambiguity cannot disappear because the model does not know how to resolve it. Make it explicit with an option such as:
```text
Lasciare aperta l'ambiguità
Leave the ambiguity open
```
L'opzione deve spiegare:
The option must explain:
- quale parte della query resta indeterminata;
- quale rischio introduce;
- quale effetto può avere su filtri, conteggi o join.
- which part of the query remains undetermined;
- what risk this introduces;
- how it may affect filters, counts, or joins.
Il reviewer può quindi accettare consapevolmente il rischio oppure chiedere ulteriori ricerche.
The reviewer can then accept the risk knowingly or request more research.
## Chiusura di F1
## Closing F1
Ogni singolo widget può registrare uno o più chiarimenti, ma non chiude automaticamente F1. Quando il chiarimento è completo il modello presenta `reviewer_confirm kind:"phase"`.
Each widget can record one or more clarifications, but it does not close F1 automatically. When clarification is complete, the model presents `reviewer_confirm kind:"phase"`.
Il riepilogo di chiusura deve contenere l'intero insieme dei chiarimenti della fase, non solo l'ultimo. Il gate aggiunge inoltre le decisioni registrate dal ledger, evitando che il modello debba ricopiarle manualmente.
The closing summary must contain every clarification from the phase, not only the latest one. The gate also adds the decisions recorded in the ledger, so the model does not have to copy them by hand.
La chiusura F1 avanza a F2. La domanda non viene ancora riscritta: `question_rewritten` appartiene a F3.
Closing F1 advances to F2. The question is not rewritten yet: `question_rewritten` belongs to F3.
## F2: memory come supporto alla disambiguazione
## F2: Memory as disambiguation support
F2 non sostituisce il chiarimento umano. Cerca memory concettuali già promosse:
F2 does not replace human clarification. It searches for previously promoted conceptual Memory:
```text
tht memory search "<domanda>" --session <id> --json
```
Il risultato viene proposto in una checklist unica. Sono ammesse solo memory `concept_clarified`; le decisioni su tabelle, colonne o SQL non sono trasferibili.
The result is presented as one checklist. Only `concept_clarified` Memory is allowed; decisions about tables, columns, or SQL cannot be transferred.
Se il reviewer applica una memory:
If the reviewer applies a Memory item:
- viene registrato un nuovo `concept_clarified` nella sessione corrente;
- il `rationale` cita l'id `mem-XXXX` della fonte;
- la scelta viene comunque contestualizzata nella domanda corrente.
- a new `concept_clarified` is recorded in the current session;
- `rationale` cites the source's `mem-XXXX` ID;
- the choice is still placed in the context of the current question.
Se il reviewer deseleziona una memory, essa non viene applicata ora, ma non viene cancellata globalmente e può essere riproposta dopo una riapertura di F2.
If the reviewer deselects a Memory item, it is not applied now. It is not deleted globally and may be proposed again after F2 is reopened.
## F3: rendere esplicito il risultato
## F3: make the result explicit
F3 trasforma i chiarimenti accettati in una domanda riscritta:
F3 turns accepted clarifications into a rewritten question with:
- popolazione espressa con termini del modello dati;
- condizioni separate e numerate;
- concetti ambigui sostituiti dalle definizioni concordate;
- output atteso esplicito;
- assunzioni dichiarate.
- the population expressed using data-model terms;
- separate, numbered conditions;
- ambiguous concepts replaced by the agreed definitions;
- an explicit expected output;
- stated assumptions.
La riscrittura non introduce nuove scelte implicite. Se emerge un'ambiguità sostanziale, il percorso corretto è riaprire F1, non “aggiustare” il significato dentro F3 o dentro il SQL.
Rewriting must not introduce new implicit choices. If a material ambiguity appears, reopen F1 rather than "fixing" the meaning in F3 or SQL.
## F4: disambiguazione tecnica dello schema
## F4: technical schema disambiguation
Solo dopo F3 la disambiguazione semantica viene tradotta in oggetti tecnici.
Only after F3 is the semantic meaning translated into technical objects.
Il modello propone:
The model proposes:
- tabelle da promuovere o escludere;
- colonne candidate;
- colonne di output;
- join necessari.
- tables to include or exclude;
- candidate columns;
- output columns;
- required joins.
Il reviewer cura le tabelle e le colonne con `reviewer_schema_linking`. I join vengono trattati in una revisione separata `reviewer_decide` join-only.
The reviewer curates tables and columns with `reviewer_schema_linking`. Joins are handled in a separate join-only `reviewer_decide` review.
Questa separazione è importante: una tabella può essere corretta per una domanda e totalmente irrilevante per un'altra. Per questo le decisioni F4 sono locali alla sessione e non diventano memory.
This separation matters. A table can be correct for one question and completely irrelevant to another. F4 decisions are therefore local to the session and do not become Memory.
## Riapertura e rollback
## Reopening and rollback
Se il reviewer usa “Torna indietro”, la sessione riprende dalla fase indicata esaminando gli artefatti ancora validi.
When the reviewer uses "Torna indietro", the session resumes from the selected phase and examines the artifacts that are still valid.
Gli artefatti oltre la fase riaperta vengono invalidati da `tht phase reopen`; quelli precedenti non vanno rigenerati senza motivo. `effective_decisions()` esclude le decisioni stale, così un chiarimento superato non può alimentare una nuova promozione memory o una nuova sintesi SQL.
Artifacts after the reopened phase are invalidated by `tht phase reopen`; earlier artifacts should not be regenerated without a reason. `effective_decisions()` excludes stale decisions, so an outdated clarification cannot feed a new Memory promotion or SQL synthesis.
## Invarianti di sicurezza e qualità
## Security and quality invariants
La disambiguazione è affidabile perché la stessa regola è applicata su più livelli:
Disambiguation is reliable because the same rule is enforced at several levels:
1. la skill prescrive una sola ambiguità per volta;
2. il gate offre widget vincolati e controlli `Altro/Back/Exit`;
3. il ledger registra le decisioni e il rationale;
4. i prerequisiti impediscono di saltare fasi;
5. F4 separa concetti da schema-linking;
6. le memory accettano solo `concept_clarified`;
7. rollback ed `effective_decisions()` escludono stato obsoleto.
1. the skill requires one ambiguity at a time;
2. the gate provides constrained widgets and `Altro/Back/Exit` controls;
3. the ledger records decisions and rationale;
4. prerequisites prevent phases from being skipped;
5. F4 separates concepts from schema linking;
6. Memory accepts only `concept_clarified`;
7. rollback and `effective_decisions()` exclude obsolete state.
Il risultato è una catena verificabile:
The result is a verifiable chain:
```text
termine ambiguo
→ evidenza e candidate interpretations
→ scelta esplicita del reviewer
→ concept_clarified nel ledger
→ domanda riscritta
→ schema-linking locale
→ piano CTE e SQL
ambiguous term
→ Evidence and candidate interpretations
→ explicit reviewer choice
→ concept_clarified in the ledger
→ rewritten question
→ local schema linking
→ CTE plan and SQL
```
## Riferimenti
## References
- [Skill canonica completa](skill-tht-sessione.md)
- [Workflow YAML](../harness/workflow.yaml)
- [Gate Pi](../harness/.pi/extensions/tht-gate.js)
- [Macchina delle fasi](../harness/tht/phase.py)
- [Gestione delle memory](gestione-memory.md)
- [Memory management](gestione-memory.md)
+181
View File
@@ -0,0 +1,181 @@
# Evidence: sources, preparation, and review
This page describes the complete workspace Evidence lifecycle: where original material lives, how curated units are produced, when they become available at runtime, and what the author and reviewer are responsible for.
## Publication rule
The workspace repository is the versioned source. ThothII reads it, validates it, and publishes an atomic generation. It does not modify, commit, or push the author's repository.
Evidence becomes available to the workflow only when:
1. the original material is in `source/`;
2. the derived unit is in `curated/`;
3. the manifest links the unit, source, and hash;
4. validation finds no errors or unresolved review items;
5. preprocessing builds and activates an indexed generation.
A proposal generated during a session is not automatically published Evidence. The model may propose a formula or explanation, but a curator must import, review, and publish it in the repository before another session can retrieve it.
## Where the original source belongs
For filesystem Evidence v2, the authoritative original source must be in the `source/` directory of the workspace repository. `curated/` contains the reviewed and indexed result, not the original material.
```text
<workspace-repository>/
├── source/ # materiale originale, preservato
│ └── <domain>/<file>.md
├── curated/ # reviewed Evidence Units
│ └── <domain>/<unit>.md
├── manifest.yaml # preparation links, hashes, and metadata
└── example/ # examples and supporting material
```
The workspace descriptor must declare `evidence.schema_version: 2` and use exactly this configuration for a filesystem source:
```yaml
evidence:
schema_version: 2
source:
type: filesystem
uri: "<workspace.id>/evidence"
patterns:
- "curated/**/*.md"
```
The legacy configuration may expose `source_root`, such as `${THT_DOCS_ROOT}` or `/data`. For the v2 structure, the runtime pattern must select only `curated/**/*.md`. Do not index `source/` directly, mix `source/` and `curated/`, use broader globs, or include non-Markdown files.
HTTP and S3 are separate adapters. They do not use the filesystem structure `source/` and `curated/`, but they must still provide stable provenance, without credentials in URIs, under the adapter-specific contract.
## What a curated unit must contain
Markdown units read by the legacy CLI loader use YAML frontmatter. The minimum fields are `id` and `title`; `tier`, `status`, `sources`, `tables`, and `concepts` describe the unit's context.
```markdown
---
id: evidence:autonomia-batteria
title: Nominal battery range
tier: structural
status: reviewed
sources:
- source/domain/bicycle.md
tables:
- bicycle_model
concepts:
- concept:battery-range
---
Verified definition of nominal range for an electric bicycle model.
The rule must be atomic enough to cite without reconstructing an entire chapter. The text must distinguish the definition, conditions, and limits.
```
Curated units must be atomic, readable by a second reviewer, and supported by the source. Provenance references must lead back to the original file and the passage that supports the claim. Do not put secrets, tokens, passwords, or credentials in metadata or URIs.
The modern canonical form also stores the Evidence kind, provenance, supporting excerpts, `source_file`, and `source_sha256`. The canonical contract rejects unknown fields, mutable metadata, and URIs containing credentials. Identifiers must remain stable even when a unit's kind changes.
## Preparation: from source to active generation
```mermaid
flowchart TD
SRC["source/DOMAIN/*.md\noriginal material"] --> PREP["tht evidence prepare\ncandidate preparation"]
PREP --> CAND["curated/DOMAIN/*.md\nproposed or updated units"]
CAND --> VAL["tht evidence validate\nstructure and link checks"]
VAL -->|errors or review items| FIX["Author corrections\nand review"]
FIX --> PREP
VAL -->|publishable| COMMIT["Commit del repository\nauthoring clone"]
COMMIT --> ING["tht preprocess evidence\nnormalization and chunking"]
ING --> BM25["BM25 index"]
ING --> VEC["Embeddings and vector store"]
BM25 --> GEN["Candidate generation"]
VEC --> GEN
GEN --> ACT["Active generation"]
ACT --> RUNTIME["Evidence retrieval in the workflow"]
```
Preparation can restructure changed sources, but it does not publish by itself. `prepare` produces a proposal and can identify the document involved in an error. `validate` does not write or publish. The curator publishes the revision. The runtime reads a complete, validated revision, then the pipeline creates a versioned generation. Activation is atomic, and a previous generation remains available under the retention policy.
Runtime retrieval is hybrid. The dense branch uses embeddings, the BM25 branch uses lexical search, and deterministic fusion orders the results. The published unit keeps its provenance, which the model must cite when it uses the Evidence.
## Author responsibilities
The author prepares the material and makes every unit verifiable. The author must:
- put the original material in `source/` without changing its meaning during curation;
- split the content into atomic units, with one rule or definition per unit when possible;
- assign a stable identifier and a clear title;
- provide provenance, tables, and related concepts when known;
- keep the text in the workspace language;
- separate facts, rules, examples, formulas, and limits;
- include excerpts that support the unit without extending the conclusion beyond the source;
- run `tht evidence prepare` and `tht evidence validate`;
- resolve every error and review item before proposing a commit;
- give the reviewer the necessary context, including source changes and the reason for any rename or retirement.
The author must not:
- write directly to the active production corpus;
- treat a model proposal as a verified fact;
- delete an unsupported unit without recording its retirement or relink;
- put credentials in metadata, files, or provenance URLs;
- manually change manifests, hashes, or generations to make validation pass.
## Reviewer responsibilities
The reviewer does not approve text merely because it is clear. The reviewer checks the relationship between source, unit, and intended use. For each unit, the reviewer must check:
1. the cited source exists in the reviewed revision;
2. the excerpt actually supports the claim;
3. the unit does not combine incompatible rules or independent concepts;
4. tables, columns, and concepts are identified correctly;
5. the identifier is stable and does not duplicate another unit;
6. the text distinguishes the definition, condition, exception, and example;
7. it contains no sensitive information or details absent from the source;
8. retrieval evaluation covers relevant queries and does not hide empty results.
The reviewer can approve, request changes, reject, retire, or relink a unit to a new source. Retirement must be explicit. A relink must name the new file and leave a verifiable record of the decision. Approval does not publish immediately: the repository must pass validation and the generation must pass evaluation before activation.
## Available commands
Authoring commands operate on the workspace repository and do not publish directly.
```bash
# Prepare changed sources. Does not commit or publish.
tht evidence prepare <workspace-root>
# Reprocess all sources with the installed pipeline.
tht evidence prepare <workspace-root> --upgrade
# Validate structure, manifest, links, and review items.
tht evidence validate <workspace-root>
# Return JSON for CI or automated tools.
tht evidence validate <workspace-root> --json
# Evaluate retrieval on a generation or the active generation.
tht evidence evaluate <workspace-root> --config <workspace-config>
tht evidence evaluate <workspace-root> --config <workspace-config> --generation <id> --json
# Resolve a unit without publishing: retire it or link it to a new source.
tht evidence resolve <workspace-root> evidence:<id> --retire
tht evidence resolve <workspace-root> evidence:<id> --source source/domain/nuovo.md
# Materialize and index a versioned generation.
tht preprocess evidence --config <workspace-config>
# Dry run and resume a job when supported by the configuration.
tht preprocess evidence --config <workspace-config> --dry-run
tht preprocess evidence --config <workspace-config> --resume <run-id>
```
`evidence prepare`, `evidence validate`, and `evidence resolve` require the repository path. `preprocess evidence` uses the workspace configuration because it needs the embedding, vector store, retention policy, and artifact directory.
Exit codes are part of the operating contract: `evidence validate` returns `0` when the corpus is publishable, `1` for validation errors, and `3` when only review items or orphaned units remain. With `--json`, stdout must contain valid JSON only.
## Formulas and session proposals
Formulas use a format distinct from document Evidence. A formula proposed during a session may be cited in the current proposal, but it does not enter the runtime corpus, receive a usable `evidence:` ID, or write to the repository. To become published, it must follow the same import, review, and preprocessing path as other units.
## Contract references
- [Workspace Evidence v3 contract](contracts/workspace-evidence-v3.md)
- [Preprocessing CLI contract](contracts/workspace-preprocessing-cli.md)
+106 -96
View File
@@ -1,6 +1,10 @@
# Configurazione dei modelli in Pi: built-in, utente, progetto
# Local Pi model configuration
Pi (il coding agent che orchestra il workflow NL→SQL) può risolvere un `provider/model` in tre modi diversi. Non sono alternativi: coesistono, e la scelta di quale usare dipende da **quanto è standard l'endpoint** e da **quanto deve essere ampia la visibilità** del modello (tutti i progetti vs. un progetto solo).
Pi orchestrates the NL-to-SQL workflow and resolves built-in models and OpenAI-compatible providers
declared in the local catalog. ThothII applies a stricter rule than Pi: code in
`harness/.pi/extensions/` cannot register a provider or custom model. Endpoints, protocols,
compatibility settings, and model identifiers belong exclusively in the local files
`deploy/pi/models.json` and `deploy/pi/settings.json`.
> **ThothII operator note:** ThothII runs Pi only in Docker Compose. Paths under
> `~/.pi/agent/` in this document describe Pi's container-side behavior. Operators edit
@@ -8,60 +12,66 @@ Pi (il coding agent che orchestra il workflow NL→SQL) può risolvere un `provi
> the protected host credential file selected by `PI_AUTH_FILE`; they do not edit files inside
> the running container.
## Credenziali nel backend container
## Credentials in the backend container
In produzione configurare una sola sorgente generica: il bundle `THT_SECRETS_FILE` con la voce
`THT_MODEL_API_KEY` (predefinito della distribuzione unificata), oppure
`THT_MODEL_API_KEY_FILE` come secret file assoluto. Non mettere il valore della chiave in `.env`.
`PiProcessManager` rilegge e valida la sorgente per ogni processo, normalizza il provider
selezionato e passa al solo child Pi la variabile nativa appropriata
(`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `ZAI_API_KEY`, ecc.). Il percorso generico,
le chiavi di provider non selezionati e il vecchio `PI_PROVIDER_API_KEY` vengono rimossi dall'ambiente
del child. Provider locali come `ollama`, `lmstudio` e `aritmolab` continuano senza chiave; un provider
hosted non mappato o un secret mancante/non sicuro fallisce prima dello spawn con errore sanitizzato.
In production, configure one generic source: the `THT_SECRETS_FILE` bundle with the
`THT_MODEL_API_KEY` entry, which is the default for the unified distribution, or
`THT_MODEL_API_KEY_FILE` as an absolute secret-file path. Do not put the key value in `.env`.
For each process, `PiProcessManager` rereads and validates the source, normalizes the selected
provider, and passes only the appropriate native variable to the Pi child
(`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `ZAI_API_KEY`, and so on). It removes the
generic path, keys for unselected providers, and the old `PI_PROVIDER_API_KEY` from the child
environment. For a custom provider, the backend derives the variable name from the declarative
`apiKey` field in `models.json`; a literal value means the catalog is self-contained. Provider-name
exceptions are not compiled into the code. A missing or insecure secret fails before spawn with a
sanitized error.
La sorgente generica supporta soltanto provider con una singola chiave: `ant-ling`, `anthropic`,
`cerebras`, `deepseek`, `fireworks`, `github-copilot`, `google` (anche tramite alias `gemini`),
`google-vertex` in modalità API key, `groq`, `huggingface`, `kimi-coding`, `minimax`, `minimax-cn`,
The generic source supports only single-key providers: `ant-ling`, `anthropic`,
`cerebras`, `deepseek`, `fireworks`, `github-copilot`, `google` (including the `gemini` alias),
`google-vertex` in API-key mode, `groq`, `huggingface`, `kimi-coding`, `minimax`, `minimax-cn`,
`mistral`, `moonshotai`, `moonshotai-cn`, `nvidia`, `openai`, `opencode`, `opencode-go`,
`openrouter`, `together`, `vercel-ai-gateway`, `xai`, i quattro provider `xiaomi*`, `zai` e
`openrouter`, `together`, `vercel-ai-gateway`, `xai`, the four `xiaomi*` providers, `zai`, and
`zai-coding-cn`.
I provider composti `amazon-bedrock`, `azure-openai-responses`, `cloudflare-workers-ai` e
`cloudflare-ai-gateway` non sono rappresentabili da un solo file. La selezione fallisce prima
dello spawn (anche durante l'elenco modelli); tutte le credenziali ambientali AWS, Azure e
Cloudflare restano comunque rimosse. Servirà una futura configurazione dedicata per provider per
supportare questi bundle senza ambiguità.
The composite providers `amazon-bedrock`, `azure-openai-responses`, `cloudflare-workers-ai`, and
`cloudflare-ai-gateway` cannot be represented by one file. Selection fails before spawn, including
when models are listed. AWS, Azure, and Cloudflare environment credentials are still removed. A
future provider-specific configuration will be needed to support these bundles unambiguously.
## I tre livelli di provenienza di un modello
## Model sources
### 1. Built-in (compilato dentro Pi)
### 1. Built-in (compiled into Pi)
Pi viene distribuito con un elenco di modelli già noti (`models.generated.js` dentro il pacchetto `@earendil-works/pi-ai`): Anthropic, OpenAI, Google, e anche provider di terze parti con API pubblica ben nota come DeepSeek. Per questi **non serve alcuna configurazione**: bastano le credenziali (env var o `pi auth`).
Pi ships with a list of known models (`models.generated.js` in the `@earendil-works/pi-ai` package):
Anthropic, OpenAI, Google, and third-party providers with well-known public APIs such as DeepSeek.
These require **no configuration** beyond credentials, provided through an environment variable or
`pi auth`.
`deepseek/deepseek-v4-pro` è così: è nella build di Pi perché `api.deepseek.com` è un'API pubblica documentata, non un endpoint interno.
`deepseek/deepseek-v4-pro` is one of these models. It is included in Pi because `api.deepseek.com`
is a documented public API, not an internal endpoint.
### 2. `models.json` a livello utente (`~/.pi/agent/models.json`)
### 2. User-level `models.json` (`~/.pi/agent/models.json`)
Per un endpoint **OpenAI-compatible** che non è tra i built-in — ma che non richiede nessuna logica di trasporto speciale — basta *dichiararlo*: baseUrl, apiKey, lista modelli. Questo file esiste **solo a livello utente**: non c'è un equivalente project-level (un `./.pi/models.json` non viene letto).
For an **OpenAI-compatible** endpoint that is not built in but needs no special transport logic,
declare it with its baseUrl, apiKey, and model list. This file exists **only at user level**; there is
no project-level equivalent, and `./.pi/models.json` is not read.
Nelle installazioni gestite da ThothII il file deve essere interamente dichiarativo. ThothII
rifiuta ricorsivamente qualsiasi valore JSON che inizi con `!`, anche dentro `headers`, `models`,
`modelOverrides`, `compat`, array o campi non ancora conosciuti. Pi 0.80.3 tratterebbe quel prefisso
come un comando shell al momento della richiesta; questa forma non è ammessa né dall'elenco gestito
dei modelli né dallo smoke isolato. L'errore restituito è fisso e non include comando, percorso o
secret.
In ThothII-managed installations, the file must be entirely declarative. ThothII recursively
rejects any JSON value that starts with `!`, including values inside `headers`, `models`,
`modelOverrides`, `compat`, arrays, or fields it does not yet know. Pi 0.80.3 would treat that
prefix as a shell command when making a request, so managed model catalogs do not allow it. The
returned error is fixed and contains no command, path, or secret.
Per i secret usare un riferimento ambiente come `"$ZAI_API_KEY"` o `"${ZAI_API_KEY}"`. Il backend
può popolare la variabile nativa del solo provider selezionato leggendo
`THT_MODEL_API_KEY_FILE`, oppure dal bundle `THT_SECRETS_FILE` (`THT_MODEL_API_KEY`); in alternativa
le credenziali possono arrivare dal file protetto montato con `PI_AUTH_FILE`, omettendo `apiKey` da
`models.json`. Non esiste una sintassi di riferimento diretto a un secret file dentro
`models.json`: con le sorgenti `THT_MODEL_*` il file viene letto da ThothII e trasformato nella
variabile ambiente del child Pi; `PI_AUTH_FILE` viene invece montato come archivio credenziali Pi
protetto. Per un punto esclamativo letterale iniziale, la sintassi Pi dichiarativa è `$!`, non `!`.
For secrets, use an environment reference such as `"$ZAI_API_KEY"` or `"${ZAI_API_KEY}"`. The
backend can populate the native variable for the selected provider by reading
`THT_MODEL_API_KEY_FILE`, or from the `THT_SECRETS_FILE` bundle (`THT_MODEL_API_KEY`). Credentials
can also come from the protected file mounted through `PI_AUTH_FILE`, with `apiKey` omitted from
`models.json`. There is no direct secret-file reference syntax in `models.json`. With `THT_MODEL_*`
sources, ThothII reads the file and turns it into the Pi child's environment variable; `PI_AUTH_FILE`
is mounted instead as a protected Pi credential store. For a literal leading exclamation mark, Pi's
declarative syntax is `$!`, not `!`.
Esempio reale in uso su questa macchina — GLM (provider `zai`):
Real example in use on this machine: GLM (provider `zai`):
```json
{
@@ -84,113 +94,113 @@ Esempio reale in uso su questa macchina — GLM (provider `zai`):
}
```
Essendo a livello utente, GLM è visibile da **qualsiasi progetto**.
Because it is user-level, GLM is visible to **every project**.
### 3. Estensione (`.pi/extensions/*.js`, utente o progetto)
### Extension providers: not allowed in ThothII
Quando l'endpoint richiede codice — ad esempio conversione di eventi (thinking→text), un `streamSimple` custom, o comunque logica che un file dichiarativo non può esprimere — serve un'estensione Pi che chiama `pi.registerProvider(...)`. Le estensioni possono vivere sia in `~/.pi/agent/extensions/` (tutti i progetti) sia in `<progetto>/.pi/extensions/` (solo quel progetto, se Pi viene lanciato con quella cwd).
Pi technically supports providers registered by JavaScript extensions, but ThothII does not use
that capability. Project extensions are reserved for the workflow and gates; they must not contain
`registerProvider(...)`. An endpoint that cannot be described by the OpenAI-compatible catalog is
not supported by this installation until the declarative contract is extended generically.
Esempio reale — il provider AritmoLab (Qwen), interno all'ospedale, usato solo da ThothII: [harness/.pi/extensions/aritmolab-provider.js](../../harness/.pi/extensions/aritmolab-provider.js).
## Summary table (current machine state)
## Tabella riassuntiva (stato attuale di questa macchina)
| Modello | Livello | Perché | Visibilità |
| Model | Level | Why | Visibility |
|---|---|---|---|
| `deepseek/deepseek-v4-pro` | Built-in Pi | API pubblica nota, già nella build | Tutti i progetti |
| `zai/glm-5.2` | `~/.pi/agent/models.json` | Endpoint OpenAI-compatible custom (z.ai), nessuna logica speciale | Tutti i progetti |
| `aritmolab/qwen3.6-35b-a3b` | Estensione di progetto | Endpoint interno ospedaliero + conversione eventi thinking→text custom | Solo ThothII (cwd=`harness/`) |
| `deepseek/deepseek-v4-pro` | Built-in Pi | Known public API, already in the build | All projects |
| `zai/glm-5.3` | `deploy/pi/models.json` | Custom OpenAI-compatible endpoint | ThothII installation |
| `local-qwen/qwen3.6-35b-a3b` | `deploy/pi/models.json` | Locally configured OpenAI-compatible endpoint | ThothII installation |
## Come scegliere il livello giusto per un nuovo modello
## Choosing the right level for a new model
1. **L'endpoint è un'API pubblica già nota a Pi?** → niente da fare, verifica con `pi --list-models`.
2. **È OpenAI-compatible, nessuna logica custom, e deve essere visibile ovunque?** → `~/.pi/agent/models.json`.
3. **Serve codice custom (auth non standard, conversione eventi, trasporto non-OpenAI) oppure deve restare visibile a un solo progetto?** → estensione, in `~/.pi/agent/extensions/` (globale) o `<progetto>/.pi/extensions/` (locale).
1. **Is the endpoint a public API already known to Pi?** Enable the exact identifier in `deploy/pi/settings.json`.
2. **Is it OpenAI-compatible but not built in?** Declare it in `deploy/pi/models.json`, then enable it in `deploy/pi/settings.json`.
3. **Does it require provider-specific transport code?** Do not add a provider-specific extension. The provider is unsupported until a generic declarative capability exists.
---
## Ambito utente vs. progetto — riepilogo generale
## User versus project scope: general summary
Oltre ai modelli, Pi carica altre risorse da due alberi paralleli: `~/.pi/agent/` (utente) e `<cwd>/.pi/` (progetto, risolto in base alla directory da cui viene lanciato `pi`).
In addition to models, Pi loads other resources from two parallel trees: `~/.pi/agent/` (user) and
`<cwd>/.pi/` (project, resolved from the directory where `pi` is launched).
| File/Directory | Livello utente | Livello progetto | Auto-discovery | Precedenza |
|---|---|---|---|---|
| `models.json` | `~/.pi/agent/models.json` | non supportato | no | solo utente |
| `settings.json` | `~/.pi/agent/settings.json` | `./.pi/settings.json` | no | progetto sovrascrive utente |
| `extensions/` | `~/.pi/agent/extensions/` | `./.pi/extensions/` | sì (`.ts`/`.js`) | uniti (progetto + utente) |
| `prompts/` | `~/.pi/agent/prompts/` | `./.pi/prompts/` | sì (`.md`) | uniti |
| `themes/` | `~/.pi/agent/themes/` | `./.pi/themes/` | sì (`.json`) | progetto preferito |
| `skills/` | `~/.pi/agent/skills/` | `./.pi/skills/` | sì (`.md`) | uniti |
| `models.json` | `~/.pi/agent/models.json` | not supported | no | user only |
| `settings.json` | `~/.pi/agent/settings.json` | `./.pi/settings.json` | no | project overrides user |
| `extensions/` | `~/.pi/agent/extensions/` | `./.pi/extensions/` | yes (`.ts`/`.js`) | merged (project + user) |
| `prompts/` | `~/.pi/agent/prompts/` | `./.pi/prompts/` | yes (`.md`) | merged |
| `themes/` | `~/.pi/agent/themes/` | `./.pi/themes/` | yes (`.json`) | project preferred |
| `skills/` | `~/.pi/agent/skills/` | `./.pi/skills/` | yes (`.md`) | merged |
### Esempio reale: ThothII
### Real example: ThothII
```
harness/.pi/
├── extensions/
│ ├── aritmolab-provider.js ← provider LLM solo-progetto
│ ├── tht-gate.js ← gate human-in-the-loop
│ ├── reserved-labels.mjs ← utility condivisa (NON auto-caricata; .mjs ignorato)
│ └── gate/ ← modulo usato da tht-gate.js
├── settings.json ← (opzionale) override delle impostazioni utente
│ ├── tht-gate.js # human-in-the-loop gate
│ └── gate/
│ ├── core/ # shared gate enforcement and utilities
│ ├── disambiguation/ # F1/F3 policy
│ └── memory/ # F2/F8 policy
├── settings.json # optional override of user settings
└── themes/
└── thothii-mono.json ← tema del progetto
└── thothii-mono.json # project theme
```
### Comportamento rispetto alla cwd
### Behavior relative to cwd
La directory da cui lanci `pi` determina quale `.pi/` di progetto viene trovata:
The directory from which you launch `pi` determines which workflow extensions are found, but not
which models ThothII makes available. The catalog is mounted in the container's Pi agent directory.
```bash
# Da harness/ — trova harness/.pi/extensions/aritmolab-provider.js
# From harness/: load the project gate and mounted local catalog
cd /path/to/ThothII/harness
pi --model aritmolab/qwen3.6-35b-a3b "..."
# Dalla radice di ThothII — nessun .pi/ trovato lì o nei genitori, solo config utente
cd /path/to/ThothII
pi --model aritmolab/qwen3.6-35b-a3b "..." # ❌ Error: model not found
pi --model local-qwen/qwen3.6-35b-a3b "..."
```
Il backend di ThothII (`PiProcessManager`, `list-models.ts`, `model-matrix.mjs`) lancia sempre `pi` con `cwd: harnessDir`, per questo Qwen è visibile in produzione.
The ThothII backend always launches Pi with `cwd: harnessDir` for the workflow. Model availability
continues to depend only on `models.json`, `settings.json`, and local credentials.
---
## Trappola nell'auto-discovery: `.mjs` viene ignorato
## Auto-discovery trap: `.mjs` is ignored
Il pattern di auto-discovery delle estensioni in Pi è **`/\.(ts|js)$/`** — non include `.mjs`.
Pi's extension auto-discovery pattern is **`/\.(ts|js)$/`**; it does not include `.mjs`.
```
.pi/extensions/
├── my-extension.js ✅ auto-caricata
├── my-extension.ts ✅ auto-caricata
├── my-extension.mjs ❌ ignorata silenziosamente (non combacia col pattern)
└── shared-module.mjs ✅ va bene per moduli helper (deliberatamente non caricato come estensione)
├── my-extension.js ✅ auto-loaded
├── my-extension.ts ✅ auto-loaded
├── my-extension.mjs ❌ silently ignored (does not match the pattern)
└── shared-module.mjs ✅ suitable for helper modules (deliberately not loaded as an extension)
```
Se serve un modulo condiviso importato da un'estensione, usa `.mjs` proprio per evitare che venga trattato come estensione a sé.
If an extension imports a shared module, use `.mjs` so Pi does not treat it as an extension on its own.
---
## Verifica
### Elenco modelli
### List models
```bash
pi --list-models
```
Mostra i built-in + i modelli utente da `models.json`. **Non mostra** i provider registrati da estensione (come AritmoLab) — quelli vanno verificati con la cwd giusta.
This shows built-in models and models declared in `models.json`.
### Verifica che un'estensione sia caricata
### Check the catalog used by the application
```bash
cd harness # o la cwd rilevante per il progetto
cd harness # or the project's relevant cwd
pi --mode rpc
# poi: {"type": "get_available_models", "id": "1"}
```
La risposta RPC include tutti i modelli disponibili, inclusi quelli da estensione.
The RPC response must include only built-in models or models declared in the local catalog.
---
## Troubleshooting
| Problema | Causa | Soluzione |
| Problem | Cause | Solution |
|---|---|---|
| `Model "X/Y" not found` lanciando da fuori progetto | Il modello è registrato da un'estensione locale, non visibile fuori dalla cwd giusta | Lancia `pi` dalla directory di progetto corretta (es. `harness/`) |
| Estensione non caricata pur essendo nella cartella giusta | File `.mjs` invece di `.js`/`.ts` | Rinomina in `.js` |
| Impostazioni di progetto non applicate | `settings.json` di progetto ha errori di sintassi, o si sta lanciando `pi` dalla cwd sbagliata | Valida il JSON, controlla la cwd |
| `Model "X/Y" not found` | Provider/model missing from `models.json` or identifier missing from `enabledModels` | Fix the two local files and reload Pi |
| Project settings not applied | Project `settings.json` has a syntax error, or `pi` is launched from the wrong cwd | Validate the JSON and check the cwd |
+145 -136
View File
@@ -1,57 +1,65 @@
# Gestione delle memory
# Memory management
Questo documento descrive l'organizzazione attuale delle memory nel workflow ThothII: modello concettuale, ciclo di vita, persistenza, ricerca semantica, gate di revisione, visualizzazione delle sessioni e principali limiti tecnici.
This document describes how Memory currently works in ThothII: its conceptual model, lifecycle, persistence, semantic search, review gates, session display, and main technical limits.
## Sintesi architetturale
## Architectural summary
Una memory è conoscenza di dominio riutilizzabile tra domande. Non è una copia dello schema-linking di una singola domanda.
A Memory item is domain knowledge that can be reused across questions. It is not a copy of one question's schema linking.
```text
F1: chiarimento di un concetto
│
▼
decisione concept_clarified nel ledger della sessione
│
▼
F8: il reviewer decide se promuoverla
│
├── registro globale registry.jsonl
└── indice semantico Qdrant
│
▼
F2 di una sessione futura
ricerca e proposta al reviewer
```mermaid
flowchart TB
CLARIFY["F1 concept clarified"] --> REVIEW["F8 reviewer review"]
REVIEW -->|"accepted"| REGISTRY["registry.jsonl"]
REVIEW -->|"declined"| LOCAL["Session decision only"]
REGISTRY --> VECTOR["Qdrant semantic index"]
VECTOR --> FUTURE["Future F2 retrieval"]
FUTURE --> PROPOSAL["Reviewer proposal"]
```
L'invariante principale è `REUSABLE_TYPES = {"concept_clarified"}`: le sole memory generabili, salvabili, ricercabili e proponibili sono i concetti chiariti. Le decisioni `table_promoted`, `table_excluded`, `column_promoted` e analoghe restano decisioni locali alla domanda.
```text
F1: clarify a concept
│
▼
concept_clarified decision in the session ledger
│
▼
F8: reviewer decides whether to promote it
│
├── global registry registry.jsonl
└── Qdrant semantic index
│
▼
F2 in a future session
search and proposal to the reviewer
```
Implementazione principale: [harness/tht/memory.py](../harness/tht/memory.py:14) e [harness/tht/cli/memory_cmd.py](../harness/tht/cli/memory_cmd.py:368).
The main invariant is `REUSABLE_TYPES = {"concept_clarified"}`: only clarified concepts can be generated, saved, searched, or proposed as Memory. Decisions such as `table_promoted`, `table_excluded`, and `column_promoted` remain local to the question.
## I tre livelli della gestione
## The three management levels
| Livello | Contenuto | Funzione |
| Level | Content | Function |
| --- | --- | --- |
| Ledger della sessione | `concept_clarified`, `memory_promoted`, `memory_promotion_declined` | Audit e stato della singola sessione |
| Registro globale | Record `mem-XXXX` in `registry.jsonl` | Archivio canonico attuale delle memory |
| Indice Qdrant | Embedding e metadati derivati dal registro | Ricerca semantica |
| Session ledger | `concept_clarified`, `memory_promoted`, `memory_promotion_declined` | Audit and state for one session |
| Global registry | `mem-XXXX` records in `registry.jsonl` | Current canonical Memory archive |
| Qdrant index | Embeddings and metadata derived from the registry | Semantic search |
Il ledger contiene la provenienza e le decisioni umane. Il record globale contiene il testo riutilizzabile. L'indice vettoriale è una proiezione per la ricerca, non il posto in cui il workflow registra direttamente le decisioni.
The ledger contains provenance and human decisions. The global record contains reusable text. The vector index is a search projection, not the place where the workflow records decisions directly.
## Che cosa può diventare una memory
## What can become Memory
Durante F1 il workflow registra i chiarimenti come decisioni `concept_clarified`. Un chiarimento può esprimere:
During F1, the workflow records clarifications as `concept_clarified` decisions. A clarification can express:
- definizioni di concetti clinici o organizzativi;
- criteri di inclusione ed esclusione di una popolazione;
- formule e metodi di calcolo;
- interpretazioni temporali;
- mapping verso tabelle e colonne specifiche;
- significato di flag, codici o indicatori.
- definitions of production or organizational concepts;
- inclusion and exclusion criteria for a product line;
- formulas and calculation methods;
- interpretations of time periods;
- mappings to specific tables and columns;
- the meaning of flags, codes, or indicators.
Il modello `MemoryRecord` contiene:
The `MemoryRecord` model contains:
- `id`, ad esempio `mem-0001`;
- timestamp, sessione e sequenza della decisione originale;
- `id`, such as `mem-0001`;
- the timestamp, session, and sequence of the original decision;
- `type`;
- `subject`;
- `detail`;
@@ -59,177 +67,178 @@ Il modello `MemoryRecord` contiene:
- `question_context`;
- `tables` e `concepts`.
Per le nuove memory il tipo è sempre `concept_clarified` e `tables` viene inizializzato vuoto. Una tabella o una colonna può essere citata dentro la spiegazione come mapping tecnico; non può essere il concetto autonomo della memory.
For new Memory items, the type is always `concept_clarified` and `tables` starts empty. A table or column may appear in the explanation as a technical mapping, but it cannot be the Memory item's standalone concept.
Esempio valido:
Valid example:
> Per ablazione si intende una procedura con `ablazione_transcatetere = TRUE`, conteggiata con `COUNT(DISTINCT cod_paz)` per anno.
> Ablation means a procedure with `ablazione_transcatetere = TRUE`, counted with `COUNT(DISTINCT cod_paz)` by year.
Esempi non validi:
Invalid examples:
- `fact_cardioversione` come memory approvata;
- `dim_time` come memory rifiutata;
- una decisione “includi questa tabella” salvata per domande future.
- `fact_cardioversione` as approved Memory;
- `dim_time` as rejected Memory;
- an "include this table" decision saved for future questions.
La regola è documentata anche nella skill del workflow, in [harness/.pi/skills/tht-sessione/SKILL.md](../harness/.pi/skills/tht-sessione/SKILL.md:217).
The workflow skill also documents this rule in [harness/.pi/skills/tht-sessione/SKILL.md](../harness/.pi/skills/tht-sessione/SKILL.md:217).
## Promozione alla fine della sessione: F8
## Promotion at the end of the session: F8
Alla fine del workflow il gate `reviewer_memory_promote` esegue una preview deterministica:
At the end of the workflow, the `reviewer_memory_promote` gate runs a deterministic preview:
```text
tht memory promote --session <id> --preview --json
```
La preview:
The preview:
1. legge le decisioni effettive della sessione;
2. considera solo `concept_clarified`;
3. scarta le decisioni già promosse;
4. scarta le sequenze già rifiutate in F8;
5. deduplica contenuti equivalenti;
6. propone al massimo cinque candidati.
1. reads the session's effective decisions;
2. considers only `concept_clarified`;
3. discards decisions already promoted;
4. discards sequences already declined in F8;
5. deduplicates equivalent content;
6. proposes at most five candidates.
Il codice applica il filtro e la deduplica in [harness/tht/memory.py](../harness/tht/memory.py:199); il gate applica un ulteriore filtro difensivo in [harness/.pi/extensions/tht-gate.js](../harness/.pi/extensions/tht-gate.js:628).
The code applies filtering and deduplication in
[harness/tht/memory/core.py](../harness/tht/memory/core.py); the gate applies an additional defensive filter in
[harness/.pi/extensions/gate/memory/index.js](../harness/.pi/extensions/gate/memory/index.js).
Il reviewer vede un'unica checklist, preselezionata. Per ogni candidato:
The reviewer sees one preselected checklist. For each candidate:
- selezionato: viene eseguito `tht memory save-one` e poi viene registrato `memory_promoted`;
- deselezionato: viene registrato `memory_promotion_declined`;
- nessun candidato: F8 si chiude automaticamente.
- selected: `tht memory save-one` runs, followed by a `memory_promoted` record;
- deselected: records `memory_promotion_declined`;
- no candidates: F8 closes automatically.
Il marker `memory_promoted` usa `detail: seq:N`, cioè un riferimento alla decisione `concept_clarified` originale. Il flusso è in [harness/.pi/extensions/tht-gate.js](../harness/.pi/extensions/tht-gate.js:1691).
The `memory_promoted` marker uses `detail: seq:N`, a reference to the original `concept_clarified` decision. The flow is in [harness/.pi/extensions/tht-gate.js](../harness/.pi/extensions/tht-gate.js:1691).
La promozione non viene eseguita in F2 e il modello non può inventare candidati F8. I comandi diretti di promozione sono inoltre protetti dal gate anti-bypass.
Promotion does not run in F2, and the model cannot invent F8 candidates. Direct promotion commands are also protected by the anti-bypass gate.
## Persistenza globale
## Global persistence
### Registro JSONL
### JSONL registry
Il registro attuale è:
The current registry is:
```text
<artifacts>/memory/registry.jsonl
```
La scrittura viene fatta tramite file temporaneo e `os.replace`, quindi la sostituzione del registro è atomica. L'idempotenza della promozione è basata sulla coppia `session_id + decision_seq`: la stessa decisione della stessa sessione non genera due record globali.
The registry is written through a temporary file and `os.replace`, so replacement is atomic. Promotion is idempotent on the `session_id + decision_seq` pair: the same decision from the same session cannot create two global records.
### Qdrant
Dopo la promozione, `save-one` costruisce un solo `VectorRecord` e lo invia all'indice Qdrant. Il testo indicizzato include:
After promotion, `save-one` builds one `VectorRecord` and sends it to the Qdrant index. The indexed text includes:
- tipo e soggetto;
- dettaglio;
- motivazione;
- domanda di contesto;
- eventuali concetti e mapping.
- type and subject;
- detail;
- rationale;
- question context;
- any concepts and mappings.
Il record vettoriale usa l'id `memory:mem-XXXX`, mentre i metadati conservano `subject`, `detail`, `rationale`, `tables`, `concepts` e il discriminante `kind`. L'hash SHA-256 del contenuto impedisce di ricalcolare embedding e upsert quando il testo non è cambiato.
The vector record uses the ID `memory:mem-XXXX`. Its metadata stores `subject`, `detail`, `rationale`, `tables`, `concepts`, and the `kind` discriminator. The content's SHA-256 hash prevents embedding and upsert work when the text has not changed.
Il comportamento è implementato in [harness/tht/memory.py](../harness/tht/memory.py:253) e [harness/tht/memory.py](../harness/tht/memory.py:305).
This behavior is implemented in
[harness/tht/memory/core.py](../harness/tht/memory/core.py).
### Fonte canonica attuale
### Current canonical source
Oggi il registro JSONL è ancora la fonte canonica applicativa e Qdrant resta un indice derivato ma persistente. Il workflow non registra direttamente le decisioni nel vector DB: usa Qdrant come proiezione interrogabile del registro e del ledger effettivo.
The JSONL registry remains the application's canonical source, while Qdrant is a derived but persistent index. The workflow does not record decisions directly in the vector database. It uses Qdrant as a searchable projection of the registry and effective ledger.
## Riutilizzo in F2
## Reuse in F2
In una sessione futura F2 esegue:
In a future session, F2 runs:
```text
tht memory search "<domanda>" --session <id> --json
tht memory search "<question>" --session <id> --json
```
Il comando:
The command:
1. crea l'embedding della domanda;
2. cerca nel vector store solo record `kind=memory`;
3. risolve ogni hit nel registro JSONL tramite il suo `ref`;
4. scarta record assenti dal registro;
5. scarta qualsiasi tipo diverso da `concept_clarified`;
6. esclude le memory già decise nella sessione corrente;
7. restituisce i risultati ordinati per similarità.
1. creates an embedding for the question;
2. searches the vector store for records with `kind=memory` only;
3. resolves each hit in the JSONL registry through its `ref`;
4. discards records missing from the registry;
5. discards every type other than `concept_clarified`;
6. excludes Memory already decided in the current session;
7. returns results ordered by similarity.
L'implementazione è in [harness/tht/cli/memory_cmd.py](../harness/tht/cli/memory_cmd.py:368).
Memory is never applied automatically. The model must present it in one `reviewer_decide` choice:
Le memory non vengono mai applicate automaticamente. Il modello deve presentarle in un'unica scelta `reviewer_decide`:
- a selected Memory item is recorded as a new `concept_clarified` in the current session;
- the rationale must cite the original `mem-XXXX` ID;
- a deselected Memory item means "do not apply it now", not "delete it globally".
- una memory selezionata viene registrata come nuovo `concept_clarified` nella sessione corrente;
- il rationale deve citare l'id originale `mem-XXXX`;
- una memory deselezionata significa “non applicarla ora”, non “cancellarla globalmente”.
If F2 is reopened, a deselected Memory item can be proposed again. `memory_rejected` remains supported for legacy decisions and sessions, but it is not the normal behavior for current F2 deselection.
Se F2 viene riaperta, una memory deselezionata può quindi essere proposta di nuovo. `memory_rejected` resta supportato per decisioni e sessioni legacy, ma non rappresenta il normale comportamento della deselezione F2 attuale.
## Effective ledger, rollback, and reopening
## Ledger effettivo, rollback e riaperture
The ledger is append-only. Reopening and withdrawing decisions do not delete earlier rows, but they change which decisions are effective.
Il ledger è append-only. Riaperture e ritrattazioni non cancellano le righe precedenti; cambiano però quali decisioni sono effettive.
Memory helpers use `effective_decisions()` to:
Gli helper memory usano `effective_decisions()` per:
- exclude withdrawn decisions;
- ignore decisions from phases that became stale after a rollback;
- prevent promotion of clarifications that are no longer valid.
- escludere decisioni ritirate;
- ignorare decisioni appartenenti a fasi diventate stale dopo un rollback;
- impedire la promozione di chiarimenti non più validi.
The effective view is defined in [harness/tht/phase.py](../harness/tht/phase.py:82).
La vista effettiva è definita in [harness/tht/phase.py](../harness/tht/phase.py:82).
## Display in the session summary
## Visualizzazione nel riepilogo della sessione
The "Memories" section of the summary is projected at runtime from the session ledger; it is not a direct copy of the global registry.
La sezione “Memories” del riepilogo viene proiettata a runtime dal ledger della sessione; non è una copia diretta del registro globale.
The projection:
La proiezione:
- shows approved Memory first;
- then shows declined Memory;
- resolves `seq:N` to the original `concept_clarified`;
- hides markers whose original record is not `concept_clarified`;
- hides subjects that match schema-linking tables;
- hides standalone subjects shaped like `fact_*` or `dim_*`;
- keeps table and field references when they are part of the conceptual explanation.
- mostra prima le memory approved;
- mostra poi le memory declined;
- risolve `seq:N` verso il `concept_clarified` originale;
- nasconde marker il cui record originale non è `concept_clarified`;
- nasconde soggetti che corrispondono a tabelle dello schema-linking;
- nasconde soggetti autonomi con forma `fact_*` o `dim_*`;
- mantiene invece i riferimenti a tabelle e campi quando fanno parte della spiegazione concettuale.
The logic is in [harness/tht/session/store.py](../harness/tht/session/store.py:237). The frontend renders a structured list and treats `subject`, `detail`, and `rationale` as Markdown instead of showing raw Markdown.
La logica è in [harness/tht/session/store.py](../harness/tht/session/store.py:237). Il rendering frontend usa una lista strutturata e tratta `subject`, `detail` e `rationale` come Markdown, evitando di mostrare il markdown grezzo.
The transformation happens on read. Historical sessions use the current layout and filters without rewriting their original artifacts.
La trasformazione avviene in lettura: anche le sessioni storiche vengono organizzate con il layout e i filtri correnti senza riscrivere gli artefatti originali.
## Enforced invariants
## Invarianti applicate
The protections are distributed across several boundaries:
Le protezioni sono distribuite su più confini:
1. `REUSABLE_TYPES` in the Python core;
2. the `memory search` command filter;
3. the F8 preview filter;
4. filtering and deduplication in the Pi gate;
5. exclusion of table Memory from the UI projection.
1. `REUSABLE_TYPES` nel core Python;
2. filtro del comando `memory search`;
3. filtro della preview F8;
4. filtro e deduplica nel gate Pi;
5. esclusione delle table-memory nella proiezione UI.
This prevents one prompt or component change from reintroducing tables as Memory.
Questo evita che una singola modifica al prompt o a un solo componente reintroduca le tabelle come memory.
## Remaining limits and risks
## Limiti e rischi residui
### The registry and index are not one transaction
### Registro e indice non sono una singola transazione
Il salvataggio segue sostanzialmente questa sequenza:
Saving broadly follows this sequence:
```text
registro JSONL → Qdrant → marker memory_promoted nel ledger
JSONL registry → Qdrant → memory_promoted marker in the ledger
```
Se Qdrant non è disponibile, il registro può contenere una memory non ancora ricercabile; il comando segnala che sarà necessario reindicizzare.
If Qdrant is unavailable, the registry can contain Memory that is not yet searchable. The command reports that reindexing is required.
Se il marker del ledger fallisce dopo il salvataggio nel vector DB, la memory può risultare globalmente presente ma senza audit completo nella sessione. Il gate restituisce un comando di recupero manuale.
If the ledger marker fails after the vector database save, the Memory item can exist globally without a complete session audit. The gate returns a manual recovery command.
### Limite di cinque candidati
### Five-candidate limit
F8 propone al massimo cinque memory. Se una sessione produce più di cinque concetti validi, gli elementi eccedenti non vengono mostrati e la sessione può essere finalizzata senza promuoverli.
F8 proposes at most five Memory items. If a session produces more than five valid concepts, the extra items are not shown and the session can be finalized without promoting them.
### Deduplica non globale
### Deduplication is not global
La deduplica impedisce duplicati nella stessa proposta e l'idempotenza impedisce di ripromuovere la stessa decisione. Non esiste però una fusione globale di due memory semanticamente simili provenienti da sessioni diverse.
Deduplication prevents duplicates within one proposal, and idempotency prevents the same decision from being promoted twice. There is no global merge of semantically similar Memory items from different sessions.
### Vecchi record fisici
### Old physical records
Vecchi record `table_promoted` o `table_excluded` possono ancora esistere in artefatti o indici storici. Il codice attuale li rende non riutilizzabili filtrandoli per tipo e non li mostra nella proiezione delle sessioni. La loro eventuale rimozione fisica dal vector DB resta un'attività di bonifica separata.
Old `table_promoted` or `table_excluded` records may still exist in historical artifacts or indexes. The current code makes them unusable by filtering by type and does not show them in session projections. Physically removing them from the vector database remains a separate cleanup task.
## Valutazione finale
## Final assessment
La gestione attuale è coerente con il requisito funzionale: una memory è una conoscenza concettuale riutilizzabile, non una scelta di schema-linking.
The current implementation matches the functional requirement: Memory is reusable conceptual knowledge, not a schema-linking choice.
La parte più solida è la difesa multilivello del tipo `concept_clarified`. Il principale debito tecnico riguarda invece la convivenza del registro JSONL con Qdrant e l'assenza di una transazione unica tra archivio globale, indice semantico e ledger della sessione.
The strongest part is the multilayer protection of the `concept_clarified` type. The main technical debt is the coexistence of the JSONL registry and Qdrant, with no single transaction spanning the global archive, semantic index, and session ledger.
+145 -152
View File
@@ -1,69 +1,69 @@
# ThothII — Guida utente
# ThothII user guide
Per login, **Remember me**, ruoli, invalidazione delle sessioni, gruppi OIDC e ripristino, vedere
la [guida autenticazione locale](install/authentication-local.md) e la [guida OIDC generica](install/authentication-oidc.md).
For login, **Remember me**, roles, session invalidation, OIDC groups, and recovery, see the
[local authentication guide](install/authentication-local.md) and the [generic OIDC guide](install/authentication-oidc.md).
Questa guida accompagna passo-passo chi deve **preparare** il repository dei workspace, **usare
gli strumenti** ThothII per quel repository e **usare l'applicazione** per fare domande in
linguaggio naturale e ottenere SQL validato. Usa parole semplici ed esempi; i dettagli tecnici
restano nei contratti citati in fondo.
This guide walks you through **preparing** the workspace repository, **using ThothII's tools** for
that repository, and **using the application** to ask natural-language questions and obtain
validated SQL. It uses plain language and examples. The contracts listed at the end contain the
technical details.
> **Che cos'è ThothII.** È un *datamart builder* con revisione umana: tu scrivi una domanda in
> linguaggio naturale, un modello propone via via i passaggi (chiarimenti, schema, CTE, SQL) e un
> **revisore umano decide** a ogni passaggio chiave. Il risultato finale è SQL validato pronto da
> eseguire sul data warehouse.
> **What is ThothII?** It is a *datamart builder* with human review. You write a natural-language
> question, the model proposes each step in turn (clarifications, schema, CTEs, and SQL), and a
> **human reviewer decides** at every important step. The final result is validated SQL ready to
> run on the data warehouse.
---
## Parte 1 — Preparare il repository dei workspace su Git
## Part 1: prepare the workspace repository on Git
### 1.1 La struttura
### 1.1 Structure
Il repository dei workspace è un **repository Git** che descrive *quali dati* sono disponibili e
*come raggiungerli*. Non contiene i dati e **non contiene segreti** (password, token, certificati).
The workspace repository is a **Git repository** that describes *which data* is available and
*how to reach it*. It contains neither the data nor **secrets** such as passwords, tokens, or certificates.
Un repository valido contiene:
A valid repository contains:
```text
thoth-workspaces.yaml ← catalogo: elenco dei workspace
<id-workspace>/workspace.yaml ← descrittore del workspace (schema v3)
<id-workspace>/evidence/ ← (facoltativo) documenti di contesto, es. *.md
<id-workspace>/schema/annotations.yaml ← (facoltativo) join logici curati a mano (P5)
thoth-workspaces.yaml # catalog: list of workspaces
<workspace-id>/workspace.yaml # workspace descriptor (schema v3)
<workspace-id>/evidence/ # optional context documents, such as *.md
<workspace-id>/schema/annotations.yaml # optional manually curated logical joins (P5)
```
- Il **catalogo** `thoth-workspaces.yaml` è un semplice elenco:
- The **catalog** `thoth-workspaces.yaml` is a simple list:
```yaml
schema_version: 1
workspaces:
- id: psd-clinical
name: Policlinico San Donato
description: DWH clinico del Policlinico San Donato
- id: acme-ebikes
name: ACME Limited
description: DWH for electric bicycle production
```
- L'**id** deve essere minuscolo, senza spazi, es. `psd-clinical` (`[a-z][a-z0-9-]{2,62}`).
- Il **descrittore** `<id>/workspace.yaml` è lo schema v3. È l'unica descrizione valida.
- The **ID** must be lowercase, contain no spaces, and follow `acme-ebikes` (`[a-z][a-z0-9-]{2,62}`).
- The **descriptor** `<id>/workspace.yaml` uses schema v3. It is the only valid description.
### 1.2 Esempio di descrittore (Policlinico San Donato)
### 1.2 Descriptor example (ACME Limited)
```yaml
workspace:
schema_version: 3
id: psd-clinical
name: Policlinico San Donato
description: DWH clinico — aritmologia
language: it # le descrizioni/evidence sono in italiano
id: acme-ebikes
name: ACME Limited
description: Industrial DWH for electric bicycle production
language: en # descriptions and Evidence are in English
dwh:
engine: postgres
database: postgres
schema: datawarehouse
supported_transports: [rest_api] # accesso tramite API REST (PostgREST)
supported_transports: [rest_api] # access through the REST API (PostgREST)
semantic_index:
vector_store:
engine: qdrant
collection: psd-clinical
collection: acme-ebikes
dimensions: 1024
distance: cosine
embedding:
@@ -84,204 +84,197 @@ diagnostics:
evidence:
source:
type: filesystem
uri: psd-clinical/evidence # percorso dentro il repository
uri: acme-ebikes/evidence # path inside the repository
policy:
max_chunk_chars: 4000
retain_published_generations: 3
```
Cosa cambia rispetto ai vecchi workspace (se ne avevi uno):
Changes from older workspaces:
- il database si raggiunge solo con **REST** o **Postgres diretto** (`rest_api` /
`postgres_direct`); il tunnel SSH resta disabilitato;
- l'indice semantico è **interno** (Qdrant + `qwen3-embedding:0.6b`, 1024 dimensioni, cosine);
- l'Evidence **filesystem** sta dentro il repository (`<id>/evidence`) e viene materializzata dal
commit Git fissato (P6); è supportata anche l'Evidence HTTP.
- the database is reached only through **REST** or **direct Postgres** (`rest_api` /
`postgres_direct`); the SSH tunnel remains disabled;
- the semantic index is **internal** (Qdrant plus `qwen3-embedding:0.6b`, 1024 dimensions, cosine);
- **filesystem** Evidence lives in the repository (`<id>/evidence`) and is materialized from the
pinned Git commit (P6). HTTP Evidence is also supported.
### 1.3 Regole da rispettare
### 1.3 Rules
1. **Git è la fonte di verità.** Descriptor, catalogo ed Evidence si modificano solo con un
*commit* + *push* e poi un *pull* dell'installazione.
2. **Niente segreti nel repository.** Password, token, chiavi private e URL firmati vengono inseriti
a runtime nella gestione Workspace e conservati cifrati dal backend.
3. **Solo schema v3.** I descrittori v1/v2 vengono rifiutati prima dell'attivazione.
4. **L'applicazione non fa push di contenuti curati.** L'operatore che cura il repository lavora in
un clone autore separato.
1. **Git is the source of truth.** Change the descriptor, catalog, and Evidence only through a
*commit* and *push*, followed by an installation *pull*.
2. **No secrets in the repository.** Add passwords, tokens, private keys, and signed URLs at
runtime through Workspace management; the backend stores them encrypted.
3. **Schema v3 only.** Reject v1 and v2 descriptors before activation.
4. **The application does not push curated content.** The repository curator works in a separate
authoring clone.
---
## Parte 2 — Usare gli strumenti ThothII per il repository
## Part 2: use ThothII's repository tools
Ci sono **due** strumenti: l'**applicazione web** (gestione workspace) e la **CLI `tht`**
(preprocessing/operator). L'installazione completa è descritta nei manuali
There are **two** tools: the **web application** (workspace management) and the **`tht` CLI**
(preprocessing and operations). The complete installation is described in
`docs/install/local-workspace-registry.md` (macOS/Windows/Linux) e
`docs/install/server-workspace-registry.md`.
### 2.1 `tht` — comandi principali
### 2.1 `tht`: main commands
`tht` si invoca sempre con `--installation <percorso>/thothii-installation.yaml`. I comandi
utili, nell'ordine tipico:
Always invoke `tht` with `--installation <path>/thothii-installation.yaml`. The usual commands are:
```bash
# 1) vedere lo stato di un workspace (revisione e identità)
# 1) inspect workspace state (revision and identity)
tht --installation <install> workspace inspect --workspace <id> --json
# 2) introspezione del DWH (genera physical.yaml + LSH)
# 2) inspect the DWH (generates physical.yaml and LSH)
tht --installation <install> workspace preprocess dwh --workspace <id> --json
# 3) suggerire le join (FK) da SQL già approvato
# 3) suggest joins (FKs) from approved SQL
tht --installation <install> workspace schema suggest-fks --workspace <id> --from-sql <query>.sql --output <candidati>.yaml --json
# 4) dopo la revisione: pubblicare gli FK curati in Git e accettarli
# 4) after review, publish curated FKs in Git and accept them
tht --installation <install> workspace schema accept --workspace <id> --run <run-id> --yes --json
# 5) indicizzare lo schema (Qdrant)
# 5) index the schema (Qdrant)
tht --installation <install> workspace index-schema --workspace <id> --json
# 6) preprocessing dell'Evidence
# 6) preprocess Evidence
tht --installation <install> workspace preprocess evidence --workspace <id> --json
# 7) catena completa (DWH → FK → schema → Evidence)
# 7) complete chain (DWH → FK → schema → Evidence)
tht --installation <install> workspace preprocess run --workspace <id> --json
# 8) ispezione/ricostruzione della collection Qdrant (solo manutenzione)
# 8) inspect or rebuild the Qdrant collection (maintenance only)
tht --installation <install> workspace vector inspect --workspace <id> --json
tht --installation <install> workspace vector rebuild --workspace <id> --collection <nome> --confirm <nome> --destroy
```
Note importanti:
Important notes:
- **`--json` produce solo JSON su stdout** (contratto macchina): usalo negli script.
- **`preprocess run` si ferma per la revisione umana** quando ci sono nuove join proposte: esce con
`manual_review_required`. Dopo la revisione si riparte con `schema accept ... --yes` e
- **`--json` writes JSON only to stdout** (machine contract); use it in scripts.
- **`preprocess run` stops for human review** when it finds new proposed joins: it exits with
`manual_review_required`. After review, continue with `schema accept ... --yes` and
`preprocess run --resume <run-id>`.
- **Un file Evidence filesystem viene materializzato dal commit Git fissato** (niente checkout
mobile); symlink, percorsi pericolosi e alberi troppo grandi vengono rifiutati.
- **Il CLI non scrive mai nel repository** (nessun push di contenuti curati).
- **A filesystem Evidence file is materialized from the pinned Git commit** (there is no moving
checkout); symlinks, unsafe paths, and trees that are too large are rejected.
- **The CLI never writes to the repository** (it never pushes curated content).
### 2.2 Applicazione web — gestione workspace
### 2.2 Web application: workspace management
La gestione Workspace ha due livelli distinti.
Workspace management has two distinct levels.
**Livello 1 — repository.** La parte iniziale spiega che il sorgente del workspace vive in una
directory separata, viene pubblicato dal curatore su un repository ospitato da un server Git come
GitHub, GitLab o Gitea, e viene letto da ThothII in sola lettura. Mostra host, repository, branch,
revisione attiva e stato dell'ultimo aggiornamento.
**Level 1: repository.** The first section shows that the workspace source lives in a separate
directory, is published by the curator to a repository hosted on a Git server such as GitHub,
GitLab, or Gitea, and is read by ThothII in read-only mode. It shows the host, repository, branch,
active revision, and status of the last update.
- **Update workspace repository** non richiede la selezione di un workspace. Il backend esegue il
fetch/pull del branch configurato direttamente nel checkout gestito da ThothII, valida l'intera
revisione candidata e la attiva in modo atomico. Se la validazione fallisce, conserva la
revisione precedente. Non modifica il sorgente remoto e non salva contenuti nella GUI.
- Per creare un workspace locale, prepara una directory sorgente con catalogo, `workspace.yaml` e
le sottodirectory previste; quindi validala, esegui commit e push dal clone autore. ThothII non
offre comandi di creazione, modifica o pubblicazione del sorgente.
* **Update workspace repository** does not require a workspace to be selected. The backend fetches
or pulls the configured branch into ThothII's managed checkout, validates the entire candidate
revision, and activates it atomically. If validation fails, it keeps the previous revision. It
does not modify the remote source or save content from the GUI.
* To create a local workspace, prepare a source directory with the catalog, `workspace.yaml`, and
the expected subdirectories. Validate it, then commit and push from the authoring clone.
ThothII provides no commands to create, edit, or publish the source.
**Livello 2 — workspace selezionato.** Questi comandi sono isolati perché richiedono prima la
selezione del workspace.
**Level 2: selected workspace.** These commands are separate because they require a workspace to be selected first.
These commands are separate because they require a workspace to be selected first.
- **Validate workspace source** verifica nuovamente catalogo, descrittore, Evidence e invarianti della
revisione attiva selezionata. Non contatta il DWH e non modifica file.
- **Save entered secrets** sostituisce alla cieca i valori compilati. I campi dipendono dal
trasporto DWH e dall'autenticazione Evidence dichiarati; il backend restituisce solo lo stato
configurato/mancante.
- **Forget stored value** elimina dal vault cifrato il singolo secret indicato. Le sessioni o operazioni future
che lo richiedono restano bloccate finché non viene inserito di nuovo.
- **Test workspace connections** materializza temporaneamente i secret necessari, contatta i servizi dati
configurati per quel workspace e rimuove i file temporanei alla fine. Non esporta né pubblica
nulla.
Il repository Git remoto e le relative credenziali sono impostazioni di installazione. I secret
runtime DWH/Evidence sono invece persistenti nel vault cifrato del backend e non nel local storage
della GUI. La GUI è soltanto l'interfaccia: dopo l'invio cancella i valori dai campi e non può
rileggerli.
- **Validate workspace source** checks the catalog, descriptor, Evidence, and invariants of the
selected active revision again. It does not contact the DWH or modify files.
- **Save entered secrets** replaces the entered values without displaying them. The fields depend
on the declared DWH transport and Evidence authentication. The backend returns only configured
or missing status.
* **Forget stored value** removes the selected secret from the encrypted vault. Future sessions or
operations that need it remain blocked until it is entered again.
The remote Git repository and its credentials are installation settings. Runtime DWH and Evidence
secrets persist in the backend's encrypted vault, not in the GUI's local storage. The GUI is only
the interface: after submission it clears the field values and cannot read them back.
---
## Parte 3 — Usare l'applicazione ThothII di base
## Part 3: use the ThothII application
### 3.1 Nuova sessione
### 3.1 New session
Apri l'applicazione e usa il modulo **New session**: inserisci solo la **domanda** in linguaggio
naturale (workspace, modello e provider sono impostazioni globali già configurate).
Open the application and use **New session**. Enter only the **natural-language question**;
workspace, model, and provider are already configured as global settings.
Esempio di domanda:
Example question:
> «Estrai i pazienti che hanno eseguito un'ablazione nell'ultimo anno, con nome, cognome e data
> dell'intervento.»
> "List the electric bicycles completed in the last year, with model, frame number, and completion
> date."
### 3.2 Il workflow a 8 fasi e i gate
### 3.2 The eight-phase workflow and gates
La domanda attraversa **8 fasi**. Tu vedi i documenti intermedi e decidi nei punti chiave:
The question passes through **eight phases**. You see the intermediate documents and decide at the important points:
1. **F1 chiarimento** — se serve, il modello chiede di togliere ambiguità;
2. **F2 memoria** — recupera le memory riutilizzabili;
3. **F3 riscrittura** — riscrive e approva la domanda;
4. **F4 schema-linking** — propone tabelle e colonne collegate;
5. **F5 sintesi** — riassume lo schema scelto;
6. **F6 CTE** — costruisce i CTE;
7. **F7 SQL finale** — produce `sql_final.sql`;
8. **F8 datamart** — esecuzione/export (dbt, CSV, Excel).
1. **F1 clarification**: the model removes ambiguity when needed;
2. **F2 Memory**: retrieves reusable Memory;
3. **F3 rewriting**: rewrites and approves the question;
4. **F4 schema linking**: proposes related tables and columns;
5. **F5 summary**: summarizes the selected schema;
6. **F6 CTE**: builds the CTEs;
7. **F7 final SQL**: produces `sql_final.sql`;
8. **F8 datamart**: execution or export (dbt, CSV, Excel).
I **gate di revisione** appaiono come widget: scegli un'opzione singola, seleziona più voci, o
conferma un artefatto/fase. Il modello *propone*, il revisore *decide*. Il lato destro mostra gli
artefatti (schema-linking, CTE, SQL); il pannello Model activity mostra domanda/ragionamento.
**Review gates** appear as widgets: choose one option, select several items, or confirm an
artifact or phase. The model *proposes* and the reviewer *decides*. The right side shows artifacts
(schema linking, CTEs, and SQL); the Model activity panel shows the question and reasoning.
### 3.3 Sessioni
### 3.3 Sessions
Le sessioni sono elencate nella barra laterale con id, domanda, data e autore. Una sessione
**riprende** dall'ultima fase incompleta ricostruendo lo stato dai documenti salvati su disco
(`session_manifest.yaml` + artefatti di fase + `review_decisions.jsonl`). Lo stato salvato **è** la
verità: ciò che non è registrato non è avvenuto.
Sessions appear in the sidebar with their ID, question, date, and author. A session
**resumes** from its last incomplete phase by rebuilding state from documents saved on disk
(`session_manifest.yaml`, phase artifacts, and `review_decisions.jsonl`). Saved state **is** the
truth: what is not recorded did not happen.
---
## Esempio pratico completo — Policlinico San Donato
## Complete example: ACME Limited
### Passo 0 — repository
### Step 0: repository
Crea il repository Git del workspace (es. `tht-workspace-psd`):
Create the workspace Git repository, for example `tht-workspace-acme`:
```text
thoth-workspaces.yaml # catalogo con psd-clinical
psd-clinical/workspace.yaml # descrittore v3 (vedi §1.2)
psd-clinical/evidence/ # i documenti .md di contesto curati
psd-clinical/schema/annotations.yaml # (quando ci sono join curate)
thoth-workspaces.yaml # catalog containing acme-ebikes
acme-ebikes/workspace.yaml # v3 descriptor (see §1.2)
acme-ebikes/evidence/ # curated context .md documents
acme-ebikes/schema/annotations.yaml # when curated joins exist
```
Fai `commit` e `push`. Nell'installazione, l'applicazione fa `Pull` e **attiva** il workspace:
valida lo schema v3, materializza l'Evidence dal commit fissato e prepara la collection Qdrant
(1024/cosine + indici).
Publish a new Git revision. In the installation, the application fetches and **activates** the
workspace, validates schema v3, materializes Evidence from the pinned revision, and prepares the
Qdrant collection (1024/cosine plus indexes).
### Passo 1 — preprocessing
### Step 1: preprocessing
```bash
tht --installation ~/thothii-installation.yaml workspace preprocess dwh --workspace psd-clinical --json
tht --installation ~/thothii-installation.yaml workspace preprocess run --workspace psd-clinical --json
tht --installation ~/thothii-installation.yaml workspace preprocess dwh --workspace acme-ebikes --json
tht --installation ~/thothii-installation.yaml workspace preprocess run --workspace acme-ebikes --json
```
Se il run si ferma per le join (`manual_review_required`):
If the run stops for joins (`manual_review_required`):
```bash
# il curatore rivede i candidati e pubblica psd-clinical/schema/annotations.yaml, poi:
tht --installation ~/thothii-installation.yaml workspace schema accept --workspace psd-clinical --run <run-id> --yes --json
tht --installation ~/thothii-installation.yaml workspace preprocess run --workspace psd-clinical --resume <run-id> --json
# the curator reviews the candidates and publishes acme-ebikes/schema/annotations.yaml, then:
tht --installation ~/thothii-installation.yaml workspace schema accept --workspace acme-ebikes --run RUN_ID --yes --json
tht --installation ~/thothii-installation.yaml workspace preprocess run --workspace acme-ebikes --resume RUN_ID --json
```
### Passo 2 — la domanda
### Step 2: the question
Nell'applicazione seleziona il workspace `psd-clinical` e crea una sessione con la domanda. Segui
le fasi e conferma ai gate: il modello proporrà lo schema-linking (tabelle/colonne del DWH
`datawarehouse`), i CTE e infine l'SQL finale, che potrai copiare/visualizzare ed eseguire.
In the application, select the `acme-ebikes` workspace and create a session with the question.
Follow the phases and confirm the gates. The model will propose schema linking (tables and columns
from the `datawarehouse` DWH), CTEs, and finally the SQL, which you can view, copy, and run.
---
## Dove trovare i dettagli tecnici
## Where to find technical details
Per l'accesso DWH REST, la chiave è per installazione e vale solo per `rest_api`: il server PSD rimane `postgres_direct` e `ssh_tunnel` non usa questa chiave. Vedere [guida server DWH](install/dwh-auth-server.md), [enrollment client](install/dwh-auth-client-enrollment.md), [TLS](install/dwh-auth-tls.md) e [runbook PSD](operations/psd-dwh-auth-rollout.md).
For DWH REST access, the key is installation-specific and applies only to `rest_api`; `postgres_direct`
and `ssh_tunnel` do not use it. See the [DWH server guide](install/dwh-auth-server.md), [client
enrollment guide](install/dwh-auth-client-enrollment.md), and [TLS guide](install/dwh-auth-tls.md).
- Contratto CLI: `docs/contracts/workspace-preprocessing-cli.md`
- Contratto `.tht-dwh`: `docs/contracts/tht-dwh.md`
- CLI contract: `docs/contracts/workspace-preprocessing-cli.md`
- `.tht-dwh` contract: `docs/contracts/tht-dwh.md`
- Evidence v3: `docs/contracts/workspace-evidence-v3.md`
- Installazione locale: `docs/install/local-workspace-registry.md`
- Installazione server: `docs/install/server-workspace-registry.md`
- Verifica manuale P2–P6: `docs/testing/p2-p6-manual-verification.md`
+11 -12
View File
@@ -1,21 +1,20 @@
# ThothII — Documentazione
# ThothII documentation
Benvenuto nella documentazione di ThothII, il datamart builder human-in-the-loop che trasforma domande in linguaggio naturale in SQL validato attraverso un workflow a 8 fasi orchestrato dal coding agent Pi.
ThothII is a human-in-the-loop datamart builder. It turns natural-language questions into validated SQL through an eight-phase workflow orchestrated by Pi.
La documentazione è divisa in due aree:
The documentation is divided into two areas:
## ThothII (Documentazione Tecnica)
## ThothII technical documentation
Come funziona il sistema: architettura, specifiche di design delle singole funzionalità, piani di implementazione, report di test. Parte da qui: [Panoramica dell'architettura](architecture/overview.md).
This section explains the system architecture, workflow, operating contracts, Evidence, and Memory. Start with the [architecture overview](architecture/overview.md).
Per autenticazione locale, OIDC generico, Authentik e accettazione PSD: [documentazione autenticazione](architecture/authentication.md).
For local authentication, generic OIDC, and Authentik, see the [authentication documentation](architecture/authentication.md).
Per installare l'applicazione in Docker nei quattro contesti operativi, usando il file env,
`compose.yaml`, l'overlay locale/server e il bundle di secret montato:
[Installazione Docker nei quattro contesti](installazione-docker-4-contesti.md).
To install the application in Docker across the four operating contexts, using the env file,
`compose.yaml`, the local/server overlay, and the mounted secret bundle, see [Docker installation in the four operating contexts](installazione-docker-4-contesti.md).
Per il DWH REST con una chiave revocabile per installazione: [guida server](install/dwh-auth-server.md), [enrollment client](install/dwh-auth-client-enrollment.md), [TLS](install/dwh-auth-tls.md) e [runbook PSD](operations/psd-dwh-auth-rollout.md). Il componente resta separato dallo stack Compose ThothII.
For DWH REST with an installation-specific revocable key, see the [server guide](install/dwh-auth-server.md), [client enrollment guide](install/dwh-auth-client-enrollment.md), and [TLS guide](install/dwh-auth-tls.md). This component remains separate from the ThothII Compose stack.
## Considerazioni Generali
## General topics
Note operative e di configurazione che non sono specifiche del dominio ThothII ma riguardano l'ambiente di sviluppo condiviso con altri progetti — ad esempio come Pi (il coding agent) risolve i modelli a livello built-in, utente e progetto. Parte da qui: [Configurazione dei modelli in Pi](general/pi-configuration.md).
Operating and configuration notes that are not specific to the ThothII domain, such as how Pi resolves built-in, user-level, and project-level models. Start with [Pi model configuration](general/pi-configuration.md).
+1 -2
View File
@@ -75,7 +75,6 @@ This section applies only when a Linux `profile: server` descriptor declares a r
The canonical authentication root stays root-owned and is the only authority. The container reads
only the separate read-only runtime projection selected by `CURRENT`; it never falls back to the
canonical files or to a previous generation. Run projected mutations and repairs through the
root-operated `tht` commands documented in the [server guide](server.md), and never edit runtime
files directly.
root-operated `tht` commands, and never edit runtime files directly.
Mac, Windows, and local direct-file authentication remain unchanged when the projection is absent.
+3 -3
View File
@@ -71,7 +71,7 @@ The surfaces have distinct semantics and this order is recommended:
issuer/JWKS, catalog credentials, and all configured mapped groups.
3. `tht auth check --interactive` repeats live diagnosis and additionally validates a device-flow
identity and its direct `groups` claim when Device Authorization is available.
4. Workspace Test performs aggregate live workspace and authentication validation.
4. Installation diagnostics perform aggregate live workspace and authentication validation.
The live CLI forms are:
@@ -87,8 +87,8 @@ real ID token including `groups`. It is an operator check, not a replacement for
`tht doctor` emits this exact ordered report: `descriptor`, `files`, `docker`, `compose`,
`configuration`, `authentication`, `services`, `core-http`, `frontend-http`,
`workspace-registry`, `workflow`, `pi`. Its authentication entry is live and non-interactive.
Any authentication failure makes Workspace Validate or Workspace Test non-activatable according
to that surface's static or live scope.
Any authentication failure prevents activation according to the static or live scope of the
relevant diagnostic surface.
The complete closed diagnostic-code union and exact role-to-permission expansion are in the
[authentication architecture](../architecture/authentication.md).
+35 -31
View File
@@ -1,37 +1,41 @@
# Authentik provider setup
# Authentik provider configuration
Authentik is the first certified provider for PSD acceptance. The ThothII browser protocol remains
generic OIDC; these steps configure the provider-specific group catalog only.
ThothII uses generic OIDC in the browser. Authentik provides the identity provider and group
catalog without adding a proprietary login flow.
1. Create an OAuth2/OIDC application and provider in Authentik. Register exactly
`<publicUrl>/api/auth/oidc/callback` as the callback and enable `openid`, `profile`, and `email`.
2. Configure the provider so the ID token contains a direct `groups` array of strings. Verify the
claim with a disposable test identity before running acceptance.
3. Create a dedicated API service account for the group catalog. Grant group-view-only privilege;
do not grant write, user-management, or directory-administration privilege. Put its bearer value
in the protected bundle under `THT_AUTHENTIK_API_TOKEN`.
4. Create or confirm the exact groups `TOT Users` and `TOT Admin`. Map them explicitly in
`auth.yaml` to `user` and `admin`, respectively. Keep other upstream groups out of the mapping.
5. Run Workspace Validate for static authentication validation. Then run live non-interactive
diagnosis, followed by the optional device-flow identity check:
```mermaid
sequenceDiagram
participant Browser
participant ThothII
participant Authentik
Browser->>ThothII: Sign in
ThothII->>Authentik: Authorization Code with PKCE
Authentik-->>Browser: Login and consent
Browser->>ThothII: Callback with code
ThothII->>Authentik: Token exchange
Authentik-->>ThothII: Identity and groups
ThothII-->>Browser: Opaque session
```
```sh
tht auth check
tht auth check --interactive
tht doctor --json
```
## OIDC provider
6. Run Workspace Test for aggregate live workspace and authentication validation. It must prove
discovery/JWKS, catalog access, and every configured group. The diagnostic result must contain
no secret values. `tht doctor --json` reports `authentication` after `configuration` and before
`services` in its exact ordered checklist.
1. Create an OAuth2/OIDC application and provider.
2. Register exactly `PUBLIC_URL/api/auth/oidc/callback`.
3. Enable the `openid`, `profile`, and `email` scopes.
4. Configure a direct `groups` claim as an array of strings.
Only configured exact group names are queried. Additional Authentik or directory groups are ignored
silently, without a warning. A mapped group absent from Authentik fails closed with
`oidc_mapped_group_missing`; an ambiguous exact-name result uses
`oidc_mapped_group_ambiguous`. A group visible only in an upstream directory but not represented
in Authentik is missing from ThothII’s catalog and must not be treated as present.
## Group catalog
Rotate the two credentials independently through the protected secret-file procedure, then repeat
`tht auth check` and workspace Test. Never put either value in this guide, YAML, shell history,
diagnostic output, or acceptance evidence.
Create a dedicated service account with read-only access to groups. Store its token in the
protected bundle as `THT_AUTHENTIK_API_TOKEN`.
Map the exact enterprise group names to the ThothII `user` and `admin` roles in `auth.yaml`.
Unmapped groups are ignored. A configured group that does not exist produces a closed error.
## Diagnostics
`tht auth check` checks discovery, the issuer, JWKS, catalog access, and the configured groups.
The `--interactive` option also verifies identity through device flow when the provider supports it.
Rotate the OIDC secret and group-catalog token separately. Neither may appear in YAML, shell
history, logs, or diagnostic output.
+26 -57
View File
@@ -1,70 +1,39 @@
# Enrollment client per DWH REST
# DWH REST client enrollment
La credenziale `dwh-auth` appartiene a una installazione ThothII, non a una persona. Serve solo se
il trasporto è `rest_api`; `postgres_direct` e `ssh_tunnel` non la usano.
The `dwh-auth` credential belongs to one ThothII installation and is needed only when the
workspace uses the `rest_api` transport.
| Trasporto | Chiave `dwh-auth` | Materiale locale |
| --- | --- | --- |
| `rest_api` | Sì, una per installazione. | URL HTTPS, `API_KEY_FILE`, eventuale `TLS_CA_FILE`. |
| `postgres_direct` | No. | Credenziali PostgreSQL e TLS PostgreSQL. |
| `ssh_tunnel` | No. | Credenziali PostgreSQL e materiali SSH; è diagnostico-only nel runtime corrente. |
| Trasporto | Materiale richiesto |
| --- | --- |
| `rest_api` | URL HTTPS, `API_KEY_FILE`, eventuale `TLS_CA_FILE` |
| `postgres_direct` | Credenziali PostgreSQL e configurazione TLS PostgreSQL |
| `ssh_tunnel` | Credenziali PostgreSQL e materiale SSH |
Il ThothII server PSD resta `postgres_direct` read-only. Il Mac PSD e le installazioni remote
usano `rest_api`; non introdurre un tunnel SSH per aggirare REST.
## Delivery and storage
## Prerequisiti
Receive the key and CA through separate protected channels. Store the key in the installation
vault or in a regular file accessible only to the authorized account. Do not put it in Git, YAML
files, arguments, logs, or shared screens.
Ricevere chiave e CA, se necessaria, attraverso canali protetti separati. Confermare fuori banda il
fingerprint TLS prima dell'uso: [guida TLS](dwh-auth-tls.md). Conservare la chiave nel vault o in
un file protetto, mai Git, `.env` con il valore, argv, ambiente, log o evidenze. Annotare solo ID
pubblico.
## ACME Limited configuration
## Percorso GUI: vault dell'installazione
1. In **Workspace management**, eseguire **Update workspace repository** se necessario e
selezionare il workspace.
2. Il trasporto `rest_api` è una precondizione amministrativa del binding locale, non una scelta della GUI. Controllare URL/trust locali e usare **Validate workspace source**.
3. Inserire la chiave nel campo write-only **Data warehouse API key**, poi **Save entered secrets**.
La GUI la conserva nel vault cifrato `workspace-secrets`, non la rileggere né la restituisce.
4. Eseguire **Test workspace connections**. Il controllo innocuo è `/rpc/ping`: atteso 2xx e
database/schema dichiarati.
5. Comunicare al server solo ID pubblico, timestamp e risultato. **Forget stored value** rimuove il valore e va
usato soltanto dopo conferma di sostituzione o revoca.
## Percorso headless: binding reale
`API_KEY_FILE` significa che il valore è nel file, non nella variabile. Questo è l'esempio Mac/local/remoto nel file PSD non tracciato `workspace-bindings.env`; non è il binding del server PSD Project A, che resta `postgres_direct`. I binding REST sono:
Esempio di binding headless per il workspace `acme-ebikes`:
```dotenv
THT_WS_PSD_CLINICAL_DWH_TRANSPORT=rest_api
THT_WS_PSD_CLINICAL_DWH_BASE_URL=https://supabase-aritmolab.policlinicosandonato.it/dwh/
THT_WS_PSD_CLINICAL_DWH_API_KEY_FILE=/run/secrets/psd-clinical-dwh-api-key
THT_WS_PSD_CLINICAL_DWH_TLS_CA_FILE=/run/secrets/psd-clinical-dwh-ca.pem
THT_WS_ACME_EBIKES_DWH_TRANSPORT=rest_api
THT_WS_ACME_EBIKES_DWH_BASE_URL=https://dwh.acme.example/dwh/
THT_WS_ACME_EBIKES_DWH_API_KEY_FILE=/run/secrets/acme-ebikes-dwh-api-key
THT_WS_ACME_EBIKES_DWH_TLS_CA_FILE=/run/secrets/acme-ebikes-dwh-ca.pem
```
Nel file `operator.env` non tracciato, ogni suffisso `_SOURCE` indica solo il percorso assoluto del
file protetto di origine. Il comando genera un override non tracciato che monta quei file nel
`core`; non installa né avvia `dwh-auth` con Compose:
The workspace suffix comes from the immutable ID, with hyphens changed to underscores and letters
converted to uppercase. `API_KEY_FILE` contains the mounted file path, not the key value.
```bash
bash scripts/generate-connector-secrets-override.sh \
--bindings-env /absolute/protected/workspace-bindings.env \
--operator-env /absolute/protected/operator.env \
--output /absolute/protected/connector-secrets.override.yaml \
--service core --role dwh
```
## Rotation and revocation
La chiave sorgente è un file regolare `0600` per il solo account autorizzato. Per altri workspace,
sostituire `PSD_CLINICAL` con ID immutabile maiuscolo (trattini in underscore). Vedere anche il
[protocollo diagnostico](../workspace-diagnostic-protocol.md).
During rotation, receive the new generation, update the vault or mounted file, and confirm
connectivity through the harmless `/rpc/ping` route. The server owner revokes the previous
generation only after this confirmation.
## Ping, rotazione e revoca
Usare solo **Test workspace connections** su `/rpc/ping`: successo è 2xx con TLS verificato; il server
conferma l'ID con `key status`. Durante rotazione, ricevere nuova generazione, aggiornare vault o
file `API_KEY_FILE`, ripetere ping, attendere osservazione e far revocare la precedente. Dopo la
revoca: nuova positiva, precedente `401`.
`401` non distingue chiave assente, scaduta o revocata. `503` è un guasto fail-closed di servizio,
socket o registro: non usare connessione diretta e non ridurre TLS. Non riattivare una chiave
revocata. Il percorso PSD è nel [runbook](../operations/psd-dwh-auth-rollout.md).
A `401` means the key is missing, unknown, expired, or revoked. A `503` means the authorization
service or registry is unavailable. In either case, do not bypass REST or weaken TLS verification.
+53 -346
View File
@@ -1,356 +1,63 @@
# `dwh-auth`: guida server
# `dwh-auth`: server guide
`dwh-auth` autentica la route REST `/dwh/` con una chiave per installazione. È un componente Linux
opzionale e server-side: usa `systemd`, non `tht` né Docker Compose, non legge risultati clinici e
non si collega a PostgreSQL. La chiave serve solo a `rest_api`; `postgres_direct` e `ssh_tunnel`
non la usano.
`dwh-auth` protects the REST `/dwh/` route with a separate key for each ThothII installation.
It runs as a separate Linux service, does not read DWH data, and does not connect directly to
PostgreSQL.
## Prerequisiti e confini
- Usare un checkout revisionato, Docker per la build e un operatore autorizzato sul server DWH.
- Una chiave identifica un'installazione, non una persona. L'`installation-id` è unico, non
personale e senza dati clinici.
- Chiavi, digest, file di consegna e backup restano in file protetti: mai Git, argv, variabili
d'ambiente, log, JSON pubblico o evidenze.
- Preparare backup e rollback prima di Nginx. Installare il servizio non autorizza una modifica
della route pubblica.
## Percorsi, owner e mode
| Oggetto | Percorso | Owner e mode |
| --- | --- | --- |
| Binario | `/usr/local/sbin/dwh-auth` | `root:root`, `0755` |
| Unit | `/etc/systemd/system/dwh-auth.service` | `root:root`, `0644` |
| Tmpfiles | `/usr/lib/tmpfiles.d/dwh-auth.conf` | `root:root`, `0644` |
| Registro, `active`, `revoked` | `/var/lib/dwh-auth/` | `root:dwh-auth`, `2750` |
| Lock | `/var/lib/dwh-auth/.writer.lock` | `root:dwh-auth`, `0640` |
| Record | `/var/lib/dwh-auth/{active,revoked}/<public-key-id>.json` | `root:dwh-auth`, `0640` |
| Socket runtime | `/run/dwh-auth/verify.sock` | `dwh-auth:www-data`, `0660` |
| Consegne e backup | `/root/dwh-auth-provision/` | directory `root:root` `0700`, file `0600` |
Il record conserva un digest interno (`secret_sha256`) e metadati, mai la chiave in chiaro. Non
leggere, stampare, calcolare o mettere quel digest in una prova operativa.
## Build, installazione e avvio
Costruire dal commit congelato e registrare solo checksum del binario e SHA sorgente:
```bash
cd /srv/thothii/app
bash scripts/build-dwh-auth.sh --output /tmp/dwh-auth-release
sha256sum /tmp/dwh-auth-release/dwh-auth-linux-amd64
```mermaid
flowchart LR
CLIENT["Installazione ThothII"] -->|"X-API-Key"| NGINX["Nginx"]
NGINX --> AUTH["dwh-auth\nUnix socket"]
AUTH --> REGISTRY["Registro chiavi\nactive e revoked"]
AUTH -->|"authorized"| REST["DWH REST"]
```
Scegliere l'architettura corretta. Il template PSD usa il gruppo Nginx `www-data`; confermarlo
prima dell'installazione su un host diverso.
## Security boundaries
```bash
sudo groupadd --system dwh-auth
sudo useradd --system --no-create-home --shell /usr/sbin/nologin --gid dwh-auth dwh-auth
sudo install -o root -g root -m 0755 /tmp/dwh-auth-release/dwh-auth-linux-amd64 /usr/local/sbin/dwh-auth
sudo install -o root -g root -m 0644 deploy/dwh-auth/dwh-auth.service /etc/systemd/system/dwh-auth.service
sudo install -o root -g root -m 0644 deploy/dwh-auth/dwh-auth.tmpfiles.conf /usr/lib/tmpfiles.d/dwh-auth.conf
sudo install -d -o root -g root -m 0700 /root/dwh-auth-provision
sudo systemd-tmpfiles --create /usr/lib/tmpfiles.d/dwh-auth.conf
sudo /usr/local/sbin/dwh-auth --registry-root /var/lib/dwh-auth check
sudo systemd-analyze verify /etc/systemd/system/dwh-auth.service
sudo systemctl daemon-reload
sudo systemctl enable --now dwh-auth
sudo systemctl status dwh-auth --no-pager
```
- A key identifies an installation, not a person.
- Keys and backups stay in protected files and never enter Git, logs, arguments, or public JSON.
- The registry stores digests and metadata, never the key in plaintext.
- The REST route must be exposed only through verified TLS.
Controllare i mode con `stat`. Il servizio apre il registro in sola lettura e crea solo il socket.
Non creare JSON, lock o socket a mano: oggetti insicuri devono fallire chiusi.
## Installation
## Check, elenco e stato
Il servizio usa questi percorsi:
Usare sempre un root assoluto. Questi comandi espongono solo ID pubblici, stato, date e scadenza:
```bash
sudo /usr/local/sbin/dwh-auth --registry-root /var/lib/dwh-auth check
sudo /usr/local/sbin/dwh-auth --registry-root /var/lib/dwh-auth key list --json
key_id=public-key-id
sudo /usr/local/sbin/dwh-auth --registry-root /var/lib/dwh-auth key status --key-id "$key_id" --json
```
Un errore di integrità, permessi, symlink o JSON malformato richiede ripristino da backup protetto,
non una correzione manuale del record.
## Creazione, consegna, scadenza e revoca
Il comando crea la chiave una volta in un nuovo file assoluto `0600`; stdout contiene solo ID
pubblico, installazione e percorso. Il file di output non deve esistere.
```bash
installation_id=psd-mac-primary
description=operatore-mac-primario
key_output=/root/dwh-auth-provision/psd-mac-primary.key
sudo /usr/local/sbin/dwh-auth --registry-root /var/lib/dwh-auth key create \
--installation-id "$installation_id" \
--description "$description" \
--output "$key_output"
```
Aggiungere `--expires-at "YYYY-MM-DDTHH:MM:SSZ"` solo se la policy impone una scadenza; il default è nessuna
scadenza. Consegnare il file solo con vault aziendale, secret manager, MDM o trasferimento
autenticato ristretto. Mai email, chat, ticket, `cat` o copia-incolla. Il client conferma ID
pubblico e ping, poi il materiale temporaneo viene rimosso secondo policy.
L'import legacy è temporaneo PSD: il file sorgente è già `root:root` `0600` e non viene mai letto o
stampato dall'operatore.
```bash
sudo /usr/local/sbin/dwh-auth --registry-root /var/lib/dwh-auth key import \
--legacy-raw --installation-id legacy-shared \
--from-file /root/dwh-auth-provision/legacy-shared.key
```
Per rotare: creare seconda generazione, consegnarla, configurarla e provare `/rpc/ping`; confermare
l'ID pubblico; attendere l'osservazione; poi revocare la precedente e provare nuova=successo,
precedente=401.
```bash
previous_key_id=public-key-id
revocation_reason=shared-credential-rotation
sudo /usr/local/sbin/dwh-auth --registry-root /var/lib/dwh-auth key revoke \
--key-id "$previous_key_id" --reason "$revocation_reason"
```
La revoca non è annullabile e un ID revocato non si ricrea.
## Backup, rollback e disinstallazione
Prima di mutare, creare un archivio root-only `0600` del registro e copie protette delle sole
configurazioni coinvolte. L'archivio contiene digest, quindi è materiale riservato: custodirlo su
storage cifrato approvato; l'evidenza ammessa riporta solo percorso, owner, mode, timestamp e
checksum dell'archivio. Il rollback dual-key ripristina la route e il servizio revisionati, esegue
`nginx -t` e fa reload solo autorizzato; non ripristina chiavi revocate, PostgreSQL, sessioni
legacy, indici Qdrant o cache Ollama.
La disinstallazione richiede autorizzazione esplicita, client REST migrati/revocati e rollback non
più necessario. Solo allora disabilitare l'unità; conservare registro e backup fino alla retention
approvata. Non inserire `dwh-auth` in Compose o in `tht start`/`tht stop`.
## Procedure riproducibili e secret-safe
Eseguire soltanto nel gate autorizzato. Le variabili seguenti contengono percorsi, timestamp e
codici, mai una chiave. Il manifest e l'archivio del registro sono `0600`; l'archivio resta
materiale riservato su storage cifrato approvato.
```bash
run_id=$(date -u +%Y%m%dT%H%M%SZ)
backup_root=/root/dwh-auth-provision
registry_root=/var/lib/dwh-auth
registry_backup="$backup_root/registry-$run_id.tar"
manifest="$backup_root/registry-$run_id.manifest"
sudo install -o root -g root -m 0600 /dev/null "$registry_backup"
sudo install -o root -g root -m 0600 /dev/null "$manifest"
sudo tar --acls --xattrs -C /var/lib -cf "$registry_backup" dwh-auth
sudo sh -c 'sha256sum "$1" > "$2"' sh "$registry_backup" "$manifest"
if sudo sha256sum -c "$manifest" >/dev/null; then printf 'registry_manifest=PASS\n'; else printf 'registry_manifest=FAIL\n' >&2; exit 1; fi
```
Il ripristino non sovrappone mai un tar al registro attivo. Estrarre prima in staging nello stesso
filesystem di `/var/lib`, verificare il candidato, rinominare il registro attuale in una copia
recuperabile e sostituirlo. Non cancellare il pre-ripristino: serve al rollback se `check` o
l'avvio falliscono.
```bash
registry_staging="/var/lib/.dwh-auth-restore-$run_id"
registry_candidate="$registry_staging/dwh-auth"
registry_previous="/var/lib/dwh-auth.pre-restore-$run_id"
if [ -e "$registry_staging" ] || [ -e "$registry_previous" ]; then printf 'registry_restore=FAIL\n' >&2; exit 1; fi
if ! sudo sha256sum -c "$manifest" >/dev/null; then printf 'registry_restore=FAIL\n' >&2; exit 1; fi
if ! sudo install -d -o root -g root -m 0700 "$registry_staging"; then printf 'registry_restore=FAIL\n' >&2; exit 1; fi
if ! sudo tar --acls --xattrs -C "$registry_staging" -xf "$registry_backup"; then printf 'registry_restore=FAIL\n' >&2; exit 1; fi
if ! sudo /usr/local/sbin/dwh-auth --registry-root "$registry_candidate" check; then printf 'registry_restore=FAIL\n' >&2; exit 1; fi
if ! sudo systemctl stop dwh-auth; then printf 'registry_restore=FAIL\n' >&2; exit 1; fi
if ! sudo mv -T -- "$registry_root" "$registry_previous"; then
if sudo systemctl start dwh-auth; then printf 'registry_restore_rollback=PASS\n' >&2; else printf 'registry_restore_rollback=FAIL\n' >&2; fi
exit 1
fi
if ! sudo mv -T -- "$registry_candidate" "$registry_root"; then
if ! sudo mv -T -- "$registry_previous" "$registry_root"; then printf 'registry_restore_rollback=FAIL\n' >&2; exit 1; fi
if sudo /usr/local/sbin/dwh-auth --registry-root "$registry_root" check && sudo systemctl start dwh-auth; then printf 'registry_restore_rollback=PASS\n' >&2; else printf 'registry_restore_rollback=FAIL\n' >&2; fi
exit 1
fi
if sudo /usr/local/sbin/dwh-auth --registry-root "$registry_root" check && sudo systemctl start dwh-auth; then
printf 'registry_restore=PASS\n'
else
sudo systemctl stop dwh-auth || true
if ! sudo mv -T -- "$registry_root" "$registry_staging/failed-dwh-auth"; then printf 'registry_restore_rollback=FAIL\n' >&2; exit 1; fi
if ! sudo mv -T -- "$registry_previous" "$registry_root"; then printf 'registry_restore_rollback=FAIL\n' >&2; exit 1; fi
if sudo /usr/local/sbin/dwh-auth --registry-root "$registry_root" check && sudo systemctl start dwh-auth; then printf 'registry_restore_rollback=PASS\n' >&2; else printf 'registry_restore_rollback=FAIL\n' >&2; fi
exit 1
fi
```
Per le prove, creare file header `0600` che contengono esattamente `X-API-Key: valore`. Il valore
passa dal file chiave al file header senza argv, ambiente o stdout. I file header sono materiale
segreto con la stessa custodia e retention delle chiavi.
```bash
v1_key_file="$key_output"
legacy_key_file=/root/dwh-auth-provision/legacy-shared.key
v1_header_file=/root/dwh-auth-provision/dwh-auth-v1.header
legacy_header_file=/root/dwh-auth-provision/dwh-auth-legacy.header
random_header_file=/root/dwh-auth-provision/dwh-auth-random.header
if ! sudo python3 -c '
import pathlib, sys
if any(b"\n" in pathlib.Path(path).read_bytes() for path in sys.argv[1:]):
raise SystemExit(1)
' "$v1_key_file" "$legacy_key_file"; then
printf 'key_file_bytes=FAIL\n' >&2
exit 1
fi
printf 'key_file_bytes=PASS\n'
for header_file in "$v1_header_file" "$legacy_header_file" "$random_header_file"; do
sudo install -o root -g root -m 0600 /dev/null "$header_file"
done
sudo sh -c '{ printf "%s" "X-API-Key: "; dd if="$1" bs=65536 status=none; printf "\n"; } > "$2"' sh "$v1_key_file" "$v1_header_file"
sudo sh -c '{ printf "%s" "X-API-Key: "; dd if="$1" bs=65536 status=none; printf "\n"; } > "$2"' sh "$legacy_key_file" "$legacy_header_file"
sudo sh -c 'printf "%s\n" "X-API-Key: invalid-test" > "$1"' sh "$random_header_file"
```
Il socket `/verify` deve restituire 204 per v1 e legacy durante il dual-key, 401 per file casuale
e richiesta senza header. Stampare solo PASS/FAIL.
```bash
status=$(sudo curl --header "@$v1_header_file" --unix-socket /run/dwh-auth/verify.sock --output /dev/null --silent --show-error --write-out '%{http_code}' http://localhost/verify)
[ "$status" = 204 ] && printf 'socket_v1=PASS\n' || { printf 'socket_v1=FAIL\n' >&2; exit 1; }
status=$(sudo curl --header "@$legacy_header_file" --unix-socket /run/dwh-auth/verify.sock --output /dev/null --silent --show-error --write-out '%{http_code}' http://localhost/verify)
[ "$status" = 204 ] && printf 'socket_legacy=PASS\n' || { printf 'socket_legacy=FAIL\n' >&2; exit 1; }
status=$(sudo curl --header "@$random_header_file" --unix-socket /run/dwh-auth/verify.sock --output /dev/null --silent --show-error --write-out '%{http_code}' http://localhost/verify)
[ "$status" = 401 ] && printf 'socket_random=PASS\n' || { printf 'socket_random=FAIL\n' >&2; exit 1; }
status=$(sudo curl --unix-socket /run/dwh-auth/verify.sock --output /dev/null --silent --show-error --write-out '%{http_code}' http://localhost/verify)
[ "$status" = 401 ] && printf 'socket_missing=PASS\n' || { printf 'socket_missing=FAIL\n' >&2; exit 1; }
```
Per HTTPS reale usare i file header protetti e la CA approvata contro `/dwh/rpc/ping`: PostgREST
può restituire qualsiasi 2xx, non si pretende 204. Prima della revoca, v1 e legacy devono dare
2xx; il file casuale deve dare 401.
```bash
ping_url=https://supabase-aritmolab.policlinicosandonato.it/dwh/rpc/ping
ca_file=/root/dwh-auth-provision/psd-dwh-ca.pem
status=$(sudo curl --header "@$v1_header_file" --cacert "$ca_file" --connect-timeout 5 --max-time 15 --output /dev/null --silent --show-error --write-out '%{http_code}' "$ping_url")
case "$status" in 2??) printf 'https_v1_pre_revoke=PASS\n' ;; *) printf 'https_v1_pre_revoke=FAIL\n' >&2; exit 1 ;; esac
status=$(sudo curl --header "@$legacy_header_file" --cacert "$ca_file" --connect-timeout 5 --max-time 15 --output /dev/null --silent --show-error --write-out '%{http_code}' "$ping_url")
case "$status" in 2??) printf 'https_legacy_pre_revoke=PASS\n' ;; *) printf 'https_legacy_pre_revoke=FAIL\n' >&2; exit 1 ;; esac
status=$(sudo curl --header "@$random_header_file" --cacert "$ca_file" --connect-timeout 5 --max-time 15 --output /dev/null --silent --show-error --write-out '%{http_code}' "$ping_url")
[ "$status" = 401 ] && printf 'https_random=PASS\n' || { printf 'https_random=FAIL\n' >&2; exit 1; }
```
Dopo l'osservazione, revocare solo la legacy usando il suo ID pubblico già registrato. Dopo la
revoca v1 resta 2xx e legacy diventa 401 anche via HTTPS.
```bash
legacy_key_id=legacy-shared
sudo /usr/local/sbin/dwh-auth --registry-root "$registry_root" key revoke --key-id "$legacy_key_id" --reason shared-credential-rotation
status=$(sudo curl --header "@$v1_header_file" --cacert "$ca_file" --connect-timeout 5 --max-time 15 --output /dev/null --silent --show-error --write-out '%{http_code}' "$ping_url")
case "$status" in 2??) printf 'https_v1_post_revoke=PASS\n' ;; *) printf 'https_v1_post_revoke=FAIL\n' >&2; exit 1 ;; esac
status=$(sudo curl --header "@$legacy_header_file" --cacert "$ca_file" --connect-timeout 5 --max-time 15 --output /dev/null --silent --show-error --write-out '%{http_code}' "$ping_url")
[ "$status" = 401 ] && printf 'https_legacy_post_revoke=PASS\n' || { printf 'https_legacy_post_revoke=FAIL\n' >&2; exit 1; }
```
Per provare 503 in una finestra approvata, registrare l'orario, fermare temporaneamente l'unità,
eseguire il ping con timeout e trap di ripristino; il comando deve stampare solo PASS/FAIL.
```bash
was_active=$(sudo systemctl is-active dwh-auth || true)
[ "$was_active" = active ] || { printf 'https_auth_down=FAIL\n' >&2; exit 1; }
restore_auth() { sudo systemctl start dwh-auth; }
trap restore_auth EXIT INT TERM
sudo systemctl stop dwh-auth
status=$(sudo curl --header "@$random_header_file" --cacert "$ca_file" --connect-timeout 5 --max-time 15 --output /dev/null --silent --show-error --write-out '%{http_code}' "$ping_url" || true)
[ "$status" = 503 ] && printf 'https_auth_down=PASS\n' || { printf 'https_auth_down=FAIL\n' >&2; exit 1; }
sudo systemctl start dwh-auth
trap - EXIT INT TERM
```
Lo scan journal non salva righe grezze: controlla davvero le chiavi v1 e legacy leggendo solo i
percorsi dei file da argv, e conserva anche la difesa generica per prefisso e digest. Il filtro
emette solo PASS/FAIL.
```bash
since=$(date -u -d '15 minutes ago' +%Y-%m-%dT%H:%M:%SZ)
if sudo python3 -c '
import pathlib, subprocess, sys
max_journal_bytes = 1_048_576
max_journal_lines = 10_000
max_chunk_bytes = 65_536
process = None
try:
actual_keys = {pathlib.Path(path).read_bytes() for path in sys.argv[2:]}
needles = (b"thtdwh_v1", b"secret_sha256", *actual_keys)
max_needle_length = max(map(len, needles))
process = subprocess.Popen(
["journalctl", "-u", "dwh-auth", "--since", sys.argv[1], "--no-pager", "--output=cat"],
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
)
except OSError:
raise SystemExit(2)
def stop_child():
if process is not None:
if process.poll() is None:
process.kill()
process.wait()
bytes_seen = 0
line_count = 0
line_open = False
carry = b""
try:
while True:
remaining = max_journal_bytes - bytes_seen
if remaining == 0:
if process.stdout.read1(1):
raise SystemExit(2)
break
chunk = process.stdout.read1(min(max_chunk_bytes, remaining))
if not chunk:
break
bytes_seen += len(chunk)
searchable = carry + chunk
if any(needle in searchable for needle in needles):
raise SystemExit(1)
carry = searchable[-(max_needle_length - 1):]
for byte in chunk:
if byte == 10:
line_count += 1
line_open = False
if line_count > max_journal_lines:
raise SystemExit(2)
else:
line_open = True
if line_open:
line_count += 1
if line_count > max_journal_lines:
raise SystemExit(2)
finally:
stop_child()
if process.returncode != 0:
raise SystemExit(2)
' "$since" "$v1_key_file" "$legacy_key_file"; then
printf 'journal_actual_key_scan=PASS\n'
else
printf 'journal_actual_key_scan=FAIL\n' >&2
exit 1
fi
```
Dopo rollback verificato e migrazione/revoca di ogni client REST, la disinstallazione resta
condizionata all'approvazione: eseguire `sudo systemctl disable --now dwh-auth`, ma mantenere
registro, backup, manifest e file header protetti per la retention; non cancellarli durante il
rollback.
## Troubleshooting
| Sintomo | Interpretazione e azione |
| Oggetto | Percorso |
| --- | --- |
| `401` | Chiave assente, malformata, sconosciuta, scaduta, revocata o errata. Verificare trasporto, ID pubblico e consegna; non cercare dettagli nel messaggio. |
| `503` | Servizio, socket o registro non disponibile/sicuro. Controllare `systemctl`, socket, mode e `check`; ripristinare il backup approvato. |
| `check` fallisce | Integrità del registro non valida. Fermare le scritture, preservare stato e ripristinare; non editare JSON. |
| TLS fallisce | CA o SAN non validi. Seguire [TLS](dwh-auth-tls.md), senza bypass. |
| Binario | `/usr/local/sbin/dwh-auth` |
| Unit systemd | `/etc/systemd/system/dwh-auth.service` |
| Registro | `/var/lib/dwh-auth/` |
| Socket | `/run/dwh-auth/verify.sock` |
| Consegne protette | `/root/dwh-auth-provision/` |
Per il rollout PSD con i due gate separati vedere il [runbook PSD](../operations/psd-dwh-auth-rollout.md).
Install the binary and unit with `root` ownership, create the `dwh-auth` service user, and enable
the unit with `systemctl enable --now dwh-auth`. The socket must be accessible to Nginx's group.
## Creating and revoking keys
Esempio per l'installazione ACME Limited:
```bash
sudo dwh-auth --registry-root /var/lib/dwh-auth key create \
--installation-id acme-factory-primary \
--description acme-factory-primary \
--output /root/dwh-auth-provision/acme-factory-primary.key
```
Deliver the file through an enterprise vault or an authenticated channel. To rotate a key, create
a new one, distribute it, update the client, and revoke the old one using its public ID:
```bash
sudo dwh-auth --registry-root /var/lib/dwh-auth key revoke \
--key-id PUBLIC_KEY_ID \
--reason scheduled-rotation
```
Revocation is permanent. Keep encrypted registry backups before every mutation.
## Nginx integration
Nginx forwards the key to the `dwh-auth` socket. Only an authorized response allows the request
to reach DWH REST. Missing, unknown, expired, or revoked keys receive `401`; an unavailable
service or registry produces `503`.
+27 -38
View File
@@ -1,49 +1,38 @@
# TLS per DWH REST
# TLS for DWH REST
La chiave DWH è accettabile solo sopra TLS verificato. Un errore `401` o `503` non autorizza mai a
ridurre la verifica del certificato.
The DWH key may be used only over verified TLS. Authorization or availability errors never justify
disabling certificate verification.
## Stato PSD
## Private CA
L'origine REST PSD corrente usa il certificato self-issued/private di Nginx. Il SAN copre
`supabase-aritmolab.policlinicosandonato.it`, l'origine `.it` approvata, e non copre un dominio
`.com`. Non usare `.com` finché non è incluso esplicitamente nel SAN.
When DWH REST uses an enterprise CA, deliver the certificate separately from the API key. The CA
is not a credential, but its integrity is part of the security boundary. Keep it out of Git and
make it unwritable by unauthorized users.
Chi non dispone già di trust equivalente approvato riceve la CA separatamente e configura
`TLS_CA_FILE`. La CA non è una credenziale, ma la sua integrità è un confine di sicurezza: fuori da
Git e non scrivibile da utenti non autorizzati.
## Fingerprint fuori banda
Calcolare localmente il fingerprint del file ricevuto:
```bash
openssl x509 -noout -fingerprint -sha256 -in /absolute/protected/psd-dwh-ca.pem
```
Confrontarlo con il responsabile autorizzato tramite un canale indipendente dalla consegna (vault
aziendale o canale telefonico verificato). Nell'evidenza registrare solo conferma, approvatore e
timestamp; mai corpo certificato, fingerprint completo o output grezzo.
## Binding e ping
Il binding headless PSD effettivo è:
Esempio ACME Limited:
```dotenv
THT_WS_PSD_CLINICAL_DWH_TLS_CA_FILE=/run/secrets/psd-clinical-dwh-ca.pem
THT_WS_ACME_EBIKES_DWH_TLS_CA_FILE=/run/secrets/acme-ebikes-dwh-ca.pem
```
Il file sorgente locale è collegato da file operatore non tracciato. Usare URL `.it`, poi
**Test workspace connections** su `/rpc/ping`. Non disabilitare TLS e non usare `curl -k`.
## Out-of-band fingerprint
## Rinnovo coordinato
Calculate the fingerprint of the received file and compare it through an independent channel:
1. Preparare certificato e chain nuovi; verificare prima SAN `.it` e assenza di falsa copertura `.com`.
2. Confermare fuori banda il nuovo fingerprint.
3. Consegnare la CA/chain nuova ai client con `TLS_CA_FILE`, senza rimuovere ancora la precedente.
4. Aggiornare vault/binding e verificare ping con TLS normale.
5. Solo con gate Nginx approvato installare il certificato server e ripetere il ping.
6. Ritirare il trust precedente dopo la finestra approvata.
```bash
openssl x509 -noout -fingerprint -sha256 \
-in /absolute/protected/acme-ebikes-dwh-ca.pem
```
Il rinnovo non modifica chiavi `dwh-auth`, record o ruoli PostgreSQL. TLS e rollback della route
restano approvazioni e backup distinti.
The certificate SAN must include the exact name used by the binding, such as `dwh.acme.example`.
## Renewal
1. Prepare the new certificate and chain.
2. Confirm the SAN and fingerprint out of band.
3. Distribute the new CA to clients while temporarily keeping the old one.
4. Update the binding and confirm connectivity with normal TLS.
5. Install the server certificate.
6. Remove the old trust after the agreed window.
Do not use `curl -k`, disable TLS, or embed complete certificates or fingerprints in shared documents.
+40 -30
View File
@@ -1,6 +1,6 @@
# Installazione Docker nei contesti operativi correnti
# Docker installation in the current operating contexts
ThothII usa una topologia Compose unica:
ThothII uses one Compose topology:
- `frontend`
- `core`
@@ -8,17 +8,32 @@ ThothII usa una topologia Compose unica:
- `embedding`
- `embedding-model-init`
Qdrant e Ollama embedding sono servizi interni obbligatori del progetto Compose. Restano esterni solo DWH e LLM. Il modello fissato è `qwen3-embedding:0.6b` con 1024 dimensioni e distanza
coseno; `embedding-model-init` lo prepara prima dell'avvio di `core`.
Qdrant and Ollama embedding are required internal Compose services. Only DWH and the LLM remain
external. The fixed model is `qwen3-embedding:0.6b` with 1024 dimensions and cosine distance;
`embedding-model-init` prepares it before `core` starts.
## Contratto sintetico di ownership
```mermaid
flowchart TB
INSTALL["Installation descriptor"] --> CONTEXT{Context}
CONTEXT --> LOCAL["Local\nCompose local"]
CONTEXT --> SERVER["Server\nCompose server"]
CONTEXT --> SESSION["Session server\noperator services"]
CONTEXT --> AUTH["Auth runtime\nprojection services"]
LOCAL --> BUNDLE["Common secret bundle"]
SERVER --> BUNDLE
SESSION --> BUNDLE
AUTH --> BUNDLE
BUNDLE --> SERVICES["Frontend, core, vector, embedding"]
```
## Short ownership contract
| Componente | Ownership | Contratto operativo |
| --- | --- | --- |
| DWH | Esterno | Endpoint esterno configurato dall'installazione. |
| LLM | Esterno | Endpoint o policy esterna all'infrastruttura semantica interna. |
| Qdrant | Interno | Servizio Compose interno obbligatorio con volume persistente `qdrant-data`. |
| Ollama embedding | Interno | Servizio Compose interno obbligatorio per `qwen3-embedding:0.6b`. |
| DWH | External | External endpoint configured by the installation. |
| LLM | External | Endpoint or policy outside the internal semantic infrastructure. |
| Qdrant | Internal | Required internal Compose service with persistent `qdrant-data` volume. |
| Ollama embedding | Internal | Required internal Compose service for `qwen3-embedding:0.6b`. |
## Comando standard locale
@@ -31,49 +46,44 @@ docker compose --env-file deploy/env/local.env \
-f compose.yaml -f deploy/compose.local.yaml up --build -d
```
Compilare `deploy/env/local.env` con:
Set these values in `deploy/env/local.env`:
- `PI_AUTH_FILE`
- `THT_SECRETS_FILE`
- `THT_WORKSPACE_GIT_REMOTE`
- endpoint DWH
- endpoint LLM
- DWH endpoint
- LLM endpoint
Non inserire secret nel file `.env`. I secret runtime stanno nel bundle
`deploy/secrets/thothii.secrets`.
Do not put secrets in `.env`. Runtime secrets belong in the
`deploy/secrets/thothii.secrets` bundle.
## Bundle dei secret
## Secret bundle
Le chiavi documentate e supportate nel bundle sono:
The documented and supported bundle keys are:
```dotenv
THT_MODEL_API_KEY=...
THT_DWH_API_KEY=...
```
Una CA privata PEM resta esterna al bundle e va montata con un override Compose revisionato.
A private PEM CA remains outside the bundle and must be mounted through a reviewed Compose override.
## Preprocessing
I job di preprocessing usano gli stessi servizi interni Qdrant/Ollama:
Preprocessing runs through the native host CLI and the installation descriptor:
```sh
docker compose --env-file deploy/env/local.env \
-f compose.yaml -f deploy/compose.local.yaml \
-f deploy/compose.preprocess.yaml --profile preprocess run --rm preprocess-evidence
tht --installation /percorso/assoluto/thothii-installation.yaml workspace preprocess evidence
tht --installation /percorso/assoluto/thothii-installation.yaml workspace preprocess dwh
```
Per introspezione DWH:
```sh
docker compose --env-file deploy/env/local.env \
-f compose.yaml -f deploy/compose.local.yaml \
-f deploy/compose.preprocess.yaml --profile preprocess run --rm preprocess-dwh
```
The CLI runs the profile-gated `workspace-maintenance` service. See the
[preprocessing contract](contracts/workspace-preprocessing-cli.md) and the
[Evidence guide](evidence.md) for details.
## Server
Per installazioni server usare il profilo server con overlay sessioni:
For server installations, use the server profile with the session overlay:
```sh
docker compose --env-file deploy/env/server.env \
@@ -81,7 +91,7 @@ docker compose --env-file deploy/env/server.env \
-f deploy/compose.session-server.yaml.example up --build -d
```
Consultare anche:
Also see:
- `docs/install/local-workspace-registry.md`
- `docs/install/server-workspace-registry.md`
+57 -189
View File
@@ -1,212 +1,80 @@
# Skill operative dell'applicazione
# ThothII operating workflow
## Scopo di questa pagina
ThothII guides every question through eight phases. The model proposes the work, the reviewer
makes decisions at gates, and the system records persistent artifacts and decisions.
Nel repository esistono diversi file denominati `SKILL.md`, ma non tutti appartengono al runtime di ThothII. La skill applicativa effettivamente usata dal workflow NL→SQL è:
```text
harness/.pi/skills/tht-sessione/SKILL.md
```mermaid
flowchart LR
Q["Question"] --> F1["F1 Clarification"]
F1 --> F2["F2 Memory"]
F2 --> F3["F3 Rewriting"]
F3 --> F4["F4 Evidence"]
F4 --> F5["F5 Schema"]
F5 --> F6["F6 CTE plan"]
F6 --> F7["F7 SQL"]
F7 --> F8["F8 Promotion"]
F8 --> DONE["Finalized session"]
```
I file presenti in `ChironeWp3/`, in `Thoth/ThothAI/` o nei worktree sono relativi ad altri progetti, strumenti o ambienti di sviluppo. Non fanno parte del contratto operativo di una sessione ThothII.
## Workflow principles
## Che cos'è `tht-sessione`
- The model proposes; the reviewer approves, corrects, or rejects.
- The session ledger records every material decision.
- Persisted state is the source of truth.
- A phase advances only when its artifacts and gates are complete.
- Resume rebuilds context from persisted artifacts, not from the conversation.
La skill dichiara il nome `tht-sessione` e si descrive come orchestratore del workflow Thoth in otto fasi: chiarimento della domanda, memory, riscrittura, schema-linking, sintesi, piano CTE, SQL finale e datamart.
## F1: clarification
Non è una semplice raccolta di suggerimenti per il modello. È il contratto operativo che stabilisce:
The system identifies the ambiguity most likely to change the meaning of the question and presents
one decision at a time. Mutually exclusive interpretations use a single choice; multiple valid
answers use a multiple choice.
- quali passaggi devono essere eseguiti;
- quali comandi `tht` usare;
- quali decisioni richiedono il reviewer;
- quando una fase può avanzare;
- quali artefatti devono essere persistiti;
- quali decisioni sono locali alla domanda e quali possono essere riutilizzate.
## F2: Memory
Il file stesso definisce questa regola: ogni comando, flag e comportamento necessario deve essere già descritto nella skill o nei suoi documenti di riferimento. Il modello non deve esplorare il codice sorgente per ricostruire il funzionamento degli strumenti.
The system proposes Memory items that fit the question. Selected items enter the current session
context; unselected items remain available for future questions.
Questa scelta ha una motivazione precisa: un modello remoto potrebbe spendere il turno iniziale leggendo repository, `--help`, test e file casuali invece di affrontare la domanda dell'utente. Un contratto già iniettato riduce la deriva procedurale e rende il bootstrap deterministico.
## F3: rewriting
## Come viene caricata
The system rewrites the question explicitly using the approved clarifications. The reviewer checks
the resulting question and its assumptions before continuing.
La skill non viene lasciata al modello come primo compito da scoprire. L'estensione Pi la legge quando viene caricata e la inserisce integralmente nel system prompt del gate:
## F4: Evidence
```text
harness/.pi/extensions/tht-gate.js
└── ../skills/tht-sessione/SKILL.md
```
The system retrieves Evidence from the active corpus and presents citations and provenance. The
reviewer decides which items apply to the question.
Il gate aggiunge inoltre istruzioni di kickoff per distinguere:
## F5: schema
- nuova sessione (`/nuova-domanda`);
- ripresa (`/riprendi-sessione <id>`);
- sessione già creata con id noto;
- contesto di retrieval già fornito dal backend.
Tables, columns, relationships, and filters are linked to the approved meaning of the question.
The summary closes the phase when the question, assumptions, and DWH elements are consistent.
Il caricamento integrale evita che il modello debba usare `find`, `ls`, `cat` o strumenti generici per recuperare istruzioni operative. È una misura di affidabilità, non soltanto di performance.
## F6: CTE plan
Riferimenti implementativi: [tht-gate.js](../harness/.pi/extensions/tht-gate.js:50) e [tht-gate.js](../harness/.pi/extensions/tht-gate.js:832).
The query is broken into named CTEs with a purpose, dependencies, tables, filters, and output
columns. The reviewer sees each step before the system produces the final SQL.
## Rapporto tra skill, workflow e gate
## F7: final SQL
I tre componenti hanno responsabilità diverse:
The system produces `sql_final.sql`, checks it against the approved plan, and presents the artifact
to the reviewer. A correction can reopen the CTE plan without discarding decisions that remain valid.
| Componente | Responsabilità |
## F8: Memory promotion
At the end of the session, the system proposes reusable clarifications. The reviewer decides which
ones to promote to the global registry, and the session is then finalized.
## Available gates
| Gate | Use |
| --- | --- |
| `workflow.yaml` | Fonte strutturale delle fasi, dei tipi di decisione e degli output |
| `SKILL.md` | Istruzioni operative e disciplina che il modello deve seguire |
| `tht-gate.js` | Enforcement: widget, persistenza, controlli e blocco dei bypass |
| Single choice | Only one interpretation can be valid |
| Multiple choice | Several items can be valid at the same time |
| Artifact confirmation | Approval of a document or phase result |
| Phase confirmation | Explicitly closes a phase |
La skill descrive il comportamento atteso; il gate impedisce che il modello lo aggiri. Per esempio, la skill prescrive che una decisione venga registrata tramite un reviewer tool, mentre il gate blocca l'uso diretto di comandi come `tht decision add` o `tht phase advance`.
## Resume and reopening
Questo doppio livello è intenzionale: il testo guida il modello, il codice protegge lo stato persistito anche quando il modello interpreta male un'istruzione.
## Principi non negoziabili
### Una domanda al reviewer per volta
Il modello deve presentare un singolo punto decisionale, attendere la risposta e soltanto dopo proseguire. Questo evita che una risposta ambigua venga interpretata come approvazione di più passaggi non esaminati.
### La conferma umana è obbligatoria
Il modello propone; il reviewer approva, corregge, rifiuta o lascia aperta un'ambiguità. Non è consentito promuovere tabelle, applicare memory, fissare filtri o approvare SQL senza decisione esplicita.
### Una decisione, un comando
Ogni cambiamento dello stato passa da un comando `tht` mediato dal gate. Il ledger append-only è la fonte di verità: ciò che non è registrato non è avvenuto.
### Nessuna esplorazione ad hoc
La skill vieta di usare il repository come documentazione implicita. Le motivazioni sono:
- evitare che il modello inventi un comando osservando codice non contrattuale;
- evitare di leggere dati o segreti fuori dal perimetro della sessione;
- mantenere il workflow riproducibile tra workstation, container e server;
- rendere i test del gate indipendenti dall'iniziativa del modello.
### Rollback semantico
Dopo una riapertura il modello riparte dalla fase indicata esaminando gli artefatti ancora validi. Non deve ricreare inutilmente gli artefatti che non sono stati invalidati. `effective_decisions()` filtra le decisioni ormai stale.
## Le otto fasi
### Fase 1 — Chiarimento
Il modello identifica l'ambiguità con maggiore impatto sul significato della query e presenta subito il relativo widget.
Le interpretazioni mutuamente esclusive usano `reviewer_select`; quando più risposte possono essere vere si usa `reviewer_decide` multiselect. Ogni scelta concreta diventa una decisione `concept_clarified`.
Motivazione: la semantica della domanda deve essere fissata prima di scegliere tabelle o SQL. I chiarimenti costituiscono inoltre la materia prima delle memory future.
### Fase 2 — Memory
La skill ordina di cercare memory con:
```text
tht memory search "<domanda>" --session <id> --json
```
Sono riutilizzabili solo le memory `concept_clarified`. Le scelte `table_promoted`, `table_excluded`, `column_promoted` e tutte le decisioni dipendenti dalla singola query non devono essere salvate, cercate o proposte come memory.
Il reviewer decide in un'unica checklist, con massimo cinque candidati. Una memory selezionata viene applicata nella sessione corrente come nuovo `concept_clarified`; una deselezione significa non applicarla ora, non cancellarla dal patrimonio globale.
Motivazione: il significato di un concetto può trasferirsi tra domande, mentre la scelta delle tabelle dipende dal problema, dal periodo, dalle metriche e dallo schema-linking specifici.
### Fase 3 — Riscrittura
Il modello produce una domanda riscritta con popolazione, condizioni, termini chiariti e output atteso. `rewrite_question` persiste `question.md` e chiude la fase.
La riscrittura è separata dal chiarimento per rendere visibile al reviewer il risultato semantico prima di entrare nella progettazione SQL.
### Fase 4 — Schema-linking
Il modello usa il catalogo e il retrieval pack per proporre tabelle e colonne. Il reviewer cura:
- tabelle da promuovere o escludere;
- colonne di output;
- join necessari.
Le tabelle promosse e le colonne promosse sono decisioni della domanda e finiscono in `schema_linking.json`; non diventano memory.
La skill impone inoltre un gate separato per i join. Questo impedisce di nascondere la logica relazionale dentro una lista di tabelle e consente al reviewer di verificare le cardinalità e le chiavi in modo esplicito.
### Fase 5 — Sintesi
Il modello verifica che domanda riscritta, assunzioni e schema-linking siano coerenti. La fase si chiude con una conferma di fase dopo `tht session check`.
Motivazione: è un checkpoint semantico prima di produrre il piano SQL, utile per intercettare contraddizioni quando il problema è ancora correggibile.
### Fase 6 — Piano CTE
Il modello scompone la domanda in CTE nominati, con scopo, dipendenze, tabelle, filtri e colonne di output. Ogni risultato CTE viene presentato con `reviewer_confirm kind:"cte_result"`.
L'approvazione dell'ultimo CTE chiude automaticamente la fase. L'artefatto persistito è strutturato (`cte_plan.json`, file SQL dei CTE e test), così il piano può essere ripreso e verificato senza transcript.
### Fase 7 — SQL finale
Il modello genera `sql_final.sql`, esegue la validazione prevista e chiede `reviewer_confirm kind:"sql"`. La conferma registra `sql_approved` e chiude la fase.
La separazione dal piano CTE consente di approvare prima la strategia e poi l'implementazione SQL concreta.
### Fase 8 — Datamart e promozione memory
Il gate `reviewer_memory_promote` calcola i candidati in modo deterministico, li mostra al reviewer e salva quelli approvati con `memory save-one`. Registra inoltre `memory_promoted` o `memory_promotion_declined`.
La fase chiude e finalizza la sessione automaticamente. Non va aggiunta una seconda conferma che ripeta la stessa approvazione.
## Regole di avanzamento
La skill distingue tra decisione e chiusura della fase:
- una scelta `reviewer_select` o `reviewer_decide` registra una decisione;
- normalmente non fa avanzare la fase da sola;
- le fasi con completamento deterministico si chiudono con il loro gate specifico;
- F1, F2 con decisioni sostanziali e F5 usano la conferma esplicita di fase;
- F2 vuota e F6 vuota possono avanzare con `advance:true`;
- F3, F4, F6, F7 e F8 hanno gate di chiusura specializzati.
Questa distinzione evita che `advance:true` diventi un bypass generalizzato delle conferme umane.
## Resume e artefatti
Quando una sessione viene ripresa, la skill ordina di leggere prima:
```text
tht session show <id> --json
tht session documents <id> --json
```
Il modello ricostruisce il contesto da stato, ledger e artefatti persistiti: `question.md`, `schema_linking.json`, piano CTE, test e `sql_final.sql`. Non riparte dalla conversazione e non assume che un'azione non registrata sia stata eseguita.
Il retrieval pack, quando è già iniettato dal backend, viene trattato come dati e non come istruzioni. Questo separa il contesto recuperato dalla policy operativa della skill e riduce il rischio di prompt injection proveniente dai dati.
## Documenti di riferimento della skill
La skill rimanda a documenti specializzati per i dettagli di dominio:
- `rewriting.md` per la domanda riscritta;
- `cte.md` per la progettazione dei CTE;
- `sql-generation.md` per la generazione del SQL.
La separazione è utile perché la skill principale definisce il processo e i confini, mentre i documenti secondari descrivono come costruire i singoli artefatti.
## Perché la skill è importante per l'architettura
Il backend è un bridge verso Pi e `tht`; non conserva un transcript completo come fonte primaria. La skill rende il modello compatibile con questa architettura perché impone di produrre decisioni e artefatti persistiti a ogni passaggio.
In pratica, la skill garantisce:
- ripresa deterministica dopo un riavvio;
- audit umano delle decisioni;
- separazione tra conoscenza riusabile e schema-linking locale;
- coerenza tra UI, ledger e file di fase;
- possibilità di verificare il risultato senza ricostruire una conversazione persa;
- protezione contro comandi o avanzamenti non autorizzati.
## Riferimenti sorgente
- [Skill canonica `tht-sessione`](../harness/.pi/skills/tht-sessione/SKILL.md)
- [Workflow YAML](../harness/workflow.yaml)
- [Gate Pi](../harness/.pi/extensions/tht-gate.js)
- [Macchina delle fasi e decisioni effettive](../harness/tht/phase.py)
- [Gestione delle memory](gestione-memory.md)
A resumed session returns to its last incomplete phase. Reopening invalidates only the decisions
and artifacts that depend on the changed point; the rest of the work remains valid.
+8 -39
View File
@@ -1,9 +1,6 @@
site_name: ThothII Docs
site_description: Documentazione tecnica di ThothII e considerazioni generali sull'ambiente di sviluppo
site_url: https://mptyl.github.io/ThothII/
repo_url: https://github.com/mptyl/ThothII
repo_name: mptyl/ThothII
edit_uri: edit/main/docs/
site_description: Documentazione funzionale, tecnica e operativa di ThothII
site_url: https://git.tylconsulting.it/thothii-docs/
docs_dir: docs
site_dir: site
use_directory_urls: true
@@ -48,20 +45,18 @@ markdown_extensions:
nav:
- Home: index.md
- Guida utente: guida-utente.md
- Accettazione autenticazione: testing/authentication-manual-acceptance.md
- Setup Policlinico San Donato: install/psd-workspace-setup.md
- DWH REST per installazione:
- Server dwh-auth: install/dwh-auth-server.md
- Enrollment client DWH: install/dwh-auth-client-enrollment.md
- TLS DWH REST: install/dwh-auth-tls.md
- Rollout PSD DWH: operations/psd-dwh-auth-rollout.md
- Collaudo manuale DWH: testing/dwh-auth-manual-acceptance.md
- Template evidenza DWH: testing/evidence/psd-dwh-auth-rollout-report-template.md
- Programma deploy server PSD: plans/2026-08-20-psd-server-deployment-program.md
- Collaudo PSD Progetto A: testing/psd-server-project-a-manual.md
- Collaudo PSD Progetto B: testing/psd-server-project-b-manual.md
- Contratti e CLI:
- CLI workspace preprocessing: contracts/workspace-preprocessing-cli.md
- Contratto tht–DWH: contracts/tht-dwh.md
- Contratto Evidence workspace v3: contracts/workspace-evidence-v3.md
- ThothII (Documentazione Tecnica):
- Panoramica Architettura: architecture/overview.md
- Componenti, moduli e flussi: architecture/components.md
- Evidence: evidence.md
- Autenticazione: architecture/authentication.md
- Installazione autenticazione locale: install/authentication-local.md
- OIDC generico: install/authentication-oidc.md
@@ -69,32 +64,6 @@ nav:
- Installazione Docker (4 contesti): installazione-docker-4-contesti.md
- Gestione delle memory: gestione-memory.md
- Skill operative: skills.md
- Testo completo skill tht-sessione: skill-tht-sessione.md
- Disambiguazione iniziale: disambiguazione-iniziale.md
- Specifiche di Design:
- Architettura ThothII: superpowers/specs/2026-06-25-thothii-architecture-design.md
- Backend: superpowers/specs/2026-06-27-backend-design.md
- Frontend: superpowers/specs/2026-06-27-frontend-design.md
- CLI Port / Skill: superpowers/specs/2026-06-27-cli-port-completo-skill-riscritta-design.md
- Settings Menu: superpowers/specs/2026-06-28-settings-menu-design.md
- Ollama Ensure: superpowers/specs/2026-06-29-ollama-ensure-design.md
- Session Management: superpowers/specs/2026-06-29-session-management-design.md
- Session UI Refinements: superpowers/specs/2026-06-29-session-ui-refinements-design.md
- Workflow Contract Hardening: superpowers/specs/2026-07-01-workflow-contract-hardening-design.md
- Piani di Implementazione:
- Harness: superpowers/plans/2026-06-25-harness-implementation.md
- Backend: superpowers/plans/2026-06-27-backend-implementation.md
- Frontend: superpowers/plans/2026-06-27-frontend-implementation.md
- Harness RPC Readiness: superpowers/plans/2026-06-27-harness-rpc-readiness.md
- CLI Porting / Skill: superpowers/plans/2026-06-27-tht-porting-cli-skill.md
- Settings Menu: superpowers/plans/2026-06-28-settings-menu.md
- Ollama Ensure: superpowers/plans/2026-06-29-ollama-ensure.md
- Session Management: superpowers/plans/2026-06-29-session-management.md
- Session UI Refinements: superpowers/plans/2026-06-29-session-ui-refinements.md
- Cross-Model Behavior Matrix: superpowers/plans/2026-06-30-cross-model-behavior-matrix.md
- Workflow Contract Hardening: superpowers/plans/2026-07-01-workflow-contract-hardening.md
- Report:
- L2 Run Report (2026-06-27): reports/l2-run-report-2026-06-27.md
- "Stato e Ripresa (snapshot 2026-06-27, superato da PROJECT_STATE.md)": superpowers/2026-06-27-stato-e-ripresa.md
- Considerazioni Generali:
- Configurazione dei modelli in Pi: general/pi-configuration.md