Aller au contenu

Skill — Enforcement (les trois anneaux)

Skill transversal du dispositif. Contrepartie *enforcement** des skills guidance. Hérite de ../Boucle-de-retroaction.md. Les fichiers markdown orientent ; ce skill décrit ce qui contraint vraiment — et où chaque contrainte doit vivre pour qu'on puisse en répondre.*

Principe. L'enforcement n'est fiable que s'il est hors de portée de la chose qu'il contraint. Un blocage qui s'exécute dans le runtime de l'agent est nécessaire mais contournable ; le seul enforcement dont on répond est celui que l'agent ne peut pas atteindre.


Anneau 1 — Permissions (déclaratif, opencode.json)

Chaque outil est allow / ask / deny, avec pattern-matching (dernière règle qui matche l'emporte).

{ "$schema": "https://opencode.ai/config.json",
  "permission": {
    "edit": { "*": "deny", "src/**": "allow", "infra/**": "ask" },
    "bash": { "*": "ask", "git *": "allow", "git push *": "deny", "rm -rf *": "deny", "sudo *": "deny" },
    "external_directory": "deny",
    "skill": { "*": "allow", "dangerous-*": "deny" }
  } }

Levier gouvernance : une organisation pousse des défauts via l'endpoint .well-known/opencode (couche de base que le local ne peut qu'étendre) — politique d'organisation centralisée.

Anneau 2 — Plugin (programmatique, tool.execute.before)

Blocage calculé : inspecter la commande/diff réelle et lever une erreur. C'est le miroir in-agent de gates.md.

import type { Plugin } from "@opencode-ai/plugin"
export const FoyerGates: Plugin = async () => ({
  "tool.execute.before": async (input, output) => {
    const cmd = String(output?.args?.command ?? "")
    if (input.tool === "bash" && /git commit/.test(cmd) && !gatesGreen())
      throw new Error("Gates non vertes — commit bloqué")
    if (input.tool === "edit" && touchesIrreversibleBoundary(output?.args?.filePath))
      throw new Error("Frontière irréversible — validation humaine requise")
  },
  "tool.execute.after": async (input) => auditLog(input)   // registre TRACE
})

Limite connue (à ne jamais oublier) : ces hooks n'interceptent pas les sous-agents lancés via l'outil task — un agent peut donc contourner la politique en déléguant. L'anneau 2 est de la défense en profondeur, pas une garantie.

Anneau 3 — Le substrat (le seul enforcement dont on répond)

Hors de portée de l'agent et de ses sous-agents :

  • Hooks Git pre-commit / pre-push — exécutés par git, refusent le commit/push si les gates échouent. gates.md à sa vraie place. Implémentation : ../tools/gates/workstation/.
  • RED-first hook : bloque si un commit ajoute du code sans test rouge correspondant (L2 discipline validation).
  • Secrets hook : .gitleaks.toml actif, stop sur tout secret détecté (L1 sécurité runtime).
  • Au poste, seul le gate secrets bloque — rapide et hors ligne. Le reste du plancher (SBOM, conteneur) exige un build : il vit en CI, où le même jeu de vérifications est rejoué (DRY).
  • CI/CD — rejoue tout côté plateforme ; rien ne fusionne sans elle.
  • Protection de branche (serveur) — la branche GitFlow source de vérité est protégée par règle de merge ; les points irréversibles ne sont pas un ask mais une règle qu'aucun agent ne franchit.
  • Cowork release — le merge sur main nécessite un rapport de release signé GO par l'humain (L2).
  • Sandbox conteneur/VM — montages read-only, allowlist d'egress réseau, permissions OS.
  • Contexte propreCLAUDE.md / AGENTS.md ≤ 5k tokens (L0).

La règle de placement

Type de contrainte Anneau
Confort, garde-fous courants (rm, push, edit hors scope) 1 (permissions)
Gate calculée (commit sans tests, secret, frontière) 2 (plugin) + miroir en 3
Tout point irréversible (push branche-vérité, migration schéma, tier, destination entité haute-cardinalité) 3 (substrat) obligatoire
Audit / trace 2 (tool.execute.after) + logs substrat

Les permissions font le confort et la défense en profondeur ; la protection de branche et la CI font la garantie. « La parade est l'architecture, pas le contrat. »


Dérivé du Manifeste Maury (CC BY-SA 4.0).