chore: preserve root worktree documents

This commit is contained in:
Codex
2026-08-31 15:34:55 +02:00
parent b5db0cd3c1
commit fa7380b3a3
8 changed files with 1021 additions and 80 deletions
+17
View File
@@ -86,3 +86,20 @@ frontend (React/SSE) → backend (Fastify) → pi --mode rpc → tht/harness →
resume with 409 when `finalized` or `archived`, and `PiProcessManager.spawnFor` must send
`/riprendi-sessione <id>` (resume mode) vs `/nuova-domanda` (new) — sending the wrong prompt
silently turns a resume into a new question.
## Agent skills
### Issue tracker
Issues and specifications are tracked in GitHub Issues for `mptyl/ThothII`. See
`docs/agents/issue-tracker.md`.
### Triage labels
The repository uses the default canonical triage labels. See
`docs/agents/triage-labels.md`.
### Domain docs
The repository uses a single-context domain model rooted at `CONTEXT.md`, with
architectural decisions under `docs/adr/`. See `docs/agents/domain.md`.
+31 -80
View File
@@ -1,95 +1,42 @@
# Contesto di dominio di ThothII
## Architettura del workflow
## Workflow
**Workflow Kernel** — Il coordinatore deterministico che possiede lo stato del workflow,
le transizioni, il rollback, la finalizzazione e l'applicazione atomica degli esiti dei
moduli.
**Workflow core** — La parte condivisa del workflow NL→SQL che governa la sequenza
delle fasi, il ciclo di vita delle sessioni, la revisione umana e la persistenza. Non è
un processo distinto né un sistema estensibile di plugin.
**Workflow Module** — Una capacità incapsulata che espone un contratto versionato. Un
modulo può partecipare a più stage e non modifica direttamente lo stato del workflow.
**Internal workflow module** — Un insieme coeso di comportamento e conoscenza di
dominio sviluppato e rilasciato insieme a ThothII. È isolato per rendere locali le
modifiche, non per essere caricato dinamicamente o sostituito da implementazioni esterne.
**Stage** — Un punto del workflow, identificato semanticamente, nel quale viene invocato
un modulo. L'identità dello stage è indipendente dalla sua posizione visiva.
**Pi** — Il runtime agentico concreto usato da ThothII per le elaborazioni che
richiedono un modello. Segue le istruzioni del workflow e usa gli strumenti messi a
disposizione dai moduli interni.
**Display code** — L'etichetta di presentazione associata a uno stage, per esempio da
`F1` a `F8`. I display code alimentano gli indicatori di avanzamento nel frontend, ma non
sono usati come identità del workflow o chiavi di dipendenza.
**Admission** — Il controllo che stabilisce se una domanda può entrare nel workflow
NL→SQL oppure deve essere riformulata o rifiutata.
**Module outcome** — Il risultato proposto da un modulo: eventi tipizzati, modifiche agli
artifact, un'eventuale richiesta di revisione umana e uno stato di esecuzione. Il
Workflow Kernel valida e applica l'esito.
**Disambiguation** — La responsabilità di rendere esplicita e sottoporre a revisione
l’interpretazione di una domanda ambigua prima della generazione SQL.
**Revision request** — La proposta tipizzata con cui un modulo segnala che lo stage
corrente non può concludersi validamente senza rieseguire lo stesso stage o uno stage
precedente. Non produce direttamente una transizione: il Workflow Kernel valida la
richiesta, sospende l'avanzamento e, per riaprire uno stage già completato, attende una
decisione umana tipizzata. Il Kernel, non il modulo, determina gli eventi e gli artifact
causalmente da rendere stale.
**Evidence** — La conoscenza di dominio curata e citabile usata per fondare
l’interpretazione della domanda e le scelte effettuate durante il workflow.
**Question Admission** — Il controllo preliminare eseguito prima delle fasi da `F1` a
`F8`. Nella prima release distingue una domanda utilizzabile da input garbage e verifica
che la domanda appartenga allo scope dichiarato dal workspace. Il suo stato è mostrato
separatamente dagli otto indicatori di fase.
**Memory** — La conoscenza riutilizzabile derivata da decisioni o sessioni precedenti e
resa disponibile alle elaborazioni successive.
**Workspace scope** — La dichiarazione gestita e versionata di ciò che il database di un
workspace rappresenta e delle domande alle quali è destinato a rispondere. Question
Admission la usa come riferimento per valutare la pertinenza di una domanda.
**Datamart** — L’attività finale già esistente che gestisce l’eventuale produzione del
datamart dopo la generazione del SQL.
**Datamart Plugin** — Il modulo sostituibile che implementa lo stage semantico
`datamart`, presentato con display code `F8`. La promozione della memory e la
finalizzazione della sessione non appartengono al Datamart Plugin.
**Ordered workflow** — La pipeline deterministica composta dal preflight Admission,
dagli otto stage principali ordinati da `F1` a `F8` e dalla finalizzazione. L'ordine
degli stage è esplicito; il workflow non è un DAG generale.
**Extension point** — Una posizione semantica nel lifecycle dell'Ordered workflow alla
quale possono contribuire uno o più moduli senza diventare nuovi stage visibili. Un
extension point non possiede un display code.
**Stage state** — La proiezione deterministica degli eventi del workflow che descrive
uno stage come `pending`, `ready`, `running`, `awaiting_human`, `completed`, `skipped` o
`failed`. Non è un valore corrente memorizzato separatamente dal ledger.
**Required contribution** — Il contributo di un modulo a un extension point che deve
concludersi o essere esplicitamente saltato secondo policy prima che il workflow possa
avanzare.
**Best-effort contribution** — Il contributo di un modulo il cui fallimento viene
registrato e mostrato come warning, ma non impedisce al workflow di avanzare.
**Blocked workflow** — La proiezione complessiva di un workflow che non può avanzare a
causa di uno stage o di un contributo required fallito o non disponibile. `Blocked` non
è uno Stage state autonomo.
**Module invocation** — Una singola richiesta del Workflow Kernel a un modulo in uno
stage o extension point. Conserva la stessa identità attraverso eventuali retry, che
sono tentativi distinti della medesima invocation.
**Stage skip** — La conclusione esplicita di uno stage senza eseguirne il comportamento.
È ammessa soltanto dalla policy dello stage e registra motivo e attore; un fallimento non
equivale mai implicitamente a uno skip.
**Stage reopen** — La riapertura di uno stage non finalizzato che rende stale gli esiti
causalmente successivi. Gli effetti esterni già prodotti richiedono una marcatura o una
compensazione esplicita e non sono presentati come automaticamente annullati. Può essere
applicata dal Workflow Kernel in seguito all'approvazione di una Revision request, ma
non può essere eseguita direttamente da Pi o da un Workflow Module.
**Completion policy** — La regola con cui uno stage si conclude: `automatic` quando il
kernel può verificarne deterministicamente l'esito, oppure `review_required` quando è
necessaria un'approvazione umana tipizzata.
**Paused session** — Una sessione interrotta intenzionalmente ma resumibile. L'azione
**Paused session** — Una sessione interrotta intenzionalmente ma resumibile. L’azione
“Stop and save” mette la sessione in pausa; non la completa e non la marca come fallita.
**Finalized session** — Una sessione completata con esito canonico e immutabile. Una
correzione successiva crea una nuova sessione derivata, collegata a quella precedente.
**Finalized session** — Una sessione completata che non accetta ulteriori modifiche al
proprio risultato.
**After-finalize hook** — Una notifica o attività best-effort eseguita tramite outbox
dopo la finalizzazione. Non può modificare il ledger, gli artifact canonici o lo stato
terminale della sessione.
**Workspace scope** — La dichiarazione di ciò che il database di un workspace
rappresenta e delle domande alle quali è destinato a rispondere.
## Catalogo dei metadati
@@ -103,8 +50,12 @@ Workspace Database. Non definisce quali elementi partecipano al workflow SQL.
**Database Profile** — L'insieme curato di scope, descrizioni e metadati semantici
associato a un Workspace Database.
**AI Proposal** — Un contenuto generato con l'ausilio dell'AI che non è ancora stato
approvato come contenuto canonico.
**Generated comment** — Il testo descrittivo prodotto dall'AI per una tabella o una
colonna. Rimane distinto dalla descrizione finché l'utente non ne richiede la copia.
**Core Schema Selection** — Il sottoinsieme di tabelle, colonne e relazioni di un
Workspace Database reso disponibile al processo core di ThothII. È distinto dal
Workspace scope, che descrive il dominio e le domande ammesse.
**Publication** — Una versione approvata e immutabile dei contenuti del Metadata
Catalog resa disponibile ai suoi consumatori.
+25
View File
@@ -0,0 +1,25 @@
# Domain documentation
ThothII uses a single domain context.
## Before exploring
Read:
- `CONTEXT.md` for the canonical domain vocabulary;
- relevant ADRs under `docs/adr/`, when present.
Missing documents are not an error. Domain terms and ADRs are created only when an
actual decision makes them necessary.
## Vocabulary
Use the canonical terms defined in `CONTEXT.md`. Avoid introducing synonyms for
concepts already defined there.
When a required concept is absent, use `domain-modeling` to clarify and record it.
## Architectural decisions
If proposed work contradicts an existing ADR, surface the conflict explicitly rather
than overriding the decision silently.
+33
View File
@@ -0,0 +1,33 @@
# Issue tracker: GitHub
Issues and specifications for this repository live in GitHub Issues. Use the `gh` CLI
for all operations.
## Repository
- Repository: `mptyl/ThothII`
- URL: https://github.com/mptyl/ThothII
- Pull requests as a triage surface: no
## Conventions
- Create issues with `gh issue create`.
- Read issues and comments with `gh issue view`.
- Apply labels with `gh issue edit`.
- Record resolutions in a comment before closing an issue.
- Refer to issues by their linked title in user-facing documentation, not only by
number.
## Wayfinding operations
- A Wayfinder map is an issue labelled `wayfinder:map`.
- Decision tickets are child issues labelled `wayfinder:research`,
`wayfinder:prototype`, `wayfinder:grilling` or `wayfinder:task`.
- Use native GitHub sub-issues and issue dependencies when available.
- If native sub-issues are unavailable, use a task list in the map and add
`Part of #<map>` to each ticket.
- If native dependencies are unavailable, add `Blocked by: #<issue>` to the ticket.
- An open, unassigned and unblocked child issue belongs to the frontier.
- Claim a ticket by assigning it before beginning work.
- Resolve a ticket by recording the decision in a comment, closing it and adding a
linked one-line summary to the map.
+9
View File
@@ -0,0 +1,9 @@
# Triage labels
| Canonical role | GitHub label | Meaning |
| ----------------- | ----------------- | --------------------------------------------- |
| `needs-triage` | `needs-triage` | The issue still needs evaluation |
| `needs-info` | `needs-info` | More information is required |
| `ready-for-agent` | `ready-for-agent` | Ready for autonomous agent work |
| `ready-for-human` | `ready-for-human` | Requires human intervention or implementation |
| `wontfix` | `wontfix` | Will not be actioned |
@@ -0,0 +1,279 @@
# PRD — Modularizzazione a moduli incapsulati: disambiguazione, evidence, memory
**Status:** riflessione architetturale di plausibilità — nessuna decisione implementativa presa qui; il
documento fissa confini, contratti condivisi e precondizioni da cui potranno nascere ticket e piani separati
**Data:** 2026-08-21
**Autore:** analisi del codice su main (`5f05754`), su richiesta del proprietario; incrocia e non sostituisce
i documenti evidence-canonica già esistenti (sez. 9)
**Uso:** riferimento per il programma di sviluppo parallelo dei tre sottosistemi (disambiguazione, gestione
evidence, gestione memory); definisce cosa deve essere vero *prima* che i tre flussi possano girare in
parallelo senza impatti incrociati
---
## 1. L'obiettivo del proprietario
Avviare uno sviluppo che cambi **in parallelo** tre aree del sistema:
1. **disambiguazione** (estratta e separata dal workflow);
2. **gestione delle evidence** — nuovo componente che espone un'API verso il resto del workflow ma resta
ben incapsulato al suo interno;
3. **gestione della memory** — con lo stesso grado di forte incapsulamento.
Vincolo accettato: le modifiche fatte su un modulo **non devono impattare gli altri moduli** se non per
eventuali ridefinizioni dei contratti (API) esposti.
## 2. Verdetto sintetico
**Plausibile, e il codebase è insolitamente ben predisposto per farlo.** Ma con tre precisazioni oneste:
1. i tre componenti sono oggi a **tre livelli di maturità diversi**: evidence è quasi un modulo, memory è
già incapsulata meglio di quanto sembri, la disambiguazione **non è un componente di codice** e
"estrarla" è un lavoro di natura diversa (sez. 4);
2. esistono **due "quinti moduli" nascosti** — il ledger dei tipi di decisione con `workflow.yaml`, e il
gate `tht-gate.js` con il vector store — che il piano a tre vie deve contemplare, altrimenti il
parallelismo si rompe dove non si sta guardando (ledger e gate sono spiegati in sez. 3; le implicazioni
in sez. 5);
3. gli accoppiamenti reali **non sono negli import Python**: sono nei decision type del ledger, nelle
proiezioni `search pack`/`search find`, nel modello di contenuto della memory e negli id evidence
citabili. Sono quelli i contratti da definire e versionare **prima** del parallelismo (sez. 6–7).
## 3. Ledger e gate: i due elementi cardine del workflow
Il resto del documento usa di continuo due concetti che conviene fissare qui, perché sono
i punti dove i tre moduli si incontrano davvero: il **ledger** (dove lo stato viene
registrato) e il **gate** (chi decide e chi scrive).
### 3.1 Il ledger — il registro delle decisioni (`review_decisions.jsonl`)
Una sessione è una directory: manifest + artefatti per fase (`question.md`,
`schema_linking.json`, `sql_final.sql`, …) + un file `review_decisions.jsonl`. Il ledger
è **append-only**: una riga JSON per ogni decisione del reviewer, con numero progressivo
`seq`, timestamp, `type`, `subject`, `detail`, `rationale` (più i campi di rollback
`retracts`/`phase`). I `DecisionType` definiti in `tht/decisions.py` sono esattamente 30.
Esempio concreto: a F1 il reviewer sceglie "per 'ablazione' intendo la colonna
`flag_ablazione` della fact delle procedure" → nel ledger viene accodata una riga come
`{"seq": 3, "ts": "…", "type": "concept_clarified", "subject": "ablazione", "detail": "…",
"rationale": "…"}`.
Due proprietà lo rendono cardine:
1. **la fase corrente non è memorizzata: è calcolata** rileggendo (fold) il ledger
(`tht/phase.py`). Il ledger è la fonte di verità — "ciò che non è registrato non è
accaduto". Il rollback non cancella nulla: aggiunge `decision_retracted`/
`phase_reopened` e la vista *effettiva* (`effective_decisions`) esclude le righe
ritirate e quelle rimaste stale oltre il punto di rollback;
2. **è il canale di comunicazione tra i moduli**: la disambiguazione *scrive*
`concept_clarified`/`ambiguity_open`/`value_grounded`; l'evidence *scrive*
`evidence_accepted`/`evidence_rejected`/`concept_formula_approved|rejected`; la memory
*legge* `concept_clarified` (per costruire le promozioni) e *scrive* `memory_promoted`/
`memory_promotion_declined`. Per questo l'insieme dei decision type **è** un contratto:
ridefinirlo (sez. 6.1) è l'equivalente di cambiare una API tra moduli.
### 3.2 Il gate — l'estensione che impone il human-in-the-loop (`tht-gate.js`)
ThothII non esegue il workflow con codice: lo esegue **un agente LLM (Pi)** che segue
SKILL.md. Il modello propone, ma non decide e non persiste direttamente. Il gate è
l'estensione JavaScript di Pi (~2000 righe, `harness/.pi/extensions/tht-gate.js`) che
svolge tre lavori:
1. **traduce le proposte del modello in widget per l'umano**: il modello chiama un tool
del gate — `reviewer_select` (scelta singola), `reviewer_decide` (multiselect),
`reviewer_confirm` (gate su artefatto/fase) più i specializzati `rewrite_question`,
`reviewer_schema_linking`, `reviewer_datamart`, `reviewer_memory_promote`,
`write_schema_linking`, `write_cte_sql`, `write_final_sql`; il gate costruisce il
descriptor del widget → RPC → backend → SSE → browser, dove il frontend lo renderizza
dal registry `src/widgets/`;
2. **persiste attraverso un unico canale**: alla risposta del reviewer, è il gate (non il
modello) a invocare la CLI `tht` (es. `tht memory promote --preview --json`,
`tht session sync-schema-linking`) e ad accodare la riga nel ledger;
3. **fa rispettare le regole (enforcement)**: l'hook anti-bypass blocca `tht phase
advance`, `tht decision add`, `tht memory promote|save-one` se il modello prova a
eseguirli direttamente dalla shell; i payload vengono validati (TypeBox) e gli
artefatti mostrati al reviewer sono ricostruiti in modo deterministico (es. il widget
CTE mostra il SQL persistito via `tht cte info`, non il testo del modello); le
chiusure di fase deterministiche sono automatiche.
Esempio concreto (F8): il modello chiama `reviewer_memory_promote`; il gate esegue
`tht memory promote --preview --json`, costruisce la checklist pre-selezionata; il
reviewer conferma; il gate salva le memory scelte (`memory save-one`), registra
`memory_promoted`/`memory_promotion_declined` nel ledger e finalizza la sessione. Il
modello non ha mai scritto nulla di propria mano.
Perché è cardine per la modularizzazione: il gate è **l'unico punto** in cui i tre moduli
appaiono all'utente e in cui le loro regole vengono imposte — ogni cambiamento a
disambiguazione/evidence/memory ha quasi sempre una controparte nel gate (sez. 5.2).
## 4. Stato attuale dei tre componenti
### 4.1 Evidence — il più vicino a essere un modulo
Fatti verificati nel codice:
- esiste già un **port vero** (`harness/tht/ports/evidence.py`: `discover`/`acquire`, credential-free,
contratti Pydantic congelati) con **tre adapter** dietro il port (`adapters/evidence/{filesystem,http,s3}.py`);
- la pipeline di canonizzazione/indicizzazione vive in `harness/tht/corpus/` (sorgente → normalize →
chunk → embed → vector store, generazioni isolate stage→publish);
- la **superficie di consumo del workflow è già quasi tutta incanalata in due comandi con JSON pulito**:
`tht search pack` (sezione evidence: top 5, proiezione `{title, status, excerpt[:400]}`) e
`tht search find [--kind evidence|formula]`. Quella proiezione è già di fatto un'API: evidence-canonica
può rifare tutto dietro di essa (card, involucro tipizzato, retrieval duale) senza che F1 se ne accorga,
finché il contratto di proiezione regge;
- l'**inventario dei punti di consumo è già fatto**: `verifica-workflow.md` elenca i 12 touchpoint (T1–T12)
con meccanismo e requisito. Quel documento **è** la bozza del contratto esterno del modulo.
Ciò che manca all'incapsulamento sono esattamente i tre GAP lì identificati:
- **GAP 1 — identità citabile**: il processo tratta l'id evidence come documento (F3 lo cita, F4 lo attacca
ai candidati, il report di sessione lo risolve in file via `resolve_evidence_file`/`rglob`). La decisione
già presa (id citabile `doc#card`) richiede l'estensione del resolver: è l'ultimo passo di contratto da
chiudere;
- **GAP 2 — formula store**: lettura cablata e funzionante, scrittura senza proprietario di produzione.
La decisione già presa (le card famiglia 3 del corpus canonico sono fonte di verità; i `.sql.md` sono
derivati della pipeline) assegna il proprietario senza toccare il contratto di lettura D14b;
- **GAP 3 — conoscenza di dominio nei testi istruzionali**: dialetto, ricette temporali PSD, esempi clinici
dentro SKILL.md e reference doc. È contenuto evidence sparso nel workflow: la migrazione verso il corpus
canonico **è parte del lavoro di confine**, non un'attività cosmetica successiva.
Nota trasversale: il ciclo di vita evidence attraversa **due linguaggi** — materializzazione e ciclo di vita
della collection stanno nel backend TypeScript (P4/P6), corpus e ricerca nello harness Python. L'API del
modulo deve quindi essere il contratto CLI/JSON (che è già lo stile di casa: `--json` pristine), non
un'interfaccia Python.
### 4.2 Memory — già incapsulata, con un accoppiamento semantico da esplicitare
Fatti verificati:
- il modulo vive in `harness/tht/memory.py` + `tht/solved.py` + `cli/memory_cmd.py`, con registro JSONL
a scrittura atomica e dedup del contenuto reviewer-visibile;
- i punti di contatto col workflow sono **esattamente tre**: F2 (ricerca + applicazione via gate), F8
(gate di promozione), finalize (indicizzazione `solved_question`, best-effort);
- il gate consuma la memory **solo tramite CLI**: `tht memory promote --preview --json` e
`memory save-one`; la logica di calcolo dei candidati (dedup, decisioni storie post-rollback, seq
rifiutate, esclusione delle già decise nella sessione) è tutta dentro memory.py. È incapsulamento vero,
già operativo.
L'accoppiamento che conta non è di codice ma **semantico**: `REUSABLE_TYPES = {"concept_clarified"}` —
il modello di contenuto della memory è definito dal tipo di output della disambiguazione. Se il ridisegno
F1 cambia cosa sia un chiarimento (target group, tassonomia dell'ambiguità, caso unanswerable), cambia
anche cosa la memory può promuovere e come. **Il contratto disambiguazione→memory va definito prima di
far girare i due flussi in parallelo**, o si scontreranno esattamente lì.
### 4.3 Disambiguazione — non è un componente di codice
Il punto onesto: **non c'è niente da "estrarre" nel senso classico**. Oggi la disambiguazione è il
comportamento dell'LLM in F1/F3/F4, governato da (ledger e gate: sez. 3):
- **prosa** (SKILL.md + `rewriting.md`): una sola ambiguità per volta, opzioni informate, "lascia aperta
l'ambiguità" esplicita;
- **retrieval**: pack iniettato (`retrieval_pack.md`), `tht search find`, `tht schema render`, LSH con
`aggregate_lsh_multi` che espone *tutte* le colonne dove un valore appare (es. "ablazione" su flag
booleano + campo patologia testuale);
- **ledger**: `concept_clarified`, `ambiguity_open`, `value_grounded`, `question_rewritten`;
- **gate**: i widget che presentano le interpretazioni candidate.
"Extractorla" significa quindi, in ordine:
1. **definire il suo contratto input/output** — in: domanda + contesto di retrieval; out: ambiguità con
target group, opzioni candidate, segnale unanswerable distinto dall'ambiguo. È ciò che
`impact-ridisegno-disambiguazione.md` §3 già elenca (retrieval duale lessicale+semantico, target group,
unanswerable);
2. **darle una superficie** che l'orchestratore consuma (sezione del pack, comando, sezione skill dedicata)
invece di comportamento libero del modello;
3. **spostare la conoscenza che usa** fuori da SKILL.md e dentro le evidence (GAP 3).
Rischio specifico e strutturale: l'LLM è colla che **può bypassare qualunque contratto su carta**. In
questo codebase l'unico enforcement che funziona è a livello gate/CLI — il pattern è già dimostrato
dall'hook anti-bypass che blocca `tht phase advance`/`decision add` da shell e `tht memory promote`/
`save-one` fuori dal gate. Finché il contratto di disambiguazione non è enforcement (hook + CLI + skill
come contratto completo), la proprietà "le modifiche non impattano altri moduli" è una speranza, non una
proprietà verificabile.
## 5. I due quinti moduli nascosti
### 5.1 Il ledger + `workflow.yaml` — la colonna vertebrale condivisa
Tutti e tre i moduli leggono/scrivono il decision ledger (`review_decisions.jsonl`, sez. 3.1): la
disambiguazione
emette `concept_clarified`/`value_grounded`; l'evidence emette `evidence_accepted`/`evidence_rejected`/
`concept_formula_approved|rejected`; la memory legge `concept_clarified` ed emette `memory_*`. Il campo
`emits` di `workflow.yaml` accoppia fasi e decision type; `phase.py` piega il ledger per calcolare la
fase corrente.
Questo è un **bene**: il ledger è già un contratto a eventi tra i moduli. Ma è superficie condivisa —
tutti e tre i flussi vorranno toccarla. Va trattata come contratto a sé (quarto), con controllo dei
cambiamenti, non come territorio libero.
### 5.2 Il gate (`tht-gate.js`) e il vector store — i punti caldi di merge
- **Il gate** (sez. 3; ~2000 righe + builders/enrich/artifact-contracts) renderizza candidati evidence, dedup
opzioni memory, costruisce il widget schema-linking con id evidence, guida F2/F8 memory e fa enforcement
anti-bypass. **Tutti e tre i flussi avranno bisogno di modifiche al gate**: se non viene spaccato lungo
gli stessi confini dei moduli, diventa il punto caldo di merge del programma parallelo.
- **Il vector store**: una collection Qdrant per workspace, kind-separata (`schema_table`, `schema_column`,
`evidence`, `memory`, `solved_question`); il contratto di collection e payload-index è di proprietà del
backend TS (P4), lettura/scrittura sono Python. I record schema/evidence sono revision-scoped (P3),
memory/solved workspace-wide. Se evidence-canonica cambia la forma dei record/payload (il frontmatter
già fluisce libero nei metadata), gli assunti di indicizzazione della memory ne risentono. Il vector
store va designato come **infrastruttura condivisa con contratto proprio**, non come territorio
implicito di uno dei tre.
## 6. I contratti che sono le vere API
Il parallelismo è protetto da quattro contratti, nessuno dei quali è un'interfaccia Python:
1. **Decision type del ledger** (`workflow.yaml` `emits` + `decisions.py`): l'interfaccia a eventi tra i
tre moduli e il workflow. Ogni ridefinizione è una modifica di contratto, va versionata e comunicata;
2. **Proiezioni di consumo evidence** (`search pack` / `search find --kind evidence|formula`): la forma di
ciò che F1/F3/F4 vedono. È il cuore del ticket "Consumo per fase", che già porta i requisiti della F1
ridisegnata e **non va congelato prima** che il disegno F1 abbia almeno uno schizzo;
3. **Modello di contenuto memory** (`REUSABLE_TYPES` + campi del record + proiezione `promote --preview
--json`): dipende semanticamente dal contratto di output della disambiguazione (sez. 4.2);
4. **Id evidence citabile** (`doc#card` + estensione del resolver id→file): il contratto che tiene insieme
F3 (citazioni), F4 (candidati), decisions (subject) e report di sessione.
## 7. Ordine di sviluppo consigliato
Coerente con quanto già deciso in `map.md` e nell'impact doc:
1. **schizzo del disegno F1** della disambiguazione (anche non finale): target group, retrieval duale,
unanswerable — perché il contratto di consumo va progettato *contro la F1 ridisegnata*;
2. **chiusura del contratto "Consumo per fase"** (cerniera tra evidence-canonica e F1): proiezioni,
filtri, id citabili `doc#card` con resolver esteso;
3. **definizione del contratto disambiguazione→memory** (cosa è riusabile, con quali campi);
4. **solo allora** i tre flussi girano in parallelo, ciascuno nel suo worktree.
Strumento di protezione del parallelismo già disponibile in casa: il **pattern dei port-contract test**
(`test_evidence_port_contract.py`, `test_vector_port_contract.py`, `test_dwh_port_contract.py`).
Estenderlo ai nuovi confini (proiezioni evidence, contratto memory, enforcement di disambiguazione) è il
modo più economico per rendere l'indipendenza dei moduli **verificabile** invece che sperata: un flusso
rompe l'altro se e solo se rompe un test di contratto, non per effetti nascosti.
## 8. Rischi e mitigazioni
| Rischio | Natura | Mitigazione |
|---|---|---|
| L'LLM orchestratore bypassa il contratto di disambiguazione | strutturale (il workflow è LLM-driven) | enforcement gate/CLI sul modello dell'anti-bypass esistente; SKILL come contratto completo, non consiglio |
| Collisione disambiguazione↔memory su `concept_clarified` | semantica | contratto esplicito (sez. 6.3) definito prima del parallelismo |
| `tht-gate.js` punto caldo di merge | organizzativa | spaccare il gate lungo gli stessi confini dei moduli (builders per modulo) |
| Deriva del contratto vector store tra TS e Python | infrastrutturale | contratto di collection/payload/kind di proprietà unica, test di contratto su entrambi i lati |
| modifiche contagiose a `workflow.yaml`/ledger | processo | change control sul ledger come quarto contratto; `emits` data-driven già aiuta |
| contratti congelati troppo presto (Consumo per fase) | sequenza | ordine sez. 7: prima schizzo F1, poi chiusura contratto |
## 9. Riferimenti
- `docs/wayfinder/evidence-canonica/map.md` — mappa del sottoprogetto evidence-canonica (destinazione,
decisioni chiuse, nebbie);
- `docs/wayfinder/evidence-canonica/prototypes/verifica-workflow.md` — i 12 touchpoint evidence (T1–T12) e
i GAP 1–3: l'inventario dell'interfaccia esterna del futuro modulo evidence;
- `docs/wayfinder/evidence-canonica/prototypes/impact-ridisegno-disambiguazione.md` — valutazione d'impatto
del ridisegno F1 (requisiti: retrieval duale, target group, unanswerable; coordinamento dei due progetti);
- `docs/wayfinder/evidence-canonica/prototypes/PROTOTIPO-formato-unita-canonica.md` e `tipologia-evidence.md`
— formato card+involucro chiuso il 2026-08-21 e casistica a 7 famiglie;
- `docs/disambiguazione-iniziale.md` — stato attuale della disambiguazione;
- `docs/gestione-memory.md` — disegno della gestione memory;
- `harness/workflow.yaml`, `harness/.pi/skills/tht-sessione/SKILL.md`, `harness/.pi/extensions/tht-gate.js`
— le fonti di codice citate (fasi, discipline, gate);
- `docs/prd/2026-08-09-workspace-preprocessing-prd.md` — il PRD da cui nasce l'attuale catena
preprocessing/registry (contesto P2–P6 citato in sez. 4.1 e 5.2).
@@ -0,0 +1,141 @@
# Brief — Refactoring modulare conservativo del workflow
**Stato:** input autorevole per `grill-with-docs`; non è ancora una specifica
implementativa.
## Obiettivo
Riorganizzare l’attuale applicazione ThothII per rendere locali e manutenibili le
modifiche a Disambiguation, Evidence e Memory, preservando integralmente comportamento,
frontend e flusso operativo esistenti.
La prima milestone è esclusivamente un refactoring. Non introduce nuove funzionalità e
non generalizza ThothII come piattaforma di plugin.
## Vincoli già decisi
- Il progetto continuerà a essere sviluppato e mantenuto da un solo proprietario, in un
unico repository e con un unico rilascio.
- I moduli sono package interni secondo normali criteri di programmazione a oggetti.
Non devono essere distribuibili indipendentemente, caricati dinamicamente o scritti da
terze parti.
- I moduli non devono condividere un contratto universale. Ogni modulo può presentare al
workflow una piccola façade adatta alla propria responsabilità.
- Moduli e fasi non coincidono necessariamente: un modulo può contribuire a più fasi e
una fase può coordinare più moduli senza introdurre un protocollo generale.
- Pi resta il runtime concreto per le elaborazioni assistite dal modello. Non viene
introdotta un’astrazione `ModelRuntime`.
- Il workflow resta ordinato e continua a usare `harness/workflow.yaml`, il ledger delle
decisioni, i documenti di sessione e gli attuali gate human-in-the-loop.
- Frontend, eventi SSE, widget, comandi `tht`, JSON, codici di uscita e formato delle
sessioni devono rimanere compatibili.
- Non sono previste migrazioni dei dati delle sessioni esistenti.
- Il flusso SQL centrale, inclusi schema linking, sintesi, CTE e SQL finale, resta nel
workflow core durante questa milestone.
- Un altro componente futuro, estraneo al workflow e al core, non appartiene a questo
refactoring.
## Moduli interni desiderati
### Admission
Occupa l’inizio logico del workflow. Nella prima milestone è un pass-through e ammette
sempre la domanda, così il comportamento osservabile non cambia.
Per una fase futura è già stata scelta questa policy: una domanda non ammessa può essere
riformulata nella stessa sessione; dopo il terzo tentativo fallito la sessione fallisce.
La classificazione e l’esperienza utente non fanno parte del primo refactoring.
### Disambiguation
Raccoglie senza cambiarne il comportamento la logica attuale di chiarimento,
interpretazione revisionata e riscrittura della domanda, comprese istruzioni Pi,
validazioni e presentazione delle relative decisioni.
### Evidence
Consolida le superfici esistenti di acquisizione, canonizzazione, ricerca e citazione.
Mantiene i seam già reali verso filesystem, HTTP e S3 e il contratto CLI/JSON necessario
fra Python e TypeScript. Non introduce un contratto generale per gli altri moduli.
### Memory
Raccoglie senza variazioni funzionali ricerca, selezione, promozione e conoscenza delle
domande già risolte, insieme alle istruzioni e ai gate specifici oggi dispersi.
### Datamart
Incapsula senza modificarla la parte di gestione del datamart oggi svolta nella fase F8.
La fase F8 rimane visivamente e funzionalmente invariata, ma l’eventuale comportamento
Memory che contiene appartiene al modulo Memory. La futura integrazione intelligente del
SQL in un progetto dbt o in un flusso ETL è deliberatamente rinviata al momento in cui
questo modulo verrà sviluppato.
## Responsabilità che restano condivise
Il workflow core conserva soltanto ciò che appartiene realmente all’intero flusso:
- ordine e avanzamento delle fasi;
- ciclo di vita e resume delle sessioni;
- ledger e documenti di sessione esistenti;
- persistenza e locking comuni;
- processo Pi e trasporto backend/frontend;
- gate generici, sicurezza anti-bypass e finalizzazione;
- coordinamento esplicito dei moduli tramite import statici.
I moduli non si scoprono tramite registry e non comunicano tramite event bus. Le
dipendenze condivise vengono ricevute dal modulo oppure coordinate dal workflow core;
un modulo non importa direttamente l’implementazione di un altro.
## Architettura esplicitamente rifiutata
Questa iniziativa non deve introdurre:
- Kernel separato dall’attuale harness;
- manifest, registry, binding o composition resolver;
- negoziazione e versionamento di un contratto universale;
- adapter di esecuzione `in_process`, `process` o `job`;
- `WorkflowDefinition`, `WorkflowCommit`, nuovo event store o upcaster;
- artifact slot e revisioni universali;
- Review Port, Interaction Request/Result o Instruction Bundle generici;
- plugin dbt o automazioni ETL nella prima milestone.
## Fatti rilevanti sul codice attuale
- `harness/.pi/extensions/tht-gate.js` è il principale punto caldo condiviso, con circa
2.000 righe e logica relativa a più domini.
- `harness/.pi/skills/tht-sessione/SKILL.md` contiene circa 450 righe di istruzioni che
attraversano tutte le fasi.
- Evidence possiede già un port reale e tre adapter per filesystem, HTTP e S3.
- Memory possiede già codice dedicato, ma responsabilità e integrazione sono distribuite
tra modulo Python, CLI, gate e istruzioni Pi.
- Disambiguation oggi è soprattutto comportamento distribuito fra istruzioni, decisioni
e gate, non un package applicativo autonomo.
- `harness/workflow.yaml` è già la fonte condivisa dell’ordine e dei requisiti delle
fasi e deve rimanere tale.
L’analisi più ampia del problema corrente resta disponibile in
[`2026-08-21-modularizzazione-disambiguazione-evidence-memory-prd.md`](2026-08-21-modularizzazione-disambiguazione-evidence-memory-prd.md).
## Criterio di riuscita
Dopo il refactoring, una modifica interna a Disambiguation, Evidence o Memory deve
richiedere normalmente interventi nella directory e nei test del relativo modulo. Il
workflow core cambia soltanto quando cambia davvero il flusso condiviso.
La suite esistente, i test di caratterizzazione e i test end-to-end devono dimostrare
che il frontend e ogni comportamento osservabile sono rimasti invariati.
## Questioni da affidare a `grill-with-docs`
L’intervista deve concentrarsi soltanto sulle decisioni necessarie al refactoring:
1. ownership esatta dei file e delle responsabilità attuali;
2. façade minima e specifica di ciascun modulo;
3. collocazione delle istruzioni Pi e degli helper del gate relativi a ciascun dominio;
4. dipendenze condivise che devono essere ricevute dai moduli;
5. ordine di estrazione incrementale e test di caratterizzazione per ogni passaggio;
6. criteri oggettivi che impediscano al refactoring di introdurre cambiamenti funzionali.
Le funzioni future di Admission e Datamart/dbt sono fuori dal frontier dell’intervista:
vanno discusse soltanto dopo il completamento del refactoring conservativo.
@@ -0,0 +1,486 @@
# Refactoring modulare conservativo del workflow — Design
**Data:** 2026-08-23
**Stato:** approvato dal proprietario
**Ambito:** modularizzazione interna di Disambiguation, Evidence e Memory senza cambiamenti
funzionali
**Baseline del codice:** `b5db0cd3c12a4184a91933f6cf9d4ee13241ba65`
**Input autorevole:**
[`docs/prd/2026-08-23-refactoring-modulare-conservativo-brief.md`](../../prd/2026-08-23-refactoring-modulare-conservativo-brief.md)
## 1. Scopo
Questa milestone riorganizza il workflow ThothII in tre moduli interni profondi:
Disambiguation, Evidence e Memory. Una modifica alla semantica interna di uno di questi domini deve
richiedere normalmente interventi nella directory e nei test del modulo relativo. Il workflow core
cambia soltanto quando cambia una responsabilità realmente condivisa.
La milestone è un refactoring conservativo. Non aggiunge funzionalità, non cambia il flusso
operativo e non trasforma ThothII in una piattaforma di plugin. Pi resta il runtime concreto del
workflow assistito dal modello. Il repository, il rilascio e la proprietà del prodotto restano
unici.
## 2. Perimetro deciso
Sono inclusi:
- estrazione delle responsabilità Disambiguation oggi distribuite fra istruzioni Pi, gate,
decisioni e rendering della domanda;
- consolidamento di Memory, compresi recall F2, promozione F8 e indicizzazione delle domande
risolte;
- consolidamento di Evidence attraverso Python e TypeScript, dall'acquisizione alla citazione;
- separazione del gate monolitico fra meccanismi condivisi e use case specifici dei moduli;
- modularizzazione delle istruzioni Pi senza modificare il prompt effettivo;
- test di caratterizzazione e contract test che dimostrino equivalenza osservabile;
- un E2E live finale sull'intero workflow.
Sono esclusi:
- un modulo Admission, anche come pass-through;
- l'estrazione di Datamart o nuove funzioni dbt/ETL;
- il futuro ridisegno di F1, inclusi target group, classificazione unanswerable o nuovo retrieval;
- nuove funzionalità Evidence o Memory;
- un kernel separato, plugin, registry, manifest o resolver di composizione;
- un contratto universale fra moduli;
- `ModelRuntime`, event bus, nuovo event store, upcaster o artifact universali;
- modifiche al frontend o all'esperienza del reviewer;
- migrazioni dei dati o delle sessioni esistenti;
- correzioni opportunistiche di failure semantics, atomicità o altri comportamenti correnti;
- un piano esecutivo dettagliato, che sarà un documento successivo.
Admission e Datamart potranno essere progettati soltanto dopo il completamento e l'accettazione di
questa milestone.
## 3. Invarianti di compatibilità
Durante la milestone sono congelati:
- `harness/workflow.yaml`, ordine delle fasi e meccanismi di avanzamento;
- tassonomia, forma e significato delle decisioni del ledger;
- nomi, parametri, schemi TypeBox e risultati dei tool Pi;
- comandi `tht`, grammatica, JSON, stdout, stderr e codici di uscita;
- nomi, formato e contenuto deterministico dei documenti di sessione;
- widget descriptor, eventi SSE e loro ordine;
- prompt Pi effettivo e comportamento di caricamento delle reference;
- lifecycle e resume delle sessioni;
- lifecycle delle collection Qdrant e relativi payload condivisi;
- sequenza delle mutazioni e comportamento degli errori parziali;
- numero e ordine dei gate, dei turni Pi e delle operazioni esterne per fixture deterministiche.
Gli import Python, JavaScript e TypeScript usati soltanto dentro il repository non sono contratti
pubblici e possono cambiare. Qualunque modifica sostanziale a un elemento congelato interrompe il
refactoring: deve essere isolata, progettata e autorizzata come lavoro funzionale separato.
Non è sufficiente aggiornare un golden perché un test torni verde. Ogni differenza deve prima essere
trattata come possibile regressione.
## 4. Principi di modularità
### 4.1 Ownership
L'ownership indica dove vive una regola e quale modulo deve cambiare quando la regola cambia. Non
indica chi possiede il repository né quale implementazione scrive fisicamente un file.
Il modulo possiede:
- la semantica del proprio dominio;
- le validazioni specifiche;
- la presentazione specifica al reviewer;
- l'ordine delle mutazioni del proprio use case;
- le istruzioni Pi specifiche;
- i test attraverso la propria façade.
Il core possiede:
- ordine e avanzamento delle fasi;
- lifecycle, resume e finalizzazione delle sessioni;
- schema e persistenza del ledger;
- documenti e repository di sessione;
- locking e scritture atomiche condivise;
- processo Pi e trasporto backend/frontend;
- tool reviewer generici e widget transport;
- enforcement anti-bypass e protezione dei file;
- registry dei workspace e infrastruttura vector-store condivisa;
- composizione statica dei moduli.
Il modulo decide **cosa** mutare, **quando** e in quale ordine. Il core fornisce il meccanismo con cui
la mutazione viene eseguita.
### 4.2 Moduli profondi e asimmetrici
I moduli non devono avere la stessa forma né implementare una base comune. Ciascuno espone la più
piccola façade adatta ai propri use case. Un modulo esiste soltanto nei runtime in cui possiede
comportamento reale:
- Disambiguation è prevalentemente JavaScript e Pi;
- Memory attraversa Python, JavaScript e Pi;
- Evidence attraversa Python e TypeScript e fornisce dati al workflow Pi;
- non vengono creati package vuoti per ottenere simmetria visiva.
Non si introducono classi `*Facade`, DTO paralleli o un metodo universale `execute()`. Le interfacce
esterne già consumate dal workflow restano la façade pubblica; gli entrypoint di installazione sono
soltanto seam interni di composizione.
## 5. Architettura risultante
```text
workflow core
phase order · session · ledger · gate core
/ | \
/ | \
Disambiguation Memory Evidence
F1/F3 policy F2/F8 + solved acquire→cite
\ | /
\------ existing contracts ---/
ledger · artifacts · CLI/JSON
```
La composizione avviene tramite import statici. Non esistono discovery, registry o binding runtime.
Un modulo non importa l'implementazione di un altro modulo.
Le sole comunicazioni semantiche fra domini sono i contratti già esistenti:
- Disambiguation → Memory: record del ledger, in particolare `concept_clarified`;
- Evidence → Disambiguation/workflow: proiezioni di `tht search pack` e
`tht search find`;
- moduli → workflow: decisioni, artefatti e risultati CLI già definiti.
`DecisionType`, `workflow.yaml` e i repository restano di proprietà del core. Ogni collegamento fra
domini è protetto da un contract test e non da una chiamata diretta.
## 6. Modulo Disambiguation
Disambiguation possiede:
- istruzioni Pi di F1 e F3 e la tecnica descritta da `rewriting.md`;
- semantica di `concept_clarified`, `ambiguity_open`, `value_grounded` e
`question_rewritten`, senza possedere il registro globale dei tipi;
- validazione e normalizzazione dei payload Disambiguation inviati ai tool reviewer generici;
- presentazione delle interpretazioni e dei chiarimenti;
- tool specializzato `rewrite_question`;
- sequenza corrente `session set-question` → `question_rewritten` → chiusura F3;
- rendering del contenuto di `question.md`, incluse le assunzioni.
Il repository condiviso continua a effettuare la scrittura di `question.md`. Il core continua a
registrare `reviewer_select` e `reviewer_decide`; quando il payload appartiene a Disambiguation,
delega staticamente policy e normalizzazione al modulo.
Non viene introdotto un motore deterministico di disambiguazione, un nuovo comando CLI o un nuovo
tool Pi. Il modello continua a proporre le opzioni secondo le istruzioni correnti.
La façade interna del gate comprende soltanto:
- installazione di `rewrite_question` tramite un entrypoint come
`installDisambiguationGate(deps)`;
- policy richiamate staticamente dai tool reviewer generici.
## 7. Modulo Memory
Memory possiede:
- ricerca, selezione, dedup e applicazione delle memory in F2;
- definizione locale dei tipi riusabili, attualmente `concept_clarified`;
- calcolo deterministico dei candidati di promozione;
- presentazione, selezione e promozione in F8;
- tool `reviewer_memory_promote` e relativi builder/normalizer;
- salvataggio mirato nel vector store e decisioni `memory_promoted` /
`memory_promotion_declined`;
- indicizzazione best-effort delle domande risolte durante finalize;
- comportamento e istruzioni Pi Memory.
La CLI `tht memory ...` conserva integralmente la propria interfaccia e diventa un adapter sottile
verso il modulo. Il core continua a possedere repository, ledger, fase, finalizzazione e vector-store
generico.
La façade esterna resta composta dai comandi `tht memory ...`, dal tool
`reviewer_memory_promote` e dall'hook solved invocato durante finalize. L'entrypoint JavaScript
interno è del tipo `installMemoryGate(deps)`; non costituisce un protocollo condiviso con gli altri
moduli.
## 8. Modulo Evidence
Evidence possiede:
- port e adapter filesystem, HTTP e S3;
- acquisizione e materializzazione;
- modelli canonici, normalizzazione, chunking, staging e publication del corpus;
- ricerca Evidence e formula store;
- contratti Evidence-specific dei comandi preprocessing e search;
- risoluzione degli ID citabili;
- proiezione `{id, file, esito, decision_seq}` usata nei documenti di sessione;
- orchestrazione TypeScript specifica di Evidence oggi dispersa nel sottosistema workspace.
Il core passa decisioni e schema linking al projector Evidence e scrive il risultato tramite il
repository di sessione. Schema linking, sintesi e relativi widget restano nel workflow core.
Restano condivisi:
- registry e configurazione dei workspace;
- orchestrazione generale del preprocessing;
- lifecycle e indici obbligatori della collection Qdrant;
- vector store, embedder e configurazione infrastrutturale;
- persistenza delle sessioni.
La façade inter-language resta il contratto CLI/JSON esistente. Non viene introdotta una nuova
interfaccia generale Python/TypeScript.
## 9. Mappa fisica
La struttura obiettivo è:
```text
harness/.pi/extensions/gate/
core/ # hook, enforcement e meccanismi generici
disambiguation/ # policy F1/F3, rewrite tool, test
memory/ # F2/F8, promotion tool, test
harness/.pi/skills/tht-sessione/modules/
disambiguation/
memory/
harness/tht/
memory/ # assorbe memory.py e solved.py
evidence/ # assorbe port, adapter, corpus e search/evidence
harness/tests/
disambiguation/
memory/
evidence/
backend/src/workspaces/evidence/
materialization.ts
preprocessing.ts
backend/test/workspaces/evidence/
```
`harness/.pi/extensions/tht-gate.js`, `harness/.pi/skills/tht-sessione/SKILL.md`, le CLI e
`backend/src/workspaces/preprocessing-service.ts` restano entrypoint stabili. Delegano ai moduli
tramite import espliciti.
Più precisamente:
- `harness/tht/memory.py` e `harness/tht/solved.py` confluiscono nel package
`harness/tht/memory/`;
- `harness/tht/corpus/`, `harness/tht/search/evidence.py`, il port Evidence e gli adapter
Evidence confluiscono sotto `harness/tht/evidence/`;
- le funzionalità non Evidence di `harness/tht/search/` restano condivise;
- `backend/src/workspaces/evidence-materialization.ts` confluisce nel modulo Evidence;
- da `preprocessing-service.ts` viene estratta soltanto l'orchestrazione specifica Evidence;
- `qdrant-collection.ts`, registry, stato e coordinamento generale restano nel package workspace;
- i test di dominio seguono il modulo; il comando pubblico `npm test` del harness continua a
eseguire l'intera suite JavaScript anche dopo l'aggiornamento della discovery interna.
Non viene creata una directory Evidence nel gate finché non esiste comportamento JavaScript
specifico da collocarvi.
## 10. Istruzioni Pi
Le istruzioni dei moduli diventano frammenti sorgente sotto
`harness/.pi/skills/tht-sessione/modules/`. `SKILL.md` rimane nel percorso attuale come proiezione
generata, deterministica e committata.
Il generatore:
- usa una lista statica e ordinata di frammenti;
- non effettua discovery;
- produce output ripetibile;
- viene verificato da un test che fallisce se `SKILL.md` non coincide con i frammenti;
- nella prima estrazione deve riprodurre byte per byte il `SKILL.md` della baseline.
Il gate continua a leggere e iniettare `SKILL.md` come oggi. I reference document che il modello
legge durante le fasi, inclusi `rewriting.md`, `cte.md` e `sql-generation.md`, mantengono in questa
milestone percorso e comportamento correnti. Non viene introdotta composizione dinamica al runtime.
## 11. Capability e sicurezza
Ogni modulo riceve un oggetto di capability specifico, costruito dal core. Non esiste
`ModuleContext` universale.
Le capability JavaScript ammissibili, assegnate nel sottoinsieme minimo necessario, sono:
- esecuzione controllata della CLI;
- emissione e attesa di una UI request;
- registrazione di una decisione;
- lettura della fase corrente;
- avanzamento o chiusura della fase;
- notifica al reviewer.
Le dipendenze Python ammissibili sono repository di sessione, port Evidence, vector store,
embedder e clock, soltanto per i moduli che li usano. Le dipendenze TypeScript Evidence comprendono
accesso controllato a filesystem, process runner e stato del preprocessing.
Un modulo non crea direttamente factory globali, non legge configurazione globale e non accede
all'implementazione di un altro modulo. Somiglianze fra due oggetti di capability non giustificano
la loro unificazione.
Restano esclusivamente nel gate core:
- hook anti-bypass;
- input lock e steering del testo libero;
- kickoff di nuova sessione e resume;
- protezione dei file e del codice del gate;
- `agent_end` safety;
- tool reviewer generici;
- enforcement dei codici di uscita e delle regole condivise.
I moduli non registrano hook globali e non ricevono accesso diretto a filesystem o ledger. Tutte le
mutazioni passano dalle capability controllate.
## 12. Failure semantics
Il refactoring conserva anche gli stati intermedi oggi osservabili, non soltanto gli happy path.
Esempi vincolanti:
- Memory continua a salvare nel vector store prima di registrare `memory_promoted`; se la seconda
operazione fallisce, conserva le attuali istruzioni di recupero;
- `rewrite_question` continua a scrivere la domanda, poi il ledger e infine ad avanzare F3;
- codici di uscita 5 e 6 conservano il significato corrente per il gate;
- retry, resume e finalizzazione continuano a reagire agli errori nello stesso modo;
- materializzazione Evidence e checkpoint del preprocessing conservano gli attuali stati di
successo, attesa e fallimento.
Migliorare atomicità, retry o messaggi richiede un ticket funzionale successivo.
## 13. Sequenza di estrazione
L'ordine obbligatorio delle slice è:
1. aggiungere i test di caratterizzazione mancanti senza spostare codice;
2. estrarre Memory e introdurre il pattern di capability specifiche;
3. estrarre Disambiguation e la composizione deterministica delle istruzioni;
4. consolidare Evidence lato Python;
5. consolidare Evidence lato TypeScript;
6. ripulire il core, rimuovere shim e verificare l'assenza di duplicazioni.
Ogni slice deve essere un commit verificabile e revertibile. Gli shim di re-export sono ammessi fra
due commit per mantenere il repository verde, ma devono sparire entro la milestone, salvo che
proteggano un import dimostrabilmente consumato fuori dal repository.
Non si eseguono le tre estrazioni in parallelo: Memory stabilisce il pattern, Disambiguation lo
applica al gate e alle istruzioni, Evidence chiude il lavoro più ampio e cross-runtime.
## 14. Strategia di caratterizzazione
### 14.1 Catalogo dei contratti
Prima della prima estrazione viene committato un catalogo leggibile dei contratti congelati. Ogni
voce collega:
- comportamento protetto;
- owner corrente e owner risultante;
- fixture o golden;
- test che lo verifica;
- tipo di uguaglianza richiesto.
Si usano fixture e golden leggibili per payload strutturati. Gli hash sono riservati a contenuti
testuali grandi, come il prompt Pi. Non viene introdotto un unico manifest opaco di hash.
### 14.2 Matrice minima delle sessioni
La matrice di caratterizzazione comprende:
- F1 con chiarimento approvato;
- F1 con ambiguità lasciata aperta;
- F2 con memory applicata;
- F2 con memory rifiutata;
- F2 senza memory;
- F3 con domanda riscritta e assunzioni;
- Evidence usata nello schema linking;
- Evidence esplicitamente accettata e scartata;
- sessione legacy senza corpus canonico;
- F8 con candidati promossi;
- F8 con candidati rifiutati;
- F8 senza candidati;
- resume nelle fasi interessate;
- repository filesystem e PostgreSQL dove la semantica differisce;
- failure path e recuperi elencati nella sezione 12.
Per ogni fixture devono restare uguali documenti, ledger, stato, tool, payload, sequenza CLI ed
eventi osservabili.
### 14.3 Gate per ogni slice
Ogni estrazione richiede:
- uguaglianza esatta dei payload deterministici;
- stessi tool registrati e stessi schemi TypeBox;
- stessa sequenza CLI e stesso comportamento d'errore;
- stessi artefatti e ledger per fixture equivalenti;
- test della façade con capability fake;
- suite completa del layer interessato;
- nessun aggiornamento non spiegato dei golden;
- nessun nuovo gate, turno Pi o I/O esterno.
I vecchi test di helper possono essere eliminati soltanto quando la stessa proprietà è coperta
attraverso la nuova façade. I test non devono restare permanentemente duplicati su entrambi i lati
del seam.
### 14.4 Gate finale
La milestone richiede:
- suite completa Python del harness;
- suite JavaScript completa del gate;
- suite, typecheck e build di backend e frontend;
- test di caratterizzazione e contract test completi;
- verifica delle sessioni legacy;
- `git diff --check`;
- un E2E live completo dalla nuova domanda alla finalizzazione;
- nel live E2E, almeno un percorso Evidence, un percorso Memory e una ripresa di sessione.
Il live E2E è eseguito una volta alla fine, non dopo ogni slice. Se ambiente, DWH o credenziali non
sono disponibili, l'automazione può risultare verde ma la milestone resta con gate manuale
pendente e non è dichiarata completa.
## 15. Budget operativo
Per una fixture deterministica, prima e dopo il refactoring devono coincidere:
- numero e ordine dei widget reviewer;
- numero e ordine dei comandi `tht`;
- numero di turni Pi necessari;
- query a DWH, Qdrant ed Evidence;
- avanzamenti di fase;
- retry e percorso di resume.
Sono ammesse soltanto differenze trascurabili di import e startup locale. Non è ammesso nuovo I/O
esterno. Il fake runtime deve registrare comandi e interazioni in modo che il budget sia verificato
automaticamente.
## 16. Criteri di accettazione
Il refactoring è accettato soltanto quando:
1. esistono moduli profondi Disambiguation, Memory ed Evidence secondo questa ownership;
2. non esistono moduli Admission o Datamart introdotti dalla milestone;
3. `tht-gate.js` contiene solo composizione e responsabilità realmente condivise;
4. una modifica interna tipica richiede interventi nel modulo e nei suoi test, non nel core;
5. nessun modulo importa l'implementazione di un altro;
6. non esiste un contratto universale, registry o discovery di moduli;
7. `SKILL.md` è generato dai frammenti e inizialmente byte-identico alla baseline;
8. tutti i contratti congelati e il budget operativo risultano invariati;
9. sessioni esistenti, legacy e riprese producono gli stessi risultati;
10. failure semantics e recuperi correnti sono preservati;
11. tutti i gate automatizzati e il gate live finale sono verdi;
12. non restano shim temporanei, codice duplicato, cartelle vuote o fonti istruzionali divergenti;
13. frontend, SSE, widget, CLI e formati persistiti non hanno richiesto modifiche funzionali.
## 17. Lavoro successivo
Dopo l'accettazione potranno essere progettati separatamente:
- Admission e la policy dei tre tentativi;
- evoluzione funzionale di Disambiguation;
- nuovo contratto di consumo Evidence per una F1 ridisegnata;
- evoluzione del contratto Disambiguation → Memory;
- Datamart intelligente, dbt o automazioni ETL;
- miglioramenti di atomicità e recovery emersi durante la caratterizzazione.
Nessuno di questi elementi può essere anticipato nel refactoring sotto forma di scaffold,
astrazione o comportamento dormiente.