docs: publish MkDocs site on gh-pages

This commit is contained in:
2026-07-23 19:28:09 +02:00
parent 724c446087
commit 17bca2b2cb
3 changed files with 294 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
name: Publish documentation
on:
push:
branches:
- gh-pages
paths:
- "docs/**"
- "mkdocs.yml"
- ".github/workflows/docs-pages.yml"
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: true
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
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
# The repository intentionally links some docs to source files outside
# docs/. MkDocs reports those as warnings, but they must not block Pages.
run: mkdocs build
- name: Configure GitHub Pages
uses: actions/configure-pages@v5
- name: Upload Pages artifact
uses: actions/upload-pages-artifact@v3
with:
path: site
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
+235
View File
@@ -0,0 +1,235 @@
# Gestione delle memory
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.
## Sintesi architetturale
Una memory è conoscenza di dominio riutilizzabile tra domande. Non è una copia dello schema-linking di una singola domanda.
```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 pgvector
│
▼
F2 di una sessione futura
ricerca e proposta al reviewer
```
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.
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).
## I tre livelli della gestione
| Livello | Contenuto | Funzione |
| --- | --- | --- |
| 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 pgvector | Embedding e metadati derivati dal registro | Ricerca semantica |
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.
## Che cosa può diventare una memory
Durante F1 il workflow registra i chiarimenti come decisioni `concept_clarified`. Un chiarimento può esprimere:
- 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.
Il modello `MemoryRecord` contiene:
- `id`, ad esempio `mem-0001`;
- timestamp, sessione e sequenza della decisione originale;
- `type`;
- `subject`;
- `detail`;
- `rationale`;
- `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.
Esempio valido:
> Per ablazione si intende una procedura con `ablazione_transcatetere = TRUE`, conteggiata con `COUNT(DISTINCT cod_paz)` per anno.
Esempi non validi:
- `fact_cardioversione` come memory approvata;
- `dim_time` come memory rifiutata;
- una decisione “includi questa tabella” salvata per domande future.
La regola è documentata anche nella skill del workflow, in [harness/.pi/skills/tht-sessione/SKILL.md](../harness/.pi/skills/tht-sessione/SKILL.md:217).
## Promozione alla fine della sessione: F8
Alla fine del workflow il gate `reviewer_memory_promote` esegue una preview deterministica:
```text
tht memory promote --session <id> --preview --json
```
La 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.
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).
Il reviewer vede un'unica checklist, preselezionata. Per ogni candidato:
- 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.
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).
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.
## Persistenza globale
### Registro JSONL
Il registro attuale è:
```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.
### pgvector
Dopo la promozione, `save-one` costruisce un solo `VectorRecord` e lo invia all'indice pgvector. Il testo indicizzato include:
- tipo e soggetto;
- dettaglio;
- motivazione;
- domanda di contesto;
- eventuali concetti e mapping.
Il record vettoriale usa l'id `memory:mem-XXXX`, mentre i metadati conservano `subject`, `detail`, `rationale`, `tables` e `concepts`. L'hash SHA-256 del contenuto impedisce di ricalcolare embedding e upsert quando il testo non è cambiato.
Il comportamento è implementato in [harness/tht/memory.py](../harness/tht/memory.py:253) e [harness/tht/memory.py](../harness/tht/memory.py:305).
### Fonte canonica attuale
Oggi il registro JSONL è ancora la fonte canonica applicativa e pgvector è l'indice derivato. Il commento iniziale di [memory_cmd.py](../harness/tht/cli/memory_cmd.py:1) segnala un debito tecnico: l'architettura futura prevista sarebbe usare direttamente il vector DB come archivio unico, ma questa migrazione non è ancora completata.
## Riutilizzo in F2
In una sessione futura F2 esegue:
```text
tht memory search "<domanda>" --session <id> --json
```
Il comando:
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à.
L'implementazione è in [harness/tht/cli/memory_cmd.py](../harness/tht/cli/memory_cmd.py:368).
Le memory non vengono mai applicate automaticamente. Il modello deve presentarle in un'unica scelta `reviewer_decide`:
- 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”.
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.
## Ledger effettivo, rollback e riaperture
Il ledger è append-only. Riaperture e ritrattazioni non cancellano le righe precedenti; cambiano però quali decisioni sono effettive.
Gli helper memory usano `effective_decisions()` per:
- escludere decisioni ritirate;
- ignorare decisioni appartenenti a fasi diventate stale dopo un rollback;
- impedire la promozione di chiarimenti non più validi.
La vista effettiva è definita in [harness/tht/phase.py](../harness/tht/phase.py:82).
## Visualizzazione nel riepilogo della sessione
La sezione “Memories” del riepilogo viene proiettata a runtime dal ledger della sessione; non è una copia diretta del registro globale.
La proiezione:
- 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.
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.
La trasformazione avviene in lettura: anche le sessioni storiche vengono organizzate con il layout e i filtri correnti senza riscrivere gli artefatti originali.
## Invarianti applicate
Le protezioni sono distribuite su più confini:
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.
Questo evita che una singola modifica al prompt o a un solo componente reintroduca le tabelle come memory.
## Limiti e rischi residui
### Registro e indice non sono una singola transazione
Il salvataggio segue sostanzialmente questa sequenza:
```text
registro JSONL → pgvector → marker memory_promoted nel ledger
```
Se pgvector non è disponibile, il registro può contenere una memory non ancora ricercabile; il comando segnala che sarà necessario reindicizzare.
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.
### Limite di cinque candidati
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.
### Deduplica non globale
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.
### Vecchi record fisici
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.
## Valutazione finale
La gestione attuale è coerente con il requisito funzionale: una memory è una conoscenza concettuale riutilizzabile, non una scelta di schema-linking.
La parte più solida è la difesa multilivello del tipo `concept_clarified`. Il principale debito tecnico riguarda invece la convivenza del registro JSONL con pgvector e l'assenza di una transazione unica tra archivio globale, indice semantico e ledger della sessione.
+1
View File
@@ -48,6 +48,7 @@ nav:
- ThothII (Documentazione Tecnica):
- Panoramica Architettura: architecture/overview.md
- Installazione Docker (4 contesti): installazione-docker-4-contesti.md
- Gestione delle memory: gestione-memory.md
- Specifiche di Design:
- Architettura ThothII: superpowers/specs/2026-06-25-thothii-architecture-design.md
- Backend: superpowers/specs/2026-06-27-backend-design.md