ThothII reviewer replay
Riproduce su http://localhost:5333 la vera UI dei widget reviewer di una
sessione registrata, senza VPN, Pi, Python, né DWH. Si usano i descriptor
dei gate così come furono proposti al revisore (catturati nel transcript Pi),
nello stesso ordine, con le scelte realmente fatte mostrate come badge di
confronto.
Schema del flusso
browser (SPA reale, :5333)
│ REST + SSE, same-origin
▼
server.mjs (node:http, zero dipendenze)
├─ static: serve il bundle SPA da tools/replay/web/
└─ replay SSE: GET /sessions/:id/events → event: ui_request (gate N)
POST /sessions/:id/response → badge "scelta reale" + gate N+1
│
legge
▼
replay.json (fixture estratta dal transcript)
│ prodotta da
▼
extract.mjs (legge ~/.pi/agent/sessions/…/*.jsonl + question.md)
Niente backend Fastify, niente pi --mode rpc, niente tht, niente DB: il
server gira direttamente sui descriptor registrati.
Comandi
# Estrai la fixture per una sessione (id, file, o directory di transcript):
scripts/replay.sh extract 2026-07-03-132933-fammi-la-lista-dei-pazienti-che-negli-ul
scripts/replay.sh extract ~/.pi/agent/sessions/--Users-mp-projects-ThothII-harness--/
# (una tantum, o dopo modifiche al frontend) costruisci il bundle SPA:
scripts/replay.sh build
# Avvia il server su :5333:
scripts/replay.sh run
# Scorciatoia: extract (default) + build (se manca) + run:
scripts/replay.sh
Cosa può estrarre extract:
| Input | Comportamento |
|---|---|
| (nessuno) | La sessione di default (3 lug 17:07, 20 gate). |
<session-id> |
Scansiona tutti i transcript nella directory Pi per quel session-id. Gestisce automaticamente sessioni spezzate su più file (resume): raccoglie i gate da tutti i transcript, li ordina cronologicamente, e scarta i gate senza risposta (quelli riproposti al resume). |
<file.jsonl> |
Un singolo transcript. |
<directory> |
Tutti i *.jsonl nella directory; se contengono sessioni diverse, tiene quella dominante (con più gate). |
La domanda della sessione è letta da session_manifest.yaml (campo question),
cercando la directory sessions in ogni workspace di harness/workspaces/ (via
paths.sessions, assoluto o relativo). Niente più path hardcoded.
Cosa vedi nel browser
- Apri
http://localhost:5333. La lista sessioni mostra una sessione (quella riprodotta) con la domanda reale. - Cliccala: appare il gate 1 — un
reviewer_select(widget select) con il titolo, l'intro e le opzioni esatte che il modello propose il 3 luglio, incluso il badge "consigliato" sull'opzione raccomandata. - Clicca un'opzione. Sotto la tua scelta compare un
infocon la scelta reale del 3 luglio (📌 SCELTA REALE (3 lug 2026): …), e — se coincide — un✓ Hai scelto come il 3 luglio. - Avanzi attraverso tutti i gate nell'ordine loggato:
select(F1 chiarimenti, F6 conferma piano),multiselect(F3 riscrittura, F4 schema linking, F6 piano CTE),artifact-gate(F2/F3/F4/F5 phase advance, F6 approvazione CTE con SQL visibile nell'ArtifactView). - Dopo l'ultimo gate:
✓ Replay completato — 20/20 gate.
I 20 gate della sessione 17:07: 4 select + 11 confirm + 5 decide.
Limiti e fedeltà al log
- Sessioni multi-file (resume): gestite. L'estrattore raccoglie i gate da
tutti i transcript della sessione, li ordina per timestamp del messaggio, e
scarta i gate senza
toolResult(quelli dove il revisore chiuse Pi senza rispondere e che furono riproposti al resume). Risultato: la sequenza esatta dei gate che il revisore ha effettivamente visto e deciso. - Workspace discovery: la domanda è letta da
session_manifest.yaml, cercandopaths.sessionsnei workspace diharness/workspaces/. Funziona per qualunque cliente (non solopsd), purché il workspace yaml sia presente epaths.sessionssia un path assoluto o relativo (non espanso con${VAR}— in quel caso il path non è risolvibile offline e la domanda resta vuota). - Phase compute disattivata: la fase corrente (colorazione
F1…F8) non è ricalcolata dal ledger (richiederebbetht phase); i gate usano un phase-tag neutroreplay. L'ordine e i descriptor sono quelli reali. - Gate falliti/saltati: alcuni gate non hanno una "scelta reale" perché
nella sessione live furono saltati (memorie vuote → avanzamento auto) o
falliti (es.
cte_plancon tipo invalido). Il badge⚠ Gate fallito nella sessione realelo segnala fedelmente — riflette il log, non un bug del replay. text_delta/ragionamenti del modello: omessi (scelta "solo gate"). Per vederli, estendiextract.mjsper raccogliere anche itext_delta.- Reserved controls (
back/exit/other): nel replay sono ammessi come qualunque altra risposta, ma non hanno semantica (non c'è stato di navigazione da ripristinare); trattali come "procedi". - Single-user, single-session: il server tiene un solo cursore globale; non è pensato per più client concorrenti (un refresh del browser riprende dal gate corrente).
File
extract.mjs— estrattore fixture dal transcript Pi JSONL.augment-schema-linking.mjs— riscrive il gate F4 tabelle/colonne come descrittoreschema-linking(vedischema-linking-fixture.json).augment-review-gates.mjs— riscrive i gate F6 piano CTE, CTE 1 approvato e Fase 5 completata come descrittori v2 (cte_plan/cte_result/phase, vedicte-plan-fixture.json,cte-result-fixture.json,phase-summary-fixture.json). Nota: questi tre gate sono ancora fixture sintetiche coerenti coi contratti v2 — dopo la prima sessione live che passa per un gatecte_plan/cte_result/phasereale, rilanciaextract.mjsper catturare i descrittori v2 reali e sostituire le fixture.server.mjs— server HTTP+SSE standalone (node:http, zero deps).replay.json— fixture generata (committata per comodità; rigenerata conaugment-schema-linking.mjspoiaugment-review-gates.mjs, in quest'ordine).web/— bundle SPA costruito dafrontend/conVITE_BACKEND_URL=http://localhost:5333(gitignorato; prodotto dabuild).