🎯 OBJECTIF
Comprendre OpenSpec à travers un fil rouge — l'ajout d'une authentification à deux facteurs (2FA) sur les comptes admin :
propose → review → apply → archive (qui fait quoi) ;opsx (profil cœur vs étendu) et à quoi sert chacune ;config.yaml, schéma custom) ;🧠 MODÈLE MENTAL
Un agent de code est puissant mais non-déterministe : si les exigences ne vivent que dans l'historique de chat, elles se perdent, se déforment, et deviennent impossibles à reviewer. OpenSpec déplace la source de vérité du chat vers un artefact écrit, versionné dans le repo, sur lequel humain et IA s'accordent avant d'écrire la moindre ligne. L'intuition n'est pas "ajouter du process" mais "figer l'intention" : on transforme un prompt flou en une spec relisable, qu'on peut diffuser, critiquer en PR, et qui survit à la fin de la session. C'est le levier « spec versionnée » évoqué dans outiller-developpement-ia-skills-agents-instructions, poussé jusqu'à un workflow complet.
openspec/changes/<change-id>/.proposal.md, le delta de specs/, design.md, tasks.md.ADDED / MODIFIED / REMOVED), par opposition à la spec courante complète.auth), à laquelle est rattachée une spec.spec-driven est le schéma intégré par défaut.MUST / SHALL / SHOULD / MAY) exprimant la force d'une exigence.Sans spec, l'agent génère du code à partir d'un prompt vague : exigences manquantes, fonctionnalités non demandées, résultat incohérent. Pire pour la review : le relecteur n'a aucun référentiel écrit auquel comparer le code produit. Et au-delà d'un certain remplissage du contexte, l'agent oublie ou altère les détails d'exigence vus plus tôt dans la conversation.
OpenSpec répond par un principe simple : structure avant code. On décrit le changement, on raffine le comportement attendu, on planifie les tâches, puis on implémente contre une spec validée.
🔑 Conclusion clé
OpenSpec ne rend pas l'agent plus intelligent — il rend l'intention auditable et persistante. Le gain vient du référentiel écrit, pas d'une couche magique.
Cas concret : on veut ajouter une 2FA sur les comptes admin. Voilà le déroulé. On lance d'abord la proposition — l'IA scaffolde le change et génère les artefacts :
You: /opsx:propose add-two-factor-auth
AI: Created openspec/changes/add-two-factor-auth/
✓ proposal.md — pourquoi & quoi
✓ specs/ — requirements & scenarios (le delta)
✓ design.md — approche technique
✓ tasks.md — checklist d'implémentation
Prêt pour la review.Le workflow global tient en quatre temps, et le point qui bloque souvent c'est qui agit à chaque étape :
flowchart LR
P["1 · PROPOSE<br/>TOI : l'intention<br/>IA : drafte les 4 artefacts"]
REV{"2 · REVIEW<br/>TOI : valides & corriges"}
AP["3 · APPLY<br/>IA : implémente tasks.md"]
AR["4 · ARCHIVE<br/>merge delta → specs/<br/>+ historise"]
P --> REV --> AP --> ARmermaidadd-two-factor-auth + 1-2 phrases) ; l'IA génère les 4 artefacts.tasks.md ; tu supervises.openspec/specs/ (la vérité courante) et le change part, daté, dans archive/. (Entre apply et archive, /opsx:sync peut propager le delta dans specs/ sans archiver — optionnel, utile pour un change long ou des changes en parallèle.)Ce que TU écris vs ce que l'IA génère
| Étape | Toi | L'IA |
|---|---|---|
| Propose | l'intention (1-2 phrases) | drafte les 4 artefacts |
| Review | valides / corriges (surtout Out of Scope + delta) |
— |
| Apply | supervises | implémente tasks.md |
| Archive | déclenches | merge dans specs/, archive |
Tu n'écris quasi jamais les .md à la main : l'IA les drafte. Ton vrai travail = la review. Le seul artefact durable que tu possèdes vraiment = la spec dans specs/.
⚠️ Ne pas sauter l'archive
Tant que le delta n'est pas fusionné dans openspec/specs/, la vérité courante ment : elle ne reflète plus le code livré. archive (ou sync) n'est pas du ménage — c'est ce qui maintient specs/ aligné avec le réel.
Par défaut, OpenSpec active le profil core. /opsx:propose y fait tout d'un coup (scaffold + tous les artefacts). Le profil étendu (à activer via openspec config profile puis openspec update) découpe cette étape pour itérer plus finement — créer un artefact à la fois, tester chaque template indépendamment.
| Commande | Profil | Rôle |
|---|---|---|
/opsx:explore |
cœur | Défricher une idée, investiguer, clarifier le besoin — partenaire de réflexion, sans structure, avant de proposer |
/opsx:propose |
cœur | Crée le change et génère tous les artefacts de planif d'un coup (chemin rapide) |
/opsx:apply |
cœur | Implémente les tâches, met à jour les artefacts au besoin |
/opsx:sync |
cœur | (optionnel) Propage le delta de specs vers specs/ sans archiver |
/opsx:archive |
cœur | Clôt le change : merge le delta dans specs/ + déplace dans archive/ |
/opsx:new |
étendu | Scaffold du change seul (dossier, sans artefacts) |
/opsx:continue |
étendu | Crée le prochain artefact possible, un à la fois (selon les dépendances) |
/opsx:ff |
étendu | Fast-forward : génère tous les artefacts de planif d'un coup |
/opsx:verify |
étendu | Valide l'implémentation contre les artefacts |
/opsx:bulk-archive |
étendu | Archive plusieurs changes terminés d'un coup |
/opsx:onboard |
étendu | Walkthrough guidé d'un change de bout en bout |
Cœur ou étendu ?
/opsx:propose (cœur) = /opsx:new + /opsx:ff en une seule commande. Passe à l'étendu seulement si tu veux construire le change incrémentalement (/opsx:continue artefact par artefact) ou vérifier finement (/opsx:verify). Sinon, le cœur suffit.
Toute la mécanique tient dans une séparation état courant / diff en cours :
openspec/specs/ — la spec vivante, source de vérité de ce que le système fait aujourd'hui. Organisée par domaine (auth/, payments/…).openspec/changes/<change-id>/ — un dossier par changement, qui regroupe proposal, delta de specs, design et tasks. Ce regroupement par feature rend le suivi lisible.openspec/changes/archive/ — l'historique daté des changements appliqués.flowchart LR
subgraph C["openspec/changes/add-two-factor-auth/"]
P["proposal.md"]
S["specs/ — delta"]
D["design.md"]
T["tasks.md"]
end
SP["openspec/specs/auth/ — verite courante"]
AR["openspec/changes/archive/ — historique date"]
C -->|"apply : implémente le delta"| SP
C -->|"sync / archive : fusionne le delta"| SP
C -->|"archive : déplacement daté"| ARmermaidPourquoi ce découpage gagne en brownfield
Séparer état et diff permet de toucher une feature existante sans réécrire toute sa spec : on ne décrit que le delta. C'est l'inverse d'un outil greenfield qui suppose que tout part de zéro. Bonus : deux changes peuvent modifier des requirements différents du même domaine sans conflit.
Ces quatre fichiers sont produits par l'IA sur /opsx:propose. Ton rôle n'est pas de les écrire mais de les relire. Voici à quoi chacun ressemble pour notre change add-two-factor-auth, et le point d'attention en review (annotations ←).
proposal.md — le pourquoi / quoi / impact.
# Proposal: Add Two-Factor Authentication
## Why
Les comptes admin sont la cible n°1 de credential stuffing.
## What Changes
- Challenge OTP (TOTP) après validation du mot de passe.
## Out of Scope ← EN REVIEW : vérifie que l'IA n'élargit pas le périmètre
- SMS comme second facteur (TOTP uniquement).
## Impact
- Specs : `auth`. Breaking : `POST /login` → `202 otp_required`.
En review : le
Out of Scopeet l'Impact(breaking changes) sont les deux blocs où l'IA dérape le plus. C'est là que tu cadres.
specs/auth/spec.md — le delta (le cœur). Une spec est un contrat de comportement : requirements + scenarios en Given/When/Then, mots-clés RFC 2119. Pas de noms de classes ni de librairie (ça, c'est design.md). On décrit le diff via trois opérations :
# Delta for Auth
## ADDED Requirements ← nouvelle exigence
### Requirement: Two-Factor Authentication
The system SHALL require a second factor (TOTP) for admin login.
#### Scenario: OTP required ← ≥1 scénario, sinon validation KO
- GIVEN a user with 2FA enabled
- WHEN the user submits valid credentials
- THEN an OTP challenge is presented
## MODIFIED Requirements ← requirement RÉÉCRIT en entier
### Requirement: Session Timeout
The system SHALL expire sessions after 15 minutes of inactivity.
#### Scenario: Idle timeout
- GIVEN an authenticated session
- WHEN 15 minutes pass without activity
- THEN the session is invalidated
## REMOVED Requirements ← + Reason / Migration
### Requirement: Remember Me
**Reason**: Deprecated in favor of 2FA.
**Migration**: Tokens révoqués ; ré-auth à chaque session.
À l'archive : ADDED s'ajoute, MODIFIED remplace, REMOVED supprime.
⚠️ En review, trois pièges de format à traquer
openspec validate échoue.### Requirement: / #### Scenario: exactement (format codé en dur dans le parser).design.md — l'approche technique (optionnel, changements non triviaux). C'est ici que vivent les décisions d'archi et les alternatives écartées — pas dans la spec.
# Design: Two-Factor Authentication
## Décision : TOTP vs SMS
TOTP (RFC 6238). SMS écarté : coût, SIM-swap, dépendance opérateur.
## Alternatives écartées
WebAuthn/passkeys : meilleure sécu, mais hors scope de cette itération.
En review : les décisions tiennent-elles ? Les alternatives sont-elles justifiées ? C'est souvent ici qu'on découvre « cette approche ne marchera pas » — avant de coder.
tasks.md — la checklist exécutable. Découpée en phases, cochée au fil de l'eau par l'IA pendant apply.
# Tasks: Add Two-Factor Authentication
## Phase 1 — MVP
- [ ] 1.1 Migration : table `user_2fa_secrets`
- [ ] 1.2 Service `TotpService` (RFC 6238)
- [ ] 1.3 `POST /login` → `202 otp_required` si 2FA active
## Phase 2 — Durcissement
- [ ] 2.1 Rate-limit après 5 échecs OTP
- [ ] 2.2 Lint + build + tests au vert ← vérif = une tâche à part entière
Conventions de nommage
user-auth, payment-capture), un seul objectif ; si la description a besoin d'un « AND », splitter.add-, update-, remove-, refactor-), unique (-2, -3 si pris).file.ts:42 ; une spec se cite specs/auth/spec.md.Point clé : le schéma spec-driven est intégré à OpenSpec. Tu ne l'écris jamais. La config ne sert qu'à le customiser, et elle est facultative. Trois niveaux.
openspec init # et c'est toutbashTu es sur le schéma spec-driven, qui génère proposal → specs → design → tasks avec les conventions vues plus haut. Aucune config nécessaire. Pour le contexte projet (stack, archi), tu remplis simplement project.md.
config.yaml (optionnel) : contexte + règlesPour imposer du contexte ou des règles maison à chaque change :
# openspec/config.yaml — UNIQUEMENT si tu veux customiser
schema: spec-driven # schéma par défaut → évite de passer --schema
context: | # injecté dans TOUS les artefacts (limite 50 KB)
Stack : TypeScript, React, Node, Postgres.
Rétro-compat obligatoire sur les API publiques.
rules: # règles PAR artefact (clé = id d'artefact)
proposal: # → injectées en générant le proposal seulement
- Inclure un plan de rollback
specs: # → injectées en générant les specs seulement
- Au moins un scénario d'erreur en plus du happy-pathyamlInjection : context et rules sont insérés dans le prompt sous forme de blocs <context> / <rules> (puis le <template> du schéma). Le contexte va partout ; une règle seulement sur l'artefact qu'elle cible. Ordre de résolution du schéma : --schema CLI → .openspec.yaml du change → config.yaml → défaut spec-driven.
Un schéma, c'est la recette d'un change : la liste des artefacts qu'il doit contenir, leur ordre de dépendance, et les instructions que l'IA suit pour générer chacun. Le défaut spec-driven impose toujours proposal → specs → design → tasks.
La différence avec le Mode 2 est là :
config.yaml) — tu gardes la recette par défaut et tu la décores (contexte + règles). La liste des fichiers ne change pas.Trois cas typiques : workflow plus léger (sauter specs/design), plus lourd (ajouter un research avant le proposal), ou changer les prompts (ex. imposer un diagramme de séquence dans chaque design.md — effet immédiat, plus besoin d'attendre une release).
On ne part jamais de zéro : on forke le défaut, puis on édite. Exemple — un workflow rapid réduit à proposal + tasks :
openspec schema fork spec-driven rapid # copie spec-driven dans openspec/schemas/rapid/bash# openspec/schemas/rapid/schema.yaml — édité : on a retiré specs et design
name: rapid
version: 1
description: Workflow léger — pas de specs ni de design
artifacts:
- id: proposal
generates: proposal.md
template: proposal.md
requires: [] # rien requis → créable en premier
- id: tasks
generates: tasks.md
template: tasks.md
requires: [proposal] # tasks juste après proposal (plus de specs/design entre)
apply:
requires: [tasks] # on n'implémente qu'une fois tasks créé
tracks: tasks.md # apply coche cette checklistyamlopenspec schema validate rapid # vérifie syntaxe, templates, pas de cycle
openspec new change quick-fix --schema rapid # ou `schema: rapid` dans config.yaml pour en faire le défautbashChamps du schema.yaml, relus sur cet exemple : id (nom de l'artefact, = clé des rules), generates (fichier produit, globs OK), template (squelette dans templates/ que l'IA remplit), instruction (consigne IA), requires (artefacts prérequis), apply.requires / apply.tracks (ce qui doit exister avant d'implémenter + la checklist suivie). Les templates/*.md sont des squelettes avec des commentaires HTML qui guident l'IA.
Dépendances = enablers, pas des gates
requires: [proposal] veut dire « proposal doit exister avant de pouvoir créer design » — c'est un prérequis d'ordre, pas une obligation d'enchaîner. Rien ne te force à créer design : les dépendances montrent ce qui devient possible, pas ce que tu dois faire ensuite. C'est tout l'esprit OPSX : des actions, pas des phases.
⚠️ Le Mode 3 est rarement nécessaire
La plupart des équipes ne touchent jamais au schéma custom : le défaut (Mode 1) couvre l'essentiel, config.yaml (Mode 2) la quasi-totalité du reste. Le Mode 3 sert seulement quand la forme même du workflow par défaut ne convient pas. Et attention : le fichier de config doit s'appeler config.yaml (pas config.yml), sinon il est ignoré sans erreur ; le YAML natif n'est pas templaté ({{…}}) ; un id d'artefact inconnu dans rules ne lève qu'un warning.
Prérequis : Node.js ≥ 20.19.0. Package npm @fission-ai/openspec (MIT, open-source).
npm install -g @fission-ai/openspec@latest # aussi : pnpm, yarn, bun, nix
cd your-project
openspec init # openspec/ + config interactive + skills
openspec update # régénère instructions agent + slash commandsbashopenspec init génère aussi des skills dans .claude/skills/ (ou équivalent) que les agents auto-détectent. Selon l'outil : Claude Code / Cursor → slash commands /opsx:* ; GitHub Copilot CLI → prompts sous .github/prompts/ ; Windsurf → workflows sous .windsurf/ ; le reste → AGENTS.md générique. Plus de 25 outils supportés.
⚠️ Qualité bornée par le modèle ET l'hygiène de contexte
OpenSpec recommande des modèles à fort raisonnement (Codex 5.5, Opus 4.7) et un contexte propre : vider le contexte avant apply. Sur un petit modèle ou un contexte pollué, le bénéfice se dégrade — l'outil discipline le workflow, il ne compense pas un mauvais raisonnement. Télémétrie anonyme par défaut (noms de commandes + version, pas de PII), désactivable via OPENSPEC_TELEMETRY=0 ou DO_NOT_TRACK=1.
| Critère | OpenSpec | Spec Kit (GitHub) | Kiro (AWS) |
|---|---|---|---|
| Poids du process | Léger, itératif, actions ré-éditables | Lourd, phase gates rigides | Structuré, intégré à l'IDE |
| Setup | npm/node, openspec init |
Setup Python | IDE dédié |
| Brownfield (1→n) | Conçu pour | Plus orienté greenfield | Variable |
| Greenfield (0→1) | Fonctionne, pas son différenciateur | Point fort | Oui |
| Lock-in | Aucun — 25+ outils | Aucun mais cérémonie | IDE AWS + modèles Claude only |
| Modèle de specs | Deux dossiers (état / delta) | Multi-fichiers Markdown | Specs éparpillées |
Quand choisir OpenSpec
⚡ TL;DR — chaque concept en une ligne
Spec-driven development (SDD) ✓ La spec écrite et versionnée devient la source de vérité, validée avant tout code. ⚠ Ne remplace pas la réflexion : une spec vague produit du code vague.
Workflow propose → review → apply → archive
✓ propose drafte les artefacts (TOI : l'intention), tu valides en review, apply implémente, archive merge le delta dans specs/ + historise.
⚠ Ton vrai travail = la review ; sauter l'archive fait diverger specs/ du code réel.
Commandes opsx
✓ Cœur : explore / propose / apply / sync / archive. Étendu : new / continue / ff / verify / bulk-archive / onboard.
⚠ propose = new + ff ; l'étendu ne sert que pour itérer artefact par artefact.
Format des specs (delta) ✓ Requirements + scenarios (Given/When/Then), RFC 2119, opérations ADDED/MODIFIED/REMOVED. ⚠ MODIFIED = requirement réécrit en entier ; tout requirement sans scénario casse la validation.
Customisation (3 niveaux)
✓ spec-driven intégré (init suffit) ; config.yaml décore (context/rules) ; schema.yaml change la recette (les artefacts eux-mêmes).
⚠ Le Mode 3 est rarement utile ; config.yaml (pas .yml) et le YAML natif n'est pas templaté.
🎓 À retenir
propose ; ton vrai travail est la review, et le seul artefact durable que tu possèdes = la spec dans specs/.archive réécrit specs/ — ADDED s'ajoute, MODIFIED remplace, REMOVED supprime, puis historise ; sync fait le merge sans archiver, en option.config.yaml décore la recette par défaut, un schéma custom change la recette ; la plupart des équipes restent en Mode 1/2.config.yaml, injection, schema.yaml + templates, schema fork/init/validate.