spec-lite — spec local durable + docs synchronisés par changement
Une sous-procédure partagée pour les skills de la stage Chorus (proposal, develop, yolo) — le mode spec léger, modélisé sur les superpowers (un spec durable qui persiste, plus des artefacts par effort) : un spec local durable par capacité (<slug>/spec.md, édité sur place, jamais synchronisé — l'historique git est sa trace), plus un dossier daté par effort de changement (<slug>/<YYYY-MM-DD>-<change-slug>/ de docs typés Chorus — prd.md, … — qui sont mirrored 1:1 dans les Chorus Documents persistants). Aucune nouvelle CLI, aucun tool MCP, aucun backend, aucun schéma — le mirroring réutilise les outils de documents existants.
Mode (comment tu es arrivé ici)
Le mode spec est calculé par le hook agentSpawn (bin/resolve-spec-mode.sh), pas par toi — la section ## Spec Mode de ton contexte indique le CHORUS_SPEC_MODE résolu. Tu es ici parce qu'il s'est résolu en lite ; s'il est autre, ce skill est inactif — retourne à l'appelant. (Pour info, la règle du hook : un CHORUS_SPEC_MODE explicite gagne, sinon OpenSpec quand utilisable, sinon lite.)
Le spec local durable — <slug>/spec.md
.chorus/specs/<slug>/spec.md — <slug> (kebab-case) nomme une capacité/feature, pas un seul changement. C'est la source unique, cumulative, lisible par un humain de la capacité : éditée sur place par chaque changement, jamais mirrored vers Chorus, ne porte aucun id Chorus. Frontmatter minimal seulement (slug, title, status: draft|active|done, created), puis de la prose — ## Intent, ## Requirements (prose + points d'acceptation - [ ], pas de grammaire SHALL/scénario), ## Non-goals. Pars du template durable spec.md inline ci-dessous. Son historique git est l'enregistrement complet — pas de section changelog, pas de round-trip Chorus. Ce fichier N'ENTRE JAMAIS dans la boucle mirror.
status décrit la capacité, pas un seul changement : active tant qu'un changement est en vol, done quand le changement actuel livre et qu'aucun n'est ouvert. Un nouveau changement contre une capacité done la réouvre à active, retour à done à la livraison.
Template — le spec.md durable
---
slug: <kebab-case-capability>
title: <Capability title>
status: draft # draft | active | done
created: <YYYY-MM-DD>
---
## Intent
<what this capability is for, in prose>
## Requirements
<prose, no SHALL/scenario grammar>
- [ ] <acceptance point>
## Non-goals
- <explicitly out of scope>
Dossiers datés par changement — <slug>/<YYYY-MM-DD>-<change-slug>/
Chaque effort de changement est un dossier daté directement sous <slug>/ (pas de wrapper changes/), p. ex. .chorus/specs/<slug>/2026-09-08-add-export/. Date + slug pour que les changements du même jour ne se heurtent pas et les dossiers se trient par date. Il contient les docs typés Chorus pour CE changement — un fichier par type de Document :
| Fichier | Document.type |
Requis ? |
|---|---|---|
prd.md |
prd |
oui — le doc principal par changement |
tech_design.md |
tech_design |
optionnel — le « comment » |
adr.md / guide.md / spec.md |
adr / guide / spec |
optionnel |
Ces fichiers SONT synchronisés — chacun correspond à un Chorus Document persistant de son type. Leur frontmatter porte les ids de sync proposalUuid et documentUuid (le type est impliqué par le nom de fichier). Pars du template de document dossier daté inline ci-dessous. Un changement différent à la même capacité est un dossier daté différent. Le dossier du changement actuel est édité et re-mirrored tout au long de son effort (jusqu'à livraison) ; seuls les dossiers datés précédemment livrés sont laissés gelés — tu ne reviens pas et ne réécris pas un changement passé.
Deux fichiers nommés
spec.md, rôles différents. Le<slug>/spec.mddurable (local seulement, pas d'ids) N'EST PAS le même qu'un docspec-type par changement, qui vivrait à<slug>/<date>-<slug>/spec.md(synchronisé, porte les ids). Préfèreprd.mdcomme doc principal par changement pour éviter la confusion.
Template — un document dossier daté
Le type de document est impliqué par le nom de fichier (prd.md → prd, tech_design.md → tech_design, …), PAS une clé frontmatter.
---
title: <Document title as it appears in Chorus>
proposalUuid: <uuid> # written on first mirror
documentUuid: # empty until the draft materializes on approval
---
# <Document title>
<body — this file's bytes are the source of truth for the Chorus Document>
Flow (un changement)
- Confirme mode =
lite(sinon no-op). - Crée le dossier daté
<slug>/<YYYY-MM-DD>-<change-slug>/et écris ses docs de changement synchronisés —prd.md(requis),tech_design.mdetc. seulement si justifiés (utilise le template de document dossier daté ci-dessus). - Mets à jour
<slug>/spec.mdsur place vers la nouvelle vérité cumulative (Requirements, points d'acceptation,status) — local seulement, pas de sync. - Crée le conteneur de proposal avec une ligne de locateur littérale dans
description(ligne propre, pas de ponctuation traînante) pour que develop trouve le changement :Spec-lite: .chorus/specs/<slug>/<YYYY-MM-DD>-<change-slug>/ - Mirror les docs du dossier daté vers Chorus (ci-dessous). Ajoute des tâches via
chorus_pm_add_task_draft— pas detasks.md, pas de CLI / validate / archive, pas de grammaire delta. Les tâches vivent dans Chorus. - Develop → continue d'éditer
spec.md+ les docs de changement, re-mirroring les docs de changement au fur et à mesure du travail et cochage des points d'acceptation. À la livraison, définis lestatus: doneduspec.mddurable.
Mirror — seulement les docs dossier daté (jamais spec.md)
Chaque <type>.md de dossier daté correspond à un Chorus Document persistant de ce type, tracé par documentUuid dans le frontmatter du fichier. Remplis content à partir des octets du fichier avec --arg-file — ne retape jamais le corps (dérive, brûle ~20k tokens). Un appel par fichier ; résous l'identité par documentUuid / (proposalUuid, type), jamais par title seul (une recherche trouvant zéro ou >1 DOIT halter). Protège chaque appel avec le helper chorus_check_response halt-on-error (openspec-aware §6). Pas de chorus sur PATH ? Retombe sur chorus-api.sh + json_encode_file (openspec-aware §3.6). <slug>/spec.md n'est JAMAIS dans cette boucle.
- Première fois qu'un doc est écrit (son dossier daté est nouveau) : écris
proposalUuiddans le frontmatter, mirror dans un draft de proposal —chorus mcp call chorus_pm_add_document_draft "{\"proposalUuid\":\"$P\",\"type\":\"prd\",\"title\":\"PRD: $TITLE\"}" --arg-file content=".chorus/specs/$SLUG/$DATED/prd.md". Édite le draft viachorus_pm_update_document_draft(draftUuidretourné) avant approbation. À l'approbation, il se matérialise en Document persistant — résous par(proposalUuid, type)viachorus_get_documents, enregistredocumentUuiddans le frontmatter, re-mirror une fois pour que local == Chorus. - Éditions ultérieures (un doc qui a déjà un
documentUuid) : édite le fichier, puischorus mcp call chorus_pm_update_document "{\"documentUuid\":\"$D\"}" --arg-file content=".chorus/specs/$SLUG/$DATED/<type>.md". Chaque mise à jour auto-incrémente la version du Document — cet historique de versions est l'enregistrement du doc de changement dans Chorus, aux côtés de git.
留痕: historique git + versions de Document
git log -- .chorus/specs/$SLUG/ est la piste d'audit — les diffs sur place du spec.md durable plus les docs de changement de chaque dossier daté ; les versions auto-incrémentées des Documents mirrored sont l'enregistrement parallèle dans Chorus. Aucune section changelog à maintenir. Seul .chorus/specs/ est versionné (.chorus/* + !.chorus/specs/).
Monowriter : le dossier est partagé — dans une vague multi-tâche seul l'orchestrator / agent principal édite + re-mirror ; les workers parallèles rapportent via chorus_report_work seulement, relisant avant toute écriture. L'état de tâche vit dans Chorus, pas dans les docs — les points - [ ] sont l'intention d'acceptation, pas un tracker.