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

spec-driven-development/spec-as-source

📦 Pubblicato su Tessl: tessl.io/registry/spec-driven-development/spec-as-source

⚠️ Il workspace è cambiato. Fino alla 2.2.0 il plugin viveva su spec-driven-devlopment — con un refuso nel nome. Quella versione è archiviata dal 2026-07-16 e non riceve più aggiornamenti: non contiene il router, plan-mode, né il gate del piano. Il nome corretto è spec-driven-development, e da lì riparte la 3.0.0. Se avevi installato il vecchio: tessl uninstall spec-driven-devlopment/spec-as-source, poi il comando di installazione qui sotto.

Workflow di spec-driven development costruito su OpenSpec, con enforcement meccanico per lo spec-as-source sopra di esso.

OpenSpec porta un progetto a spec-anchored (propose → apply → archive, con openspec/specs/<capability>/spec.md come specifica canonica), ma non verifica nulla da solo. Questo plugin aggiunge una schema OpenSpec custom (spec-as-source) e gli strumenti che rendono la spec un vincolo strutturale: ownership dei file, verifica dei test, workflow CI, e le skill che li orchestrano.


Installazione

# prerequisito: CLI OpenSpec
npm install -g @fission-ai/openspec

# nel tuo progetto
tessl install spec-driven-development/spec-as-source

Richiede: openspec CLI, git, un test runner (pytest / vitest / jest / cargo / go), e PyYAML (pip install -r requirements.txt) per il router.


Un solo punto d'ingresso

Le skill sono 18 e formano una pipeline ordinata: non serve conoscerle tutte. skill-router è pensata per essere l'unica registrata globalmente; le altre restano nel repo e vengono lette da lì quando servono.

bash skills/skill-router/install.sh          # symlink in ~/.claude/skills/, mai una copia
bash skills/skill-router/install.sh --check  # sola lettura: stato + omonimi obsoleti

Dato un prompt, il router decide in tre passaggi in quest'ordine:

  1. esclude il lavoro che non merita il processo (bugfix banali, one-liner, domande senza codice), citando la fonte dell'esclusione;
  2. deduce la fase dallo stato reale del disco tramite sonde ordinate (P0-P8) — e se la richiesta somiglia a una skill a valle della fase rilevata, dirotta a monte dicendo quale sonda ha fallito;
  3. solo allora confronta le description, e solo fra i candidati ammissibili in quella fase.

Il motivo dell'ordine: queste skill sono le fasi di un processo, non un menù di attrezzi paritetici. Un router che instrada per sola somiglianza salta le fasi, e saltare le fasi è esattamente ciò che questo metodo esiste per impedire.

Ogni decisione è tracciata: gate con la fonte, fase con la sonda e il suo output, candidati, scartati con motivo, scelta, modalità di esecuzione, e il frammento di description su cui il match si fonda.

skills/skill-router/routing-table.md è generata dalle skill (python3 skills/skill-router/build_router.py), non scritta a mano: è deterministica byte-a-byte, e riporta anche le divergenze fra disco e plugin.json, le copie ombra che sono andate alla deriva, e i riferimenti a skill inesistenti.

Limite dichiarato: il router governa cosa succede quando viene invocato lui, non cosa carica l'agente all'avvio. Se in ~/.claude/skills/ è già installata una versione obsoleta di una skill, quella continua ad attivarsi da sola — install.sh la segnala e non la rimuove, perché è una decisione dell'operatore.


Cosa installa

Rule (sempre attive)

RuleComportamento imposto
spec-as-sourceL'implementazione non è completa finché tutti i [@test] esistono; un targets non cambia senza la sua spec
generated-file-headerOgni file targets inizia con # GENERATED FROM SPEC
handoff-suggestionSuggerisce /handoff save quando il contesto della conversazione si fa lungo (fallback portabile dell'hook PreCompact)
prompt-loopPrima di openspec-propose, per lavoro di sviluppo non banale: attiva in automatico il loop di rifinimento del prompt (punteggio→intervista→lock) finché non è meccanicamente pronto
history-logDopo ogni transizione di stato (proposta creata, task completato, verify passato/fallito, iterazione di spec-loop, change archiviato, snapshot handoff salvato, voce di piano avanzata) appende una riga a .handoff/HISTORY.md, se presente — il diario continuo che completa gli snapshot periodici di handoff
plan-modeNessun change parte fuori dal piano: openspec/PLAN.md deve avere una voce approvata da un umano che lo dichiari, e scripts/check-plan-gate.sh lo verifica meccanicamente. Il gate è imposto a due porte — il router non instrada verso openspec-propose, e openspec-propose lo esegue da sé prima di creare qualunque artifact — così invocare la skill direttamente non lo aggira. Un cancello solo: stesso script, stesse reason, nessuna logica di approvazione duplicata

Skill — prima di scrivere codice (on-demand)

SkillQuando usarlaComando
plan-modeper costruire il piano che porta al goal, prima ancora di sapere quali change servono: esplora in sola lettura, scrive le voci, poi si ferma e chiede l'approvazioneUse plan-mode.
prompt-loopper rifinire il prompt in ingresso con un loop punteggio→intervista→lock finché non è pronto (si attiva anche in automatico prima di openspec-propose per lavoro non banale, assorbendo l'interview di requirement-gathering)Use prompt-loop.
requirement-gatheringquando la richiesta è vaga: interview strutturata, una domanda alla voltaUse requirement-gathering.
spec-writerper scrivere o correggere a mano una openspec/specs/<capability>/spec.mdUse spec-writer.

Skill — workflow OpenSpec (on-demand, genericizzate cross-agente)

SkillQuando usarlaComando
openspec-exploreper investigare un'idea prima di proporlaUse openspec-explore.
openspec-proposeper proporre un change e generare tutti gli artifact in un colpo — esegue il gate del piano come propria precondizione: senza una voce approvata si ferma prima di creare qualsiasi cosaUse openspec-propose.
openspec-apply-changeper implementare i task di un changeUse openspec-apply-change.
spec-loopper implementare i task in loop autonomo Ralph-style: un task per iterazione a contesto fresco, gate meccanico su verify.shUse spec-loop.
openspec-sync-specsper unire le delta spec di un change in openspec/specs/Use openspec-sync-specs.
openspec-archive-changeper chiudere e archiviare un change completatoUse openspec-archive-change.

spec-loop (scripts/ralph/loop.sh) espone i suoi guardrail come variabili d'ambiente — MAX_ITERATIONS (default 25), MAX_CONSECUTIVE_FAILURES (default 1), MAX_FIX_ATTEMPTS (default 10). I default sono bassi apposta per non sprecare crediti su un fallimento sistemico (es. session/rate limit esaurito): il loop si ferma dopo un solo tentativo a vuoto invece di bruciarne diversi prima di arrendersi. Se serve più impegno/precisione — tollerare un fallimento transitorio che si autocorregge al tentativo successivo, o lasciare più margine alla modalità di repair — questi valori si possono alzare passandoli come variabili d'ambiente al lancio (es. MAX_CONSECUTIVE_FAILURES=3 bash scripts/ralph/loop.sh <change>), a costo di consumare più crediti per run.

Skill — enforcement spec-as-source (on-demand)

SkillQuando usarlaComando
spec-as-source-setupuna volta, per installare lo schema OpenSpec + script + CI + pre-commitUse spec-as-source-setup.
spec-verifya ogni giro, per verificare spec/test/codice + drift semanticoUse spec-verify.
work-reviewprima di dire "ho finito": review requisito-per-requisito con evidenza file:rigaUse work-review.
spec-ci-syncdopo aver aggiunto una spec o cambiato stackUse spec-ci-sync.
spec-rebuildper dimostrare che le spec sono la sorgenteUse spec-rebuild.

Skill — continuità di sessione (on-demand)

SkillQuando usarlaComando
handoffper congelare lo stato della sessione (.handoff/HANDOFF-NNN.md) e riprenderlo dopo, o per installare l'hook PreCompactUse handoff.
handoff-skillper tracciare bug/malfunzionamenti emersi testando una skill (.handoffskill/<nome-skill>/BUG-NNN.md + STATUS.md), in vista del suo upgradeUse handoff-skill.

Docs

docs/policy.md — la policy in chiaro: perché esiste, come funziona, il ciclo di vita completo, il gap di migrazione da v1.x. Vive nel repo, non nel pacchetto: la CLI Tessl (0.94.0) non impacchetta docs/ anche quando è dichiarato, quindi il link punta a GitHub invece di promettere un file che non arriverebbe.


Confine di fiducia

Il registry segnala questo plugin con un advisory: un W012 e due W011. Non sono un difetto da correggere — nominano una dipendenza su cui il plugin è costruito, e il conteggio ne descrive esattamente la forma.

WarningCosa lo innesca
W012 — dipendenza esterna non verificabilespec-as-source-setup installa la CLI OpenSpec (npm install -g @fission-ai/openspec) e il workflow è guidato dal suo output. Togliere la dipendenza toglierebbe ciò che questo plugin serve a rendere vincolante
W011 ×2 — contenuto di terze parti nel contestoopenspec-propose e openspec-apply-change eseguono openspec instructions --json e seguono i campi instruction, template e rules restituiti. È il modo in cui gli schemi OpenSpec restano autorevoli sulla forma degli artifact, ma significa che contenuto esterno alla conversazione influenza ciò che l'agente scrive

Cosa resta imposto comunque: i controlli meccanici non si fidano di quel canale. check-target-ownership.sh, check-spec-links.sh e check-plan-gate.sh leggono il repository, non l'output della CLI. Uno schema che dicesse all'agente di scrivere codice fuori da una spec fallirebbe comunque l'ownership; un change senza voce approvata fallirebbe comunque il gate, a entrambe le porte. Il raggio d'azione della fiducia concessa è la forma degli artifact, non l'enforcement.


Uso tipico

# 1. una tantum: installa schema OpenSpec + infrastruttura di enforcement
Use spec-as-source-setup.
Use handoff.    # opzionale: installa anche l'hook PreCompact (Workflow E)

# 2. una volta per progetto (e poi a ogni nuovo blocco di lavoro)
Use plan-mode.                 # costruisce openspec/PLAN.md e ne fa approvare le voci — senza una voce approvata, openspec-propose non parte

# 3. per ogni nuova feature
Use prompt-loop.               # rifinisce il prompt in ingresso (punteggio→intervista→lock) prima di proporre — assorbe l'interview di requirement-gathering
Use openspec-propose.         # o openspec-explore se l'idea non è ancora chiara
Use spec-writer.               # se serve scrivere/correggere una spec a mano
Use openspec-apply-change.     # implementa tasks.md
Use spec-loop.                 # oppure: implementa tasks.md in loop autonomo (Ralph)
Use spec-verify.                # verifica targets/test/drift contro la spec
Use work-review.                 # review requisito-per-requisito prima di dire "fatto"
Use openspec-archive-change.     # chiude il change, unisce le spec

# 4. manutenzione occasionale
Use spec-ci-sync.    # dopo aver cambiato stack o aggiunto test
Use spec-rebuild.     # per dimostrare che le spec bastano a ricostruire il codice

# 5. quando la sessione si allunga
Use handoff.    # salva lo stato prima che il contesto rischi allucinazioni

Cosa crea spec-as-source-setup nel progetto:

openspec/schemas/spec-as-source/     # schema OpenSpec custom con targets + [@test]
scripts/check-spec-links.sh          # ogni [@test] punta a un file esistente
scripts/check-target-ownership.sh    # un target non cambia senza la sua spec
scripts/build-spec-manifest.py       # mappa spec → requisiti → target → test
scripts/verify.sh                    # esegue i tre script + test suite
scripts/check-plan-gate.sh           # il gate del piano, eseguibile e pronto all'uso
.pre-commit-config.yaml              # gli stessi check in locale
.github/workflows/spec-verification.yml  # gli stessi check in CI

Il gate del piano viene installato dal setup, non lasciato a un passaggio manuale: un progetto appena inizializzato può eseguirlo subito, prima ancora che il piano esista. plan-mode crea poi il piano che il gate giudica:

openspec/PLAN.md                     # il piano: goal + voci approvate che sequenziano i change

openspec/PLAN.md non è un target di nessuna spec, di proposito: cambia a ogni transizione di stato, e dichiararlo posseduto renderebbe check-target-ownership.sh rosso a ogni voce che avanza. La spec di plan-mode governa il formato e la rule, non l'istanza — stessa scelta già fatta per .handoff/HISTORY.md.


I 5 controlli di spec-verify

I primi 4 girano anche in CI (meccanici); il quinto è una review qualitativa che solo un agente può fare:

#ControlloSe fallisceRecupero
1check-spec-linksun [@test] punta a un file inesistentecrea il file di test mancante
2check-target-ownershipun targets è cambiato senza la sua specannulla la modifica → aggiorna la spec → rigenera dalla spec
3build-spec-manifestuna spec non è parsabilecorreggi frontmatter / header dei requisiti
4test suiteil comportamento contraddice la specallinea il codice, o cambia prima la spec
5drift semanticoil codice fa qualcosa che la spec non descrive (o viceversa), pur con link e test a postoaggiorna la spec (spec-writer) se il codice è corretto, altrimenti correggi il codice

Il controllo #2 è il cuore dello spec-as-source: è ciò che OpenSpec da solo non ha. Il controllo #5 è il cuore di work-review: prova che spec e codice dicano davvero la stessa cosa, non solo che i link tra loro siano formalmente a posto.


Aggiornamento

tessl update spec-driven-development/spec-as-source
# poi RIAVVIA Claude Code: le skill si caricano all'avvio della sessione

Se tessl update non vede il plugin, non è registrato in tessl.json: installalo prima con tessl install spec-driven-development/spec-as-source.


Convenzioni delle spec

Perché gli script funzionino, ogni openspec/specs/<capability>/spec.md deve avere:

---
targets:
  - src/percorso/file.py      # i file che questa capability possiede
---

# <Capability> Specification

## Purpose
...

## Requirements

### Requirement: <nome del requisito>
Descrizione normativa (SHALL/MUST).

**Verified by**: [@test] tests/percorso/test_file.py

#### Scenario: <nome scenario>
- **WHEN** ...
- **THEN** ...
  • frontmatter con targets (dichiarazione di proprietà), prima del titolo;
  • requisiti con nome libero nell'header ### Requirement: <nome> (formato OpenSpec, non più ID REQ-*);
  • ogni requisito con una riga **Verified by**: [@test] <path> dopo la frase normativa, mai come prima riga del blocco.

Una spec appena creata da openspec archive per una capability nuova parte senza frontmatter (lo scheletro di OpenSpec non ne genera uno): non è una violazione finché non viene modificata in modo non banale, momento in cui deve guadagnarsi un targets:.


Migrazione da v1.x

Le versioni precedenti alla 2.0.0 imponevano un formato di spec diverso e fatto in casa: file flat in specs/<nome>.spec.md con header REQ-<AREA>-<NNN>. Non esiste una skill di migrazione automatica. Spostare un progetto v1.x a questa versione richiede di ricreare manualmente ogni spec sotto openspec/specs/<capability>/spec.md seguendo la nuova convenzione frontmatter + **Verified by**, e aggiornare i percorsi [@test] di conseguenza.


Autore

Costruito da Giuseppe Di Canosa. Workspace spec-driven-development.

Workspace
spec-driven-development
Visibility
Public
Created
Last updated
Publish Source
CLI
Badge
spec-driven-development/spec-as-source badge