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.tomlactif, 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
askmais une règle qu'aucun agent ne franchit. - Cowork release — le merge sur
mainné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 propre —
CLAUDE.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).