Files
ThothII/tools/replay/README.md
T
marcopan c3a3cb8da5 feat(replay): standalone reviewer-gate replay server on :5333
Reproduces the real reviewer UI for any recorded session, with no VPN/Pi/
Python/DWH. The server (node:http, zero deps) serves the built SPA and a
tiny SSE/REST shim that re-emits the reviewer gates captured in a Pi
transcript, in their original order, with the reviewer's real 3-Jul
choices shown as comparison badges.

- tools/replay/extract.mjs: extracts gates from one or many transcripts
  (session-id, file, or directory). Handles sessions split across resume
  re-entries by sorting on message timestamp and dropping unanswered
  gates. Reads the question from session_manifest.yaml, resolving the
  sessions dir from any workspace yaml (no hardcoded paths).
- tools/replay/server.mjs: same-origin :5333. SSE streams gates; POST
  /response advances the cursor and pushes info badges (scelta reale).
  POST /resume and the final "Ripeti/Esci" widget close the SSE so the
  browser EventSource reconnects (cursor resets, gate 1 re-emitted) — the
  replay is re-runnable any number of times. GET /sessions/:id/documents
  reads the real session files so GateArtifactBody resolves file-reference
  artifacts. Exit emits system_event {event:"session_exit"} to return to
  the landing.
- scripts/replay.sh: launcher (extract / build / run / all).
- tools/replay/README.md: data flow, commands, fidelity notes.
- .gitignore: ignore tools/replay/web/ (built artifact, like dist/).
2026-07-05 18:24:26 +02:00

5.4 KiB

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

  1. Apri http://localhost:5333. La lista sessioni mostra una sessione (quella riprodotta) con la domanda reale.
  2. 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.
  3. Clicca un'opzione. Sotto la tua scelta compare un info con la scelta reale del 3 luglio (📌 SCELTA REALE (3 lug 2026): …), e — se coincide — un ✓ Hai scelto come il 3 luglio.
  4. 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).
  5. 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, cercando paths.sessions nei workspace di harness/workspaces/. Funziona per qualunque cliente (non solo psd), purché il workspace yaml sia presente e paths.sessions sia 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 (richiederebbe tht phase); i gate usano un phase-tag neutro replay. 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_plan con tipo invalido). Il badge ⚠ Gate fallito nella sessione reale lo segnala fedelmente — riflette il log, non un bug del replay.
  • text_delta/ragionamenti del modello: omessi (scelta "solo gate"). Per vederli, estendi extract.mjs per raccogliere anche i text_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.
  • server.mjs — server HTTP+SSE standalone (node:http, zero deps).
  • replay.json — fixture generata (committata per comodità).
  • web/ — bundle SPA costruito da frontend/ con VITE_BACKEND_URL=http://localhost:5333 (gitignorato; prodotto da build).