CtrlK
BlogDocsLog inGet started
Tessl Logo

spec-driven-development/spec-as-source

Spec-driven development on OpenSpec, with mechanical spec-as-source enforcement: a custom 'spec-as-source' OpenSpec schema adds file-ownership (targets) and test-verification ([@test]) metadata to every capability spec, three scripts (link check, ownership check, manifest build) keep code and specs from drifting apart, plus requirement-gathering, spec-writer, work-review, and a session-handoff skill with a proactive context-warning hook.

68

Quality

85%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Low

Low-risk findings worth noting

Overview
Quality
Evals
Security
Files

handoff-template.mdskills/handoff/references/

HANDOFF-NNN.md — Template Completo

# HANDOFF-[NNN] — [Brand/Progetto]
> Data: [YYYY-MM-DD HH:MM] | Sessione: #[N] | Continua da: HANDOFF-[N-1].md (o —)
> Progetto: [nome] | Operatore: [persona + modello AI es. "Giuseppe + Claude Sonnet 4.6"]
> Cliente: [se applicabile] | Deadline: [se applicabile]

---

## 🎯 Goal
[Se il progetto ha `openspec/PLAN.md`: riporta VERBATIM la riga `goal:` del piano e cita il file
come sorgente — es. "(fonte: openspec/PLAN.md)". Non riscriverlo con parole tue: due obiettivi
formulati in modo indipendente divergono, e la divergenza non si vede.
Altrimenti: obiettivo finale, 2-4 righe. Cosa si sta costruendo/creando, per chi, con quale
deliverable atteso. Questa sezione cambia raramente — solo se lo scope del progetto cambia davvero.]

## 📍 Posizione nel piano
[SOLO se il progetto ha `openspec/PLAN.md`. Se non ce l'ha, ometti l'intera sezione.
Si compila LEGGENDO il file, non a memoria: apri `openspec/PLAN.md` e riporta gli stati reali.

- **Voce corrente**: E<NN> — <titolo> (stato: <draft|approved|in-progress|done|dropped>)
- **Cosa stiamo facendo per il goal**: 1-2 righe — in che modo questa voce avvicina il `done-when` del piano
- **Resta da fare**: E<NN> (stato), E<NN> (stato) — solo le voci non ancora `done` né `dropped`
- **Piano**: `openspec/PLAN.md` — gate: `bash scripts/check-plan-gate.sh`

Se una voce legata a un change già sotto `openspec/changes/archive/` non è `done`,
segnalalo qui: è la stessa staleness che riporta il gate.]

## 🔄 Stato volatile
[Sezione OBBLIGATORIA. Qui NON si scrivono valori: si scrivono **i comandi che li
producono**. Conteggi, stati, numeri di commit, task aperti, esiti dei test e del gate
invecchiano fra il momento in cui scrivi e il momento in cui qualcuno legge — spesso
prima: "16 commit locali" era già sbagliato quando è stato scritto, perché il commit
di questo handoff non esisteva ancora. Chi legge deve **rigenerarli, non ereditarli**.

Regola che generalizza il blocco qui sotto: **ogni conteggio citato altrove in questo
documento ha qui il comando che lo rigenera.** Se un numero non ha il suo comando,
o il comando manca o il numero non andava scritto.

~~~bash
git log --oneline origin/main..HEAD | wc -l        # commit non pushati
bash scripts/verify.sh | tail -2                   # test verdi/rossi
bash scripts/check-plan-gate.sh                    # stato del gate
grep -c '^- \[ \]' openspec/changes/*/tasks.md     # task aperti per change
grep -E '^### E[0-9]+|^- \*\*State\*\*:' openspec/PLAN.md | paste - -   # stati delle voci
~~~

Adatta i comandi al progetto: questo blocco è un esempio funzionante, non una lista fissa.
Un comando che in questo progetto non esiste si toglie, non si lascia per inerzia.

Eccezione, unica: se un valore non è rigenerabile da nessun comando, scrivilo datato —
`3 render approvati dal cliente (al 2026-08-04 17:16 — riverificare)`. È un ripiego,
non la forma normale.]

## ✅ Current Progress

### Completato ✓
- [x] [Task specifico con riferimento a file/asset — es. "Homepage hero generata, salvata in /assets/hero-v3.png"]
- [x] [Task specifico]

### In corso / Sospeso
- [ ] [Task — a che punto è, cosa manca]
- [ ] [Task non iniziato]

## 💡 What Worked
[Approcci, prompt, comandi, decisioni con esito positivo.
Riporta i dettagli tecnici ESATTI — il prossimo operatore deve poterli riusare.]

- **[Tecnica/Approccio]**: [descrizione] → [risultato] ([perché ha funzionato])
- Esempio: **Prompt NanoBanana hero ESAC**: "single red LED strip, deep ocean black bg, cinematic 4K, product hero shot, no text" → qualità eccellente al primo tentativo (lighting semplice = più controllo in post)

## ❌ What Didn't Work
[Errori, vicoli ciechi, approcci falliti con parametri esatti.
Ogni voce qui è tempo risparmiato al prossimo operatore. MAI lasciare vuota per ottimismo.]

- **[Approccio fallito]**: [cosa è andato storto] → [cosa NON rifare]
- Esempio: **Seedance speed 1.0**: movimento troppo agitato, effetto non premium → usare max 0.6 per contenuti luxury

Se nulla è fallito: "Nessun vicolo cieco rilevante in questa sessione."

## 🚀 Next Steps
[Lista numerata, ordinata per priorità. Item #1 eseguibile subito senza domande.
Se il progetto ha `openspec/PLAN.md`: ogni voce cita l'id della entry che fa avanzare
(es. "[E03]"), oppure dichiara esplicitamente "fuori piano". Un next step che non sa a
quale entry appartiene è il sintomo che il piano va aggiornato, non che la citazione va omessa.]

1. [E<NN>] [Azione concreta — tool, file input, output atteso]
2. [E<NN>] [Azione concreta]
3. [fuori piano] [Azione concreta]

## 📎 Context Importante (opzionale)
[Solo se serve: vincoli cliente, decisioni architetturali con motivazione,
convenzioni di naming, link a file di supporto. Non duplicare sezioni sopra.
Qui va anche il contesto operativo che vive fuori dal repo — un progetto di prova,
una cartella temporanea, un file di scratch: se serve a un Next Step e non è
versionato, o lo scrivi qui con il comando per ricrearlo, o sparisce con la sessione.]
- Credenziali: → vedi .env (mai nel testo)
- Riferimenti: `.handoff/PROMPTS.md`, `.handoff/CLIENTS.md`

Le tre categorie di fatto

Un handoff trasporta tre tipi di affermazione. Hanno tempi di vita diversi e vanno scritte in forme diverse — dare loro la stessa forma è il modo in cui un handoff perde informazione senza che nessuno se ne accorga.

CategoriaCosa contieneCome si scrive
Fatti stabilidecisioni, motivazioni, What Worked, What Didn't Workprosa, esattamente come sempre — non invecchiano, e infatti si trasferiscono intatti
Fatti volatiliconteggi, stati, numeri di commit, task aperti, esiti di test e del gateil comando che li produce, mai il valore congelato — sezione 🔄 Stato volatile
Puntatoriqualsiasi affermazione sul contenuto di un filefile § sezione verificabile, mai un riassunto senza fonte

Perché. Misurato su un passaggio di consegne reale — HANDOFF-009 letto da un agente in finestra nuova, 2026-08-04:

  • l'handoff diceva "16 commit locali"; erano 17. Il numero era già sbagliato quando è stato scritto, perché non contava il commit dell'handoff stesso. L'agente ha verificato il disco e ha comunque riportato 16: nulla segnalava quel numero come deperibile, quindi l'ha ereditato invece di rigenerarlo;
  • l'handoff diceva "il task 7.1(e) contraddice ROUTER.md" senza dire dove. L'agente ha ricostruito la tesi e ha citato "§ Reporting", una sezione che non esiste: le sezioni vere sono skills/skill-router/ROUTER.md §§ Stage 2C, Reconciling 2C and 2D, Stage 5 — Trace. Un riassunto senza fonte non è un riassunto: è un invito a inventarne una.

Le motivazioni si trasferiscono, i numeri no. Il formato non deve dargli la stessa autorevolezza.

Regola dei puntatori

Ogni affermazione su cosa contiene un file si cita come file § sezione: il path e il titolo della sezione sotto cui l'affermazione è verificabile. Il numero di riga può accompagnare il titolo, non può sostituirlo — le righe si spostano a ogni edit, i titoli no.

  • skills/skill-router/ROUTER.md § Stage 5 — Trace (riga 274)
  • ❌ "ROUTER.md dice il contrario" — chi legge non può controllare, quindi ricostruisce
  • ROUTER.md:274 da solo — fra due sessioni punta a un'altra riga e nessuno se ne accorge

Un puntatore rotto si vede al primo grep; un riassunto sbagliato no. Per questo la forma verificabile vale anche quando costa una riga in più.


Regole di aggiornamento (quando HANDOFF già esiste)

Quando crei HANDOFF-NNN.md a partire dal precedente:

  1. Incrementa numero sessione, aggiorna data.
  2. Migra i Next Steps completati → Current Progress (checked ✓).
  3. Appendi nuove voci a What Worked / What Didn't Work — non cancellare mai le precedenti. Se serve chiarezza, prefissa con (S3) per indicare la sessione.
  4. Riscrivi Next Steps da zero per la prossima sessione. Se esiste openspec/PLAN.md, la continuità di medio periodo vive lì, non qui: prima di riscrivere, controlla che ogni intenzione che sopravvive alla sessione sia una entry del piano. Un item che ricompare come "ereditato" di handoff in handoff è una entry mancante, non un next step.
  5. Goal — tocca solo se lo scope è cambiato davvero. Con un piano presente, il Goal si aggiorna cambiando goal: in openspec/PLAN.md e ricopiandolo qui, mai solo qui.
  6. Posizione nel pianorigenerala sempre rileggendo openspec/PLAN.md, mai copiata dall'handoff precedente: è una fotografia dello stato corrente, e una fotografia riportata in avanti è esattamente il modo in cui il piano e il racconto smettono di coincidere.
  7. Stato volatilerigenerala sempre, mai ereditata dall'handoff precedente. Stessa disciplina della posizione nel piano, per la stessa ragione: qui però si rigenera la lista dei comandi, non i loro output. Se durante la sessione hai citato un conteggio nuovo da qualche parte nel documento, il suo comando entra in questa sezione prima di salvare.

skills

README.md

tile.json