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
@@ -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.