openspec-aware

Par chorus-aidlc · chorus

Création de contenu en mode OpenSpec opt-in pour les workflows Chorus PM dans Codex. Détecte le CLI `openspec` local, crée la structure `openspec/changes/<slug>/` sur le disque, et synchronise les fichiers Markdown dans les brouillons de documents Chorus via le wrapper `chorus-mcp-call.sh`. Lecture obligatoire pour les skills proposal, develop et yolo lorsque l'utilisateur dispose du CLI `openspec`.

npx skills add https://github.com/chorus-aidlc/chorus --skill openspec-aware

Authoring compatible avec OpenSpec (plugin Codex)

Cette compétence est une sous-procédure partagée invoquée par les compétences de l'étape Chorus (proposal, develop, yolo) chaque fois que l'utilisateur souhaite un authoring piloté par spécifications via la CLI OpenSpec. Elle est opt-in :

  • S'active quand les trois signaux sont présents (voir §1) : CHORUS_OPENSPEC_MODE n'est pas off, un répertoire openspec/ existe à la racine du projet, et la CLI openspec est dans PATH.
  • Sinon, la compétence appelante revient à son comportement libre existant.

Quand vous atteignez un point dans proposal / develop / yolo où cette compétence est référencée, lisez la valeur de CHORUS_OPENSPEC_ACTIVE depuis le contexte SessionStart (voir §1) et branchez-vous en fonction. Ne relancez pas le bloc de détection — le hook SessionStart l'a déjà fait une fois pour cette session.

Spécificités Codex : le wrapper MCP sans état de Codex est chorus-mcp-call.sh, situé à $CHORUS_PLUGIN_DIR/hooks/chorus-mcp-call.sh après résolution de $CHORUS_PLUGIN_DIR avec §2.1. Il est invoqué comme chorus-mcp-call.sh <TOOL_NAME> '<JSON_ARGUMENTS>' — pas de sous-commande mcp-tool (contrairement à la variante Claude Code). Le port Codex n'a pas d'état de session sur disque ; chaque appel est autonome.

Les snippets d'aide ci-dessous sont des snippets Bash. Codex exécute les commandes shell via le shell configuré de l'utilisateur, qui peut être zsh ; exécutez les blocs d'aide multi-lignes sous bash -lc (ou sauvegardez-les comme un script .sh avec un shebang Bash) avant d'utiliser json_encode_file / chorus_check_response.


§1. Détection — déjà effectuée au SessionStart

Le hook SessionStart du plugin Chorus (hooks/on-session-start.sh) calcule CHORUS_OPENSPEC_ACTIVE une seule fois à l'ouverture de la session et écrit une section ## OpenSpec Mode dans le contexte du message développeur. La valeur de CHORUS_OPENSPEC_ACTIVE est 1 uniquement quand les trois conditions suivantes sont vraies :

  1. CHORUS_OPENSPEC_MODE n'est pas défini sur off (l'exclusion explicite l'emporte).
  2. La racine du projet contient un répertoire openspec/ (c'est-à-dire que quelqu'un a lancé openspec init ici).
  3. La CLI openspec est dans PATH.

Les deux signaux (2) et (3) sont requis car le chemin d'authoring OpenSpec a besoin du répertoire de travail et de la CLI — avoir l'un sans l'autre laisse le workflow non-exécutable. Si le signal (2) tient mais (3) ne tient pas, le hook SessionStart affiche un conseil « Repo OpenSpec détecté — installer avec : npm i -g @fission-ai/openspec » à l'utilisateur ; l'agent doit le transmettre s'il est demandé plutôt que de choisir silencieusement la forme libre.

Comment lire la valeur

Vous devriez déjà voir quelque chose comme ceci dans votre contexte (cherchez la section ## OpenSpec Mode près du haut du message développeur) :

## OpenSpec Mode

CHORUS_OPENSPEC_ACTIVE=1 (openspec/ directory + openspec CLI both present)

ou :

## OpenSpec Mode

CHORUS_OPENSPEC_ACTIVE=0 (no openspec/ directory at /path/to/repo/openspec)

Branchez-vous :

  • CHORUS_OPENSPEC_ACTIVE=1 → suivez §3 (authoring OpenSpec).
  • CHORUS_OPENSPEC_ACTIVE=0 → revenez au chemin de forme libre de la compétence appelante. Ne scaffoldez pas openspec/changes/. N'ajoutez pas la ligne de slug à la description de la proposal.

Fallback manuel

Si vous êtes dans un sous-agent qui n'a pas vu le contexte SessionStart (par exemple, créé en milieu de session sans contexte parent transmis), reconstruisez la valeur vous-même avec les trois mêmes vérifications — les hooks Codex s'exécutent depuis $PWD, donc utilisez cela comme sonde de racine du projet :

if [ "${CHORUS_OPENSPEC_MODE:-}" = "off" ]; then
  CHORUS_OPENSPEC_ACTIVE=0
elif [ ! -d "$PWD/openspec" ]; then
  CHORUS_OPENSPEC_ACTIVE=0
elif ! openspec --version >/dev/null 2>&1; then
  CHORUS_OPENSPEC_ACTIVE=0
else
  CHORUS_OPENSPEC_ACTIVE=1
fi

Utilisez ceci uniquement quand le contexte SessionStart est vraiment indisponible — dupliquer la détection est inefficace quand le hook l'a déjà calculée.


§2. Configuration du wrapper et règles non-négociables

Les deux sont appliquées au moment de la revue. Les deux ont causé des incidents dans les versions précédentes.

2.1 Résoudre le chemin du wrapper Codex

Définissez ceci une fois avant le premier appel du wrapper. Ne demandez pas à l'utilisateur d'ajouter les scripts du plugin à PATH ; Codex ne le fait pas automatiquement.

resolve_chorus_plugin_dir() {
  if [[ -n "${CHORUS_PLUGIN_DIR:-}" && -x "$CHORUS_PLUGIN_DIR/hooks/chorus-mcp-call.sh" ]]; then
    printf '%s\n' "$CHORUS_PLUGIN_DIR"
    return 0
  fi
  if [[ -n "${PLUGIN_ROOT:-}" && -x "$PLUGIN_ROOT/hooks/chorus-mcp-call.sh" ]]; then
    printf '%s\n' "$PLUGIN_ROOT"
    return 0
  fi
  if [[ -n "${CLAUDE_PLUGIN_ROOT:-}" && -x "$CLAUDE_PLUGIN_ROOT/hooks/chorus-mcp-call.sh" ]]; then
    printf '%s\n' "$CLAUDE_PLUGIN_ROOT"
    return 0
  fi

  local _codex_home="${CODEX_HOME:-$HOME/.codex}"
  local _candidate
  _candidate=$(
    find "$_codex_home/plugins/cache" -path '*/hooks/chorus-mcp-call.sh' -type f 2>/dev/null | sort | tail -n 1
  )
  if [[ -n "$_candidate" ]]; then
    dirname "$(dirname "$_candidate")"
    return 0
  fi

  return 1
}

CHORUS_PLUGIN_DIR="$(resolve_chorus_plugin_dir)" || {
  echo "Unable to locate Chorus Codex plugin root; cannot call chorus-mcp-call.sh" >&2
  exit 1
}
API="$CHORUS_PLUGIN_DIR/hooks/chorus-mcp-call.sh"

Règle 1 — Miroiter via le wrapper, ne jamais retaper le contenu du document depuis la sortie agent

Les appels de mirage document/brouillon (chorus_pm_add_document_draft, chorus_pm_update_document_draft, chorus_pm_update_document) DOIVENT passer par :

"$API" <TOOL_NAME> "$PAYLOAD"

avec $PAYLOAD construit avec json_encode_file (défini en §3.4). Appeler ces outils directement depuis le harnais MCP de Codex avec un champ content tapé à la main est une violation de protocole pour le mode OpenSpec et échouera à la revue. Raisons :

  1. Coût en jetons. Retaper un corps markdown multi-millier-ligne via le modèle brûle des jetons entrée + sortie pour chaque brouillon. Le wrapper fait passer des octets via jq -Rs '.' — le contenu n'entre jamais dans le contexte du modèle. Un mirage de proposal typique 3-doc via le script coûte environ zéro jeton-contenu ; via MCP direct, cela coûte routinièrement 20 k+.
  2. Égalité d'octets. jq -Rs '.' est un encodeur fidèle d'octets : barres obliques inverses, guillemets, sauts de ligne, contenu des délimiteurs de code, caractères de largeur nulle survivent tous. La réémission du modèle a un taux d'échec non nul sur la longue syntaxe markdown — l'alignement du tableau dérive, les échappements de délimiteur se font « corriger », les URLs longues s'enroulent. La garantie d'égalité d'octets (modulo \n final) tient uniquement sur le chemin du wrapper.
  3. Source unique de vérité. Avec le wrapper, le openspec/changes/<slug>/*.md local est autoritaire et Chorus est un mirage. Avec la retape d'agent, l'autorité se divise entre le fichier local et ce que le modèle a heureusement produit — une future diff ne peut pas dire lequel est correct.

Règle 2 — S'arrêter sur erreur via chorus_check_response

Chaque appel du wrapper doit vérifier trois signaux : code de sortie du wrapper, "error": dans le corps, corps vide. Un RC=$? nu est insuffisant pour la même raison de bogue du wrapper décrite en §6 — continuez à utiliser l'aide même si le chorus-mcp-call.sh de Codex diffère légèrement en implémentation du chorus-api.sh de Claude Code.


§3. Authoring en mode OpenSpec

3.1 Choisir un slug

openspec/changes/<slug>/ est le dossier de changement local. Le slug doit être :

  • kebab-case (add-export-csv, pas addExportCsv ou add_export_csv),
  • dérivé du titre de l'Idée source,
  • unique dans openspec/changes/.

Enregistrez-le pour les étapes ultérieures :

SLUG="add-export-csv"

3.2 Scaffolder le dossier de changement

openspec new change "$SLUG" --description "<résumé d'une ligne de l'idée>"

Ceci crée openspec/changes/$SLUG/ avec README.md et .openspec.yaml. Puis authoriez à la main :

Fichier local Objectif Miroité comme Document.type
proposal.md Pourquoi + Quoi change + Capacités + Impact prd
design.md Architecture, contrats, risques tech_design
specs/<capability>/spec.md Spec delta (## ADDED Requirements + Scénarios) spec (un brouillon par capacité)
tasks.md Liste de tâches OpenSpec (non mirorité — les brouillons de tâche Chorus sont source de vérité)

Utilisez openspec instructions <artifact> --change "$SLUG" (artifacts: proposal, specs, design, tasks) pour les modèles.

3.3 Forme de fichier spec (vérifiée par openspec instructions specs)

Une spec delta liste un ou plusieurs en-têtes de bloc — ## ADDED Requirements, ## MODIFIED Requirements, ## REMOVED Requirements, ## RENAMED Requirements — et dans chacun, des entrées ### Requirement:. Mélangez librement dans le même fichier ; incluez uniquement les blocs que vous avez réellement besoin.

## ADDED Requirements

Ajouter une Requirement toute neuve à la spec long-terme.

## ADDED Requirements

### Requirement: <nom>
<texte requirement — utilisez SHALL / MUST pour le comportement normatif>

#### Scenario: <nom>
- **WHEN** <condition>
- **THEN** <résultat attendu>

## MODIFIED Requirements

Remplacement de bloc entier, pas fusion. Tout ce que vous écrivez ici remplace complètement la Requirement de même nom existante dans la spec long-terme — titre, description et tous les scénarios. L'écrire à moitié supprime le reste.

## MODIFIED Requirements

### Requirement: <nom existant>
<texte requirement complet mis à jour>

#### Scenario: <nom>
- **WHEN** <condition>
- **THEN** <résultat attendu>

#### Scenario: <autre nom>
- **WHEN** <condition>
- **THEN** <résultat attendu>

Incluez toujours chaque scénario que vous voulez que la spec post-archive ait, même ceux qui étaient déjà présents et inchangés.

## REMOVED Requirements

Supprimez une Requirement de la spec long-terme. Le bloc sous l'en-tête n'est que le(s) nom(s) de requirement que vous supprimez — pas besoin de scénarios.

## REMOVED Requirements

### Requirement: <nom existant>

## RENAMED Requirements

Renommez le titre d'une Requirement. Le corps et les scénarios sont préservés tel-quel dans la spec long-terme ; utilisez MODIFIED à la place si vous avez besoin de changer autre chose que le titre.

## RENAMED Requirements

### Requirement: <ancien nom> -> <nouveau nom>

Règles de formatage critiques (vérifiées) :

  • Les scénarios DOIVENT utiliser exactement 4 dièses (#### Scenario:). 3 dièses ou une liste à puces échouent silencieusement la validation.
  • Chaque ### Requirement: sous ADDED ou MODIFIED DOIT avoir au moins un #### Scenario:.
  • Les blocs MODIFIED DOIVENT inclure le contenu mis à jour complet — ils écrasent, ne patché pas.
  • Utilisez SHALL / MUST pour les requirements normatives ; évitez should / may.
  • La fusion dans openspec/specs/<capability>/spec.md se produit au moment de openspec archive (§3.9), pas au moment de la proposal. Tandis que la proposal est en vol, Chorus ne voit que le fichier delta comme un spec Document — il n'y a pas d'état demi-fusionné pour que la compétence raisonne dessus.

Optionnel :

openspec validate "$SLUG"

3.4 Aide : json_encode_file

Définissez une fois en haut de la session d'authoring. Avec jq disponible, il fait passer le fichier dans une chaîne JSON ; le fallback correspond à l'échappement du wrapper Codex quand jq est manquant.

json_encode_file() {
  local _file_path="$1"
  if command -v jq >/dev/null 2>&1; then
    jq -Rs '.' < "$_file_path"
  else
    local _content
    _content=$(cat "$_file_path")
    _content=${_content//\\/\\\\}
    _content=${_content//\"/\\\"}
    _content=${_content//$'\n'/\\n}
    printf '"%s"' "$_content"
  fi
}

Aller-retour : le backend Chorus ajoute un \n unique au contenu brouillon à l'écriture, donc le serveur content est octet-égal modulo un saut de ligne final. Les relecteurs diffant fichier local vs serveur doivent ignorer cet octet unique.

3.5 Créer le conteneur de proposal avec la ligne de provenance du slug

Utilisez l'outil MCP chorus_pm_create_proposal régulier (aucun wrapper requis pour cet appel unique — la description est courte, la version émise par le modèle est fine). La description doit porter exactement une ligne :

OpenSpec change slug: <slug>
  • sur sa propre ligne (aucun autre texte sur cette ligne),
  • préfixe littéral OpenSpec change slug: (O majuscule, S majuscule, espace unique après deux-points),
  • aucune ponctuation finale,
  • la valeur correspond au slug passé à openspec new change.

Cette ligne est greppable-machine par les futures exécutions de cette compétence et par le déclencheur d'archive §3.9.

3.6 Miroiter chaque brouillon de document via le wrapper

Rappel Règle 1 : ces appels passent par chorus-mcp-call.sh, pas par MCP direct. L'agent ne doit pas retaper le corps du document.

Définissez le chemin du wrapper depuis §2.1 et l'aide halt-on-error depuis §6 une fois en haut, puis lancez un appel par fichier. Notez la signature de wrapper Codex deux-arguments : <TOOL_NAME> <JSON_ARGUMENTS> — il n'y a pas de sous-commande mcp-tool.

# Brouillon PRD
CONTENT=$(json_encode_file "openspec/changes/$SLUG/proposal.md")
PAYLOAD=$(cat <<JSON
{
  "proposalUuid": "$PROPOSAL_UUID",
  "type": "prd",
  "title": "PRD: $HUMAN_TITLE",
  "content": $CONTENT
}
JSON
)
RESULT=$("$API" chorus_pm_add_document_draft "$PAYLOAD")
RC=$?
chorus_check_response "chorus_pm_add_document_draft (prd)" "$RC" "$RESULT"
PRD_DRAFT_UUID=$(printf '%s' "$RESULT" | grep -o '"draftUuid"[[:space:]]*:[[:space:]]*"[^"]*"' | head -1 | sed 's/.*"\([^"]*\)"$/\1/')

Répétez avec type: "tech_design" pour design.md, et un appel par capacité avec type: "spec" pour chaque specs/<capability>/spec.md. Ne miraitez pas tasks.md — les brouillons de tâche Chorus (créés via l'outil MCP chorus_pm_add_task_draft, aucun wrapper requis) sont la source de vérité pour les tâches.

Pourquoi l'analyse utilise printf '%s' "$RESULT" | grep et non echo "$RESULT" | jq : echo interprète les séquences d'échappement de barres obliques inverses à l'intérieur de la JSON capturée, transformant \n intégré en un saut de ligne réel. jq avorte alors avec Invalid string: control characters from U+0000 through U+001F must be escaped. printf '%s' émet les octets capturés verbatim. Le même motif s'applique à toute l'analyse de résultat du wrapper dans cette compétence.

3.7 Éditer un brouillon après le premier mirage

Les modifications de fichiers locaux se propagent via chorus_pm_update_document_draft — le même wrapper, le même json_encode_file, la même vérification halt.

CONTENT=$(json_encode_file "openspec/changes/$SLUG/proposal.md")
PAYLOAD=$(cat <<JSON
{
  "proposalUuid": "$PROPOSAL_UUID",
  "draftUuid": "$PRD_DRAFT_UUID",
  "content": $CONTENT
}
JSON
)
RESULT=$("$API" chorus_pm_update_document_draft "$PAYLOAD")
RC=$?
chorus_check_response "chorus_pm_update_document_draft" "$RC" "$RESULT"

3.8 Éditer un Document après approbation de la proposal

Une fois la proposal approuvée, les brouillons se matérialisent en Documents avec leurs propres UUID. Pour garder openspec/changes/$SLUG/ et le Document Chorus synchronisés, miroitez les éditions de fichiers via chorus_pm_update_document :

CONTENT=$(json_encode_file "openspec/changes/$SLUG/specs/<capability>/spec.md")
PAYLOAD=$(cat <<JSON
{
  "documentUuid": "$SPEC_DOCUMENT_UUID",
  "content": $CONTENT
}
JSON
)
RESULT=$("$API" chorus_pm_update_document "$PAYLOAD")
RC=$?
chorus_check_response "chorus_pm_update_document" "$RC" "$RESULT"

Pour re-dériver $SPEC_DOCUMENT_UUID depuis un shell frais, cherchez-le via chorus_get_documents pour le projet de la proposal et filtrez par title + type. Re-dérivez $SLUG en greppant la description de la proposal pour ^OpenSpec change slug:.

3.9 Archiver après la dernière tâche vérifiée

Quand la DERNIÈRE tâche d'une idée en mode OpenSpec est admin-vérifiée via chorus_admin_verify_task, le hook PostToolUse du plugin (hooks/on-post-verify-task.sh) injecte un rappel additionalContext contenant la sous-chaîne littérale openspec archive <slug> pour que vous puissiez agir sans relire le slug.

Le hook est en lecture seule ; vous (l'agent) effectuez l'archive :

  1. Exécutez l'archive localement. Utilisez --yes pour le mode non-interactif. NE PASSEZ PAS --skip-specs (annule le mirage-retour) ou --no-validate (laisse les deltas malformés corrompre les specs cumulatives).

    openspec archive "$SLUG" --yes

    Ceci déplace openspec/changes/$SLUG/ sous openspec/changes/archive/<date>-<slug>/ et émet/met à jour openspec/specs/<capability>/spec.md pour chaque capacité. (Lancez openspec archive --help contre votre version installée pour confirmer l'ensemble d'indicateurs actuel — les indicateurs peuvent dériver entre les versions.)

  2. Miroitez chaque openspec/specs/<capability>/spec.md mis à jour vers le Document Chorus post-approbation correspondant (contrat §3.8). chorus_get_documents ne supporte que les filtres serveur projectUuid + type ; filtrez par titre côté client. Un appel chorus_pm_update_document par capacité.

  3. Arrêtez-vous sur toute erreur de openspec archive ou chorus_pm_update_document. Imprimez stderr verbatim, postez un commentaire sur la proposal enregistrant l'échec (chorus_add_comment avec targetType: "proposal", targetUuid: <proposalUuid>), puis arrêtez. Aucune tentative. Correspond au §6 « pas d'erreurs silencieuses ». (Commentez sur la proposal, non l'idée : l'échec est dans l'archivage des specs dérivées de proposal, et les proposals peuvent être inputType: "document" sans idée attachée.)

  4. Confirmez le succès. Lister les fichiers openspec/specs/<capability>/spec.md et vérifiez qu'ils font un aller-retour octet-égal (modulo saut de ligne final) avec leurs équivalents Chorus Document.

Opt-in strict : si la tâche vérifiée n'est pas la dernière de son idée, OU la description de la proposal ne porte pas la ligne OpenSpec change slug: <slug>, OU le shell local n'a pas la CLI openspec, le hook sort 0 silencieusement et aucun rappel d'archive n'est injecté. Le comportement de forme libre existant est préservé.


§4. Authoring fallback (sans openspec)

Quand la détection met l'agent en mode fallback (CHORUS_OPENSPEC_ACTIVE=0), cette compétence est un no-op. Revenez au chemin de forme libre de la compétence appelante :

  • Aucun dossier openspec/changes/ n'est créé ou référencé.
  • Aucune ligne OpenSpec change slug: … n'est ajoutée à la description de la proposal.
  • Les brouillons de document sont authored via des appels MCP directs chorus_pm_add_document_draft avec content en ligne — le même qu'avant cette compétence existait.
  • La Règle 1 (mirage wrapper-uniquement) ne s'applique pas — il n'y a pas de source de vérité du fichier local.
  • Le hook d'archive §3.9 ne fait rien (pas de slug → sortie silencieuse).

§5. Mappage de type de document (tableau de référence)

Fichier local Chorus Document.type Miroité ?
openspec/changes/<slug>/proposal.md prd oui
openspec/changes/<slug>/design.md tech_design oui
openspec/changes/<slug>/specs/<capability>/spec.md spec oui (un brouillon par capacité)
openspec/changes/<slug>/tasks.md (non mappé) non — les brouillons de tâche Chorus sont source de vérité

prd, tech_design, spec sont des valeurs Document.type valides pré-existantes — aucun changement de schéma requis.


§6. Visibilité des défaillances — l'aide chorus_check_response

Il existe un cas limite de wrapper connu partagé avec la variante Claude Code : quand le serveur retourne HTTP 4xx (par exemple 401 d'une mauvaise CHORUS_API_KEY), le filtre jq interne du wrapper peut produire un stdout vide et quitter 0. Une vérification RC=$? nu n's'arrêterait pas sur ceci — le mode de défaillance runtime le plus commun serait invisible.

Définissez cette aide une fois en haut de la session d'authoring et utilisez-la après chaque appel du wrapper :

chorus_check_response() {
  local _tool="$1"
  local _rc="$2"
  local _body="$3"
  local _has_error=0
  local _is_empty=0

  local _trimmed
  _trimmed=$(printf '%s' "$_body" | tr -d ' \t\n\r')
  [ -z "$_trimmed" ] && _is_empty=1

  if [ "$_is_empty" -eq 0 ]; then
    if command -v jq >/dev/null 2>&1; then
      if printf '%s' "$_body" | jq -e 'try ([.. | objects | has("error")] | any) catch false' >/dev/null 2>&1; then
        _has_error=1
      fi
    else
      printf '%s' "$_body" | grep -qE '"error"[[:space:]]*:' && _has_error=1
    fi
  fi

  if [ "$_rc" -ne 0 ] || [ "$_has_error" -eq 1 ] || [ "$_is_empty" -eq 1 ]; then
    echo "ERROR: $_tool failed (exit=$_rc, error_in_body=$_has_error, empty_body=$_is_empty)" >&2
    echo "Output: $_body" >&2
    [ "$_rc" -ne 0 ] && exit "$_rc" || exit 1
  fi
}

Anti-patterns — ne pas :

  • S'effondrer en || true.
  • Rediriger stderr vers /dev/null.
  • Enterrer l'appel du wrapper à l'intérieur d'un pipeline (masque $?).
  • Sauter la capture de $RESULT dans une variable ; l'aide a besoin du corps.
  • Utiliser uniquement if [ "$RC" -ne 0 ]; then ... — cela manque le chemin d'erreur HTTP.

Forme minimale du site d'appel (Codex) :

RESULT=$("$API" <nom_outil> "$PAYLOAD")
RC=$?
chorus_check_response "<nom_outil>" "$RC" "$RESULT"
# ...si nous arrivons ici, l'appel a réussi ; analysez RESULT et continuez.

Ceci est la politique à l'échelle du projet : pas d'erreurs silencieuses.


§7. Liste de contrôle de référence rapide

Quand invoqué depuis une compétence d'étape (proposal / develop / yolo) :

  1. Lisez CHORUS_OPENSPEC_ACTIVE depuis la section ## OpenSpec Mode dans le contexte du message développeur SessionStart (§1). Si ce n'est pas là, revenez à la sonde manuelle en §1.
  2. Si CHORUS_OPENSPEC_ACTIVE=0 → revenez au chemin de forme libre du caller (§4).
  3. Sinon : a. Choisissez $SLUG (§3.1). b. openspec new change "$SLUG" (§3.2). c. Authorisez proposal.md, design.md, specs/<capability>/spec.md (§3.2–§3.3). Mélangez les blocs ADDED / MODIFIED / REMOVED / RENAMED selon les besoins ; souvenez-vous que MODIFIED écrase toute la Requirement. d. Optionnel : openspec validate "$SLUG". e. chorus_pm_create_proposal (MCP direct) avec la ligne OpenSpec change slug: $SLUG dans la description (§3.5). f. Définissez les aides $API, json_encode_file, chorus_check_response. g. Pour chaque ligne en §5 avec « oui » — miroitez via "$API" chorus_pm_add_document_draft (§3.6). Enregistrez chaque $DRAFT_UUID. h. Sur tout chorus_check_response échoué — arrêtez, surfacez l'erreur, NE PROCÉDEZ PAS.
  4. Édits avant approbation → §3.7. Édits après approbation → §3.8.
  5. Dernière tâche vérifiée → le hook se déclenche → lancez le flux d'archive §3.9.

Skills similaires