docs(spec): frame cold-start resume correctness as a prerequisite

Add the SKILL.md design contract (persisted state is the truth) and a
dedicated Resume correctness section: backend new/resume prompt mode,
missing cold-start procedure in the skill, end-to-end verification gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-29 11:39:56 +02:00
co-authored by Claude Opus 4.8
parent 916075329d
commit 4364bdd4a1
@@ -25,6 +25,14 @@ directory; advancing to the next phase does **not** require replaying the prior
conversation. Therefore **the phase documents ARE the persistence** — we do **not**
need to store the verbatim chat.
This is the explicit design contract of the orchestrator
([SKILL.md](../../../harness/.pi/skills/tht-sessione/SKILL.md)): *"the persisted state
(ledger `review_decisions.jsonl` + artifacts) is the truth — what is not recorded did not
happen."* The documents are not meant to *contain* the chat; they are meant to make the
chat **reconstructable / unnecessary** — the documents are the *state*, the skill is the
*procedure*, and together they let a fresh model continue the task. Whether that holds for
a cold-start resume is a **verifiable prerequisite**, treated explicitly below.
This was the crux decision in brainstorming. It means:
- The "vista divisa" is a **read-only document viewer** driven by what's on disk, not a
@@ -136,15 +144,10 @@ new `ThtRunner` method that shells the matching `tht` subcommand:
Before spawning, read the manifest (`tht.sessionShow`); if `status == "finalized"` or
`archived`, return `409` with a clear message and do **not** spawn Pi.
**Resume must rebuild phase context (gap to fix).** Today `PiProcessManager.spawnFor`
unconditionally sends `/nuova-domanda "kickoff"`
([pi-process-manager.ts](../../../backend/src/pi/pi-process-manager.ts)), and `resume`
calls the same path — so a "resume" actually kicks off a *new* question instead of
re-entering the workflow. `spawnFor` must take a **mode**: new sessions send
`/nuova-domanda`, resumed sessions send `/riprendi-sessione <id>` (the existing prompt
[riprendi-sessione.md](../../../harness/.pi/prompts/riprendi-sessione.md)) so Pi rebuilds
state and lands on the last incomplete phase. Without this, the resume model does not
actually work.
**Resume must rebuild phase context.** This is the backend half of the resume-correctness
prerequisite: `spawnFor` gains a `new`/`resume` mode (new → `/nuova-domanda`, resume →
`/riprendi-sessione <id>`). See the dedicated **Resume correctness** section below for the
full picture (backend prompt, skill cold-start procedure, end-to-end verification).
## Frontend layer (React + base-ui + Tailwind + React Query)
@@ -202,6 +205,40 @@ actually work.
- **Rail organization:** collapsible group headers + "Senza gruppo"; "Archivio" as a
separate section, never mixed into the active list.
## Resume correctness — verifiable prerequisite
The whole "no chat persistence" premise rests on a fresh Pi process being able to
**re-enter the workflow at the last incomplete phase using only the persisted state**.
That is the design contract quoted above (ledger + artifacts are the truth; each phase's
prerequisites are prior artifacts; rollback `/torna N`, discipline 11, already resumes
"reviewing the existing artifacts" without replaying chat). The premise is sound — but the
**cold-start resume path** (new process, zero conversation) is today thin and unproven.
This feature treats resume correctness as a prerequisite and must close three gaps:
1. **Backend sends the wrong prompt.** `PiProcessManager.spawnFor`
([pi-process-manager.ts](../../../backend/src/pi/pi-process-manager.ts)) always sends
`/nuova-domanda "kickoff"`, and `resume` calls the same path — so a "resume" kicks off a
*new* question. `spawnFor` must take a `new`/`resume` mode: new → `/nuova-domanda`,
resume → `/riprendi-sessione <id>` (the prompt
[riprendi-sessione.md](../../../harness/.pi/prompts/riprendi-sessione.md)).
2. **No cold-start procedure in the skill.** The phases assume "you must already be in
Phase 1"; there is no "Phase 0 / Resume" step telling a fresh process to read
`tht session show <id>`, take the current phase N, load that phase's artifacts, and
resume its procedure. Add a short **Resume** section to
[SKILL.md](../../../harness/.pi/skills/tht-sessione/SKILL.md) (or confirm the model
reliably bootstraps from `tht session show` + artifact reads). The one-line resume
prompt is not sufficient on its own — intra-session rollback works only because the
conversation is still live; cold start has nothing to lean on.
3. **End-to-end verification.** A test that creates a session, drives it partway (e.g. into
F4), tears down the Pi process, resumes in a fresh process, and asserts it lands on the
**correct phase** with the prior artifacts available and presents the next gate — not a
new-question kickoff.
Until (1)–(3) pass, the phase-document model is a design assumption, not a proven fact.
The read panel, rename, group, archive, and delete work regardless of resume; but the
**"Riprendi"** action is only trustworthy once resume correctness is verified, so it must
not ship as "working" before this gate is green.
## Testing approach (TDD per repo convention)
- **Harness (pytest):** new `store.py` helpers (set-name/group, archive/unarchive,
@@ -214,6 +251,10 @@ actually work.
invalidate the query; grouping/archived filtering renders correctly; the panel renders
documents via the right viewers and shows/hides "Riprendi" by resumability;
delete-confirm flow.
- **Resume correctness (end-to-end):** the verification described in the *Resume
correctness* section — partway session → teardown → fresh-process resume → lands on the
correct phase with prior artifacts, not a new-question kickoff. This gate must be green
before "Riprendi" is considered working.
## Out of scope / deferred (with triggers)