Aller au contenu

Invariant de sécurité

La règle en une phrase

sdlc/sdlc.js est le seul acteur autorisé à modifier .product/sdlc-registry.json. Toute autre modification — directe, en masse, par un agent IA, par un humain pressé — est une violation.

Cet invariant est la condition de fiabilité de tout le kit. S'il tombe, la machine d'états devient fiction, la traçabilité devient narrative, les portes deviennent suggestions.

Pourquoi c'est critique

Le registre sdlc-registry.json est la mémoire longue du projet : il encode quel Business Epic est en cadrage, quelle Feature est livrée, quel Récit est en test. Cette mémoire doit respecter trois propriétés :

  1. Atomicité : une transition franchit une porte entièrement, ou pas du tout. Pas de demi-état.
  2. Traçabilité : chaque transition laisse un événement markdown + un commit git. Impossible de faire disparaître une décision.
  3. Conformité : chaque transition respecte les préconditions et invariants de sa porte. Pas de court-circuit.

Si un agent IA pouvait éditer le registre directement, ces trois propriétés tombent en silence. La CLI garantit les trois en un seul acte.

Comment l'invariant est protégé

Couche 1 — Convention documentée

La CLI elle-même porte la règle en commentaire d'en-tête :

// ╔══════════════════════════════════════════════════════════════════════════╗
// ║  CE SCRIPT EST LE SEUL POINT DE MODIFICATION D'ÉTAT.                   ║
// ║  Tout changement direct dans sdlc-registry.json est une violation.     ║
// ╚══════════════════════════════════════════════════════════════════════════╝

Le README.md, le GUIDE.html, et cette documentation répètent la règle. Aucun agent IA ne peut l'ignorer par omission — elle est injectée dès le SessionStart.

Couche 2 — Interdiction dans les prompts IA

Les prompts des skills (.github/prompts/*.prompt.md) et le prompt de l'agent coach-emergent.agent.md interdisent l'écriture directe dans .product/sdlc-registry.json. L'agent propose au mieux un node sdlc/sdlc.js move <id> <STATUT> à l'utilisateur.

Couche 3 — Hook git pre-commit (couche exécutive)

Un hook git pre-commit installé par scripts/bootstrap.ps1 refuse tout commit où sdlc-registry.json est modifié sans être accompagné d'un événement dans .product/.events/ écrit le même jour. Un humain ou agent qui aurait édité le registre à la main serait donc bloqué au commit.

flowchart LR
    A[Agent IA<br/>ou humain] -->|propose| B{Modif<br/>sdlc-registry.json ?}
    B -->|Non| OK[commit OK]
    B -->|Oui| C{Événement<br/>.product/.events/<br/>du jour ?}
    C -->|Oui| D{Commit<br/>par sdlc/sdlc.js ?}
    C -->|Non| REJECT1[✗ pre-commit bloque]
    D -->|Oui| OK
    D -->|Non| REJECT2[✗ pre-commit bloque]
    REJECT1 -.-> FIX[Passer par la CLI]
    REJECT2 -.-> FIX

Couche 4 — Signal visuel dans l'historique

Chaque transition par la CLI produit un commit dont le message suit le pattern :

chore(sdlc): <ID> <FROM>→<TO> [<PORTE>] — <message optionnel>

Une transition qui ne suivrait pas ce pattern (commit manuel, amende, force-push) est immédiatement détectable dans git log. La revue de PR devient une défense supplémentaire.

Les trois règles dérivées

Règle 1 — Un seul acteur autorisé

Acteur Lecture registre Écriture registre
Agent IA (Copilot) ✓ ✗
Humain (éditeur de texte) ✓ ✗
Script PowerShell (hook) ✓ ✗
node sdlc/sdlc.js ✓ ✓ (seul)

Règle 2 — Pas de transition sans événement

Toute ligne modifiée dans sdlc-registry.json doit avoir un pendant dans .product/.events/YYYY-MM-DD-sdlc-<id>-<statut>.md. La CLI écrit les deux ensemble. Le hook pre-commit vérifie la parité.

Règle 3 — Pas d'événement sans checklist

Chaque transition qui franchit une porte avec preconditions exige une confirmation humaine explicite (prompt oui/non) ou une invocation de --force tracée dans l'historique avec forced: true. L'IA ne peut pas répondre oui à la place de l'humain — elle n'exécute jamais node sdlc/sdlc.js move de son propre chef.

Décisions structurantes

Ce que cet invariant n'empêche pas

  • Qu'un humain passe --force pour franchir une porte dont les préconditions sont imparfaites. L'historique le trace (forced: true), mais l'acte est permis car tracé.
  • Que l'IA propose un mauvais move. C'est à l'humain de refuser avant le prompt oui/non.
  • Que le registre soit supprimé ou corrompu manuellement. Dans ce cas, validate remonte l'incohérence au prochain run.

L'invariant protège la structure du flux, pas la sagesse des décisions. Celle-ci reste humaine.

Voir aussi : CLI sdlc · Les 4 portes · Hooks.