From 0f306b2953ad266d6c7a35efb93c3f8e8ba6c885 Mon Sep 17 00:00:00 2001 From: Constantin Jais <74049135+constantin-jais@users.noreply.github.com> Date: Sat, 24 Oct 2026 14:50:30 +0200 Subject: [PATCH 1/3] docs: confine every review pass to its worktree as CA-32, assigned to WP-4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Owner decisions of 2026-10-24, verbatim: - R-13: "Linux netns + nftables (Recommandé)". - K1: "6e condition, deux pilotes (Recommandé)". The second-family reviews of WP-3 (Kimi K3, GLM 5.3, on #27) both rated as major a channel that the letter of INV-a does not cover. The pi `read` tool resolves absolute and `~` paths outside the pass directory (pi 1.4.0, dist/core/tools/path-utils.js:38-44), and the verdict, free text included, is published under docs/reviews/. A reviewed commit can therefore get a host file copied into a public artefact. No deterministic control over the verdict can tell a copied secret from a comment, so only a boundary enforced outside pi closes the channel. Requirements specification: - CA-32: the pass reads nothing outside its detached worktree, enforced by the R-13 mechanism, with no provider secret readable from inside the pass (neither a mounted file nor an environment variable that a process pseudo-file would expose). Both drivers refuse real passes until it holds. Red vector `pass-read-confined`, with a neutralisation. - R-16 records the channel and the decision. R-13 records the mechanism and the options rejected that day. - CA-26: the profile also carries --no-extensions and --no-approve, with their measured reason (an extension can replace a built-in tool by name, dist/core/agent-session.js:2893-2894). The validation command points at fanout-core.ts, because WP-2 moved the pi spawn out of fanout.ts. The "value at the commit read" now names 7644bfd and stays red there. - Threat table: one row for the channel. PRD: the WP-4 row now covers CA-32. Its scope gains tools/review/confinement/** (the R-13 mechanism) and tools/review/*.ts (both drivers must run their passes under it). The WP-4 issue will cite the commit of this change. Norm reader: RESULT: pass. Exempt: docs-only Signed-off-by: Constantin Jais <74049135+constantin-jais@users.noreply.github.com> --- docs/cahier-des-charges.md | 22 +++++++++++++++------- docs/prd.md | 18 ++++++++++++------ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/docs/cahier-des-charges.md b/docs/cahier-des-charges.md index 1c151e4..da485c9 100644 --- a/docs/cahier-des-charges.md +++ b/docs/cahier-des-charges.md @@ -4,12 +4,13 @@ Maturité : prototype Provenance : ce document reprend la spécification de mission `specs/execution-continuity-evaluator/cahier-des-charges.md` de l'analyse du 2026-10-14. Changements : -les sections 1.3, 1.4, 4.1, 4.2, 5 (EF-10), 6 (ENF-8), 7.3, 8.2, 8.4, 12 (risques R-11 à R-15) et la +les sections 1.3, 1.4, 4.1, 4.2, 5 (EF-10), 6 (ENF-8), 7.3, 8.2, 8.4, 12 (risques R-11 à R-16) et la table de la section 13, qui portent la décision du propriétaire du 2026-10-23 (ADR-0058 : `tools/review` premier consommateur LangGraph JS de la flotte, sous ADR-0045) ; la comparaison section à section avec la mission, relevée le 2026-10-23, montre en plus des écarts dans les sections 1.1, 1.5, 3, 7.1, 8.1, 8.3, 10, 11.1 et 11.2, non classés ici entre décision LangGraph et réécriture de forme ; -les critères CA-18 à CA-31, ajoutés à la suite des dix-sept critères de la mission, dont le texte est +les critères CA-18 à CA-32, ajoutés à la suite des dix-sept critères de la mission (CA-32 et R-16 le +2026-10-24, par décision du propriétaire), dont le texte est inchangé aux réécritures de forme près ; et ces réécritures, exigées par la norme des paquets de travail de `libre-ai/project-governance` (`docs/specifications/WORK-PACKAGES.md`) : chaque paramètre écrit entre chevrons y est écrit en capitales (`ORG/DEPOT`, `DEPOT-CONSOMMATEUR`, `REV`, @@ -190,7 +191,7 @@ déclaré au commit lu (Q11). `tools/review` lance `pi` seulement (`fanout.ts:11 admis par ADR-0058 : graphe rôles → lots → vérification → agrégation, passes `pi` en nœuds-outils, contrat observable inchangé (plan JSON, verdicts, ajout seul, nettoyage `finally`), six conditions d'entrée d'ADR-0058 section 3, INV-a à INV-c d'ADR-0045 avec leurs vecteurs rouges, preuve de - retrait d'ADR-0058 section 4 (CA-18 à CA-31). + retrait d'ADR-0058 section 4, passes confinées à leur worktree (CA-18 à CA-32). - La preuve de performance de référence (banc). ### 4.2 Hors périmètre (non-objectifs, `project.v1.yaml:34-41`, repris sans réécriture) @@ -393,7 +394,11 @@ mémoire sont internes à l'invocation et n'apparaissent dans aucune entrée ni fiables écrites pour une lecture humaine), et les vecteurs rouges 1 à 3 d'ADR-0045 (Conséquences), chacun prouvé rouge sans son contrôle, et le critère CA-31 pour le vecteur 4 (suite d'attaques adaptatives, taux d'utilité et taux d'attaque réussie rapportés ensemble ; - arbitrage du propriétaire du 2026-10-23 : ajouté à WP-4, R-15). + arbitrage du propriétaire du 2026-10-23 : ajouté à WP-4, R-15). La lettre d'INV-a laisse un canal + ouvert : `read` lit hors du worktree de la passe, et le verdict, texte libre compris, est publié + sous `docs/reviews/` ; réponse : le critère CA-32 (passe confinée à son worktree par une frontière + appliquée hors de `pi`), sixième condition d'activation, appliquée aux deux pilotes (décision du + propriétaire du 2026-10-24, R-16). - Vérification Ed25519 : hors crate par choix de frontière (`tool_observation.rs:19-26`) ; le consommateur fournit le `HarnessSignatureVerifier` et lie `signing_key_id` à l'attestation de harness du run. Conséquence mesurée : le gate `check:capabilities` refuse toute section de @@ -431,6 +436,7 @@ deux derniers avis visent donc le graphe de `tools/review`, et ADR-0058 ne les l | désérialisation non sûre (GHSA-j87f-x5h5-gr75, GHSA-r399-636x-v7f6) | K3 | versions corrigées ; aucun octet venu d'ailleurs que du processus de l'orchestrateur n'est relu par les sérialiseurs du paquet ; aucun checkpoint persistant | CA-18, CA-23, CA-25 | | espaces de noms de stores non isolés (GHSA-8p8h-xj5r-m5wf) | I-09 | aucun paquet `@langchain/langgraph-checkpoint-postgres`, `-redis`, `-mongodb`, `-sqlite` ni store ; checkpoint en mémoire le temps d'une invocation au plus | CA-23 | | télémétrie LangSmith activée par variable d'environnement | ADR-0032 D4, ADR-0042 section 4 | gate textuel sur `LANGSMITH_*` et les alias `LANGCHAIN_*` ; politique réseau sans hôte de l'éditeur ; vecteur rouge | CA-21, CA-22 | +| fichier de l'hôte lu par une passe et recopié dans le verdict publié | I-32 | passe confinée à son worktree par une frontière appliquée hors de `pi`, aucun secret de fournisseur lisible depuis la passe ; sixième condition d'activation, deux pilotes | CA-32 | | dépendance non épinglée, téléchargement à l'exécution, licence hors politique | ADR-0032 D5, I-11 | versions exactes, verrou, âge de trois jours, clôture des licences | CA-19, CA-20 | | le paquet devient une autorité par glissement (type dans le verdict, graphe qui étend le plan) | ADR-0032 D2, D3 | `review-verdict.v0.1` byte-identique ; graphe opaque ; chemin sans le paquet vivant | CA-25, CA-29 | @@ -438,7 +444,7 @@ deux derniers avis visent donc le graphe de `tools/review`, et ADR-0058 ne les l Numérotation : les numéros CA-1 à CA-11 de la première rédaction sont conservés ; les critères issus de la division de CA-1, CA-3, CA-5, CA-7 et CA-9 portent CA-12 à CA-16, et CA-17 est nouveau à la -mission. Les critères CA-18 à CA-31 sont ajoutés le 2026-10-23 pour le jalon LangGraph de +mission. Les critères CA-18 à CA-31 sont ajoutés le 2026-10-23, et CA-32 le 2026-10-24, pour le jalon LangGraph de `tools/review` (`prd.md` section 9) ; chacun cite la condition ou l'invariant dont il dérive. Les commandes de validation sont relatives à la racine du dépôt ; `.clones/` désigne la composition de flotte de la mission. @@ -468,12 +474,13 @@ flotte de la mission. - CA-23 **Aucune persistance LangGraph externe** (ADR-0058 section 3, condition 5 ; ADR-0032 D4 ; ADR-0034 « Frontière du premier incrément »). `@langchain/langgraph-checkpoint-postgres`, `-redis`, `-mongodb`, `-sqlite` et les stores du paquet n'entrent ni dans `tools/review/package.json` ni dans `tools/review/bun.lock` ; un gate les refuse par nom ; le seul checkpoint admis est en mémoire, créé et détruit dans la même invocation (ADR-0032 D3). Validation : `bun test tools/review/gates` vert (test `persistence-packages`), sortie imprimant le nombre de dépendances examinées dans le manifeste et le verrou ; neutralisation : ajouter `@langchain/langgraph-checkpoint-sqlite` au manifeste dans une copie doit rougir ; `grep -n 'checkpoint' tools/review/*.ts` ne rend que des usages en mémoire ou rien. - CA-24 **Valeur propre mesurée** (ADR-0058 section 3, condition 6 ; ADR-0032 D5 « valeur propre mesurée face à un worker plus petit »). La même tâche est mesurée des deux côtés, avec et sans le paquet, sur le même commit : taille du graphe de dépendances de `tools/review`, temps de démarrage de l'orchestrateur en `--dry-run`, mémoire maximale ; les trois nombres de chaque côté sont relevés verbatim, avec leur commande et leur date, dans `docs/evidence/`, et l'écart est écrit, avec sa date, dans `project.v1.yaml`. Un écart défavorable n'interdit pas l'adoption, décidée par ADR-0058 ; il s'écrit. Validation : un fichier `docs/evidence/` portant les six nombres et les trois commandes (`bun pm ls --all --cwd tools/review` puis `wc -l` ; `/usr/bin/time -l bun tools/review/fanout.ts PLAN-MESURE --dry-run`, temps réel et mémoire maximale résidente) ; PLAN-MESURE est `tools/review/plan.example.json` dont le champ `commit` est remplacé par le commit que rend `git rev-parse HEAD`, écrit dans un fichier hors de l'arbre suivi : le plan d'exemple porte le commit nul (`jq -r .commit tools/review/plan.example.json`), que `fanout.ts` refuse par `git cat-file -e` (`tools/review/fanout.ts:230`) avant la branche `--dry-run` (`:256`), si bien que la commande sur le plan tel quel sort en erreur ; `grep -n 'langgraph' project.v1.yaml` rend une ligne datée portant l'écart ; référence sans le paquet mesurée le 2026-10-23 : 42 lignes de `bun pm ls --all --cwd tools/review` (en-tête compris), à rejouer avant comparaison. - CA-25 **Le paquet n'est pas une autorité** (ADR-0032 D2 et D3, maintenus par ADR-0058 section 2). Aucun type LangGraph, LangChain ou LangSmith n'entre dans le plan, le verdict ni le journal : `reviewVerdictSchema` est byte-identique à sa forme au commit lu et son `$id` reste `review-verdict.v0.1` ; `fanout-core.ts` ne mentionne pas le paquet ; le graphe interne reste opaque (aucun nœud, aucune arête ni aucun état du graphe n'apparaît dans une entrée ou une sortie de l'outil) et ne peut pas étendre le plan ; les sérialiseurs du paquet ne relisent aucun octet venu d'ailleurs que du processus de l'orchestrateur. Validation : `bun run check:review-tools` vert avec un test qui compare le SHA-256 de `JSON.stringify(reviewVerdictSchema)` à la valeur relevée au commit lu ; `grep -c -i 'langgraph' tools/review/fanout-core.ts` rend 0 ; `grep -c -i 'langgraph' tools/review/plan.example.json tools/review/plan.orchestrator-ca44eb3.json docs/reviews/orchestrator/ca44eb3/events.jsonl` rend 0 partout ; neutralisation : un champ ajouté au schéma dans une copie doit rougir le test de SHA-256. -- CA-26 **INV-a : aucune chaîne non fiable devant un modèle qui détient un outil à effet** (ADR-0045 décision 1, `docs/adr/0045-*.md:84` de `project-governance`). Le contenu revu (différences, fichiers du commit, preuve enveloppée) n'entre dans aucun contexte de modèle qui détient un outil dont l'accès est `write`, `execute` ou `network` : chaque passe `pi` est lancée avec un profil d'outils en lecture seule (`pi --tools read,grep,find,ls --no-mcp`, mode documenté par `pi --help` : « Read-only mode (no file modifications possible) »), et l'écriture du verdict dans `docs/reviews/` est faite par l'orchestrateur, composant déterministe, après validation de schéma ; aucune sortie de `pi` n'est relue par un second modèle. Validation : `bun test tools/review/vectors` vert (vecteur 2 d'ADR-0045 : la ligne de commande de chaque passe, capturée, porte le profil lecture seule et aucun outil à effet ; le prompt privilégié n'existe pas : aucun modèle à outil à effet ne reçoit d'octet du contenu revu) ; `grep -n -- '--tools' tools/review/fanout.ts` rend la liste fermée ; neutralisation : retirer `--tools` dans une copie doit rougir ; valeur au commit lu : aucune option `--tools` (`fanout.ts:114-127`), critère rouge aujourd'hui. +- CA-26 **INV-a : aucune chaîne non fiable devant un modèle qui détient un outil à effet** (ADR-0045 décision 1, `docs/adr/0045-*.md:84` de `project-governance`). Le contenu revu (différences, fichiers du commit, preuve enveloppée) n'entre dans aucun contexte de modèle qui détient un outil dont l'accès est `write`, `execute` ou `network` : chaque passe `pi` est lancée avec un profil d'outils en lecture seule (`pi --tools read,grep,find,ls --no-mcp`, mode documenté par `pi --help` : « Read-only mode (no file modifications possible) »), complété par `--no-extensions` et `--no-approve` : l'allowlist ne filtre que des noms, et une extension peut enregistrer un outil sous le nom d'un outil intégré et le remplacer (`pi` 1.4.0, `dist/core/agent-session.js:2893-2894` ; exemple livré : un `read` routé par ssh, `examples/extensions/ssh.ts:128-129`), et les fichiers propres au projet du worktree sont ceux du commit revu, donc non fiables, et l'écriture du verdict dans `docs/reviews/` est faite par l'orchestrateur, composant déterministe, après validation de schéma ; aucune sortie de `pi` n'est relue par un second modèle. Validation : `bun test tools/review/vectors` vert (vecteur 2 d'ADR-0045 : la ligne de commande de chaque passe, capturée, porte le profil lecture seule et aucun outil à effet ; le prompt privilégié n'existe pas : aucun modèle à outil à effet ne reçoit d'octet du contenu revu) ; `grep -n -- '--tools' tools/review/fanout-core.ts` rend la liste fermée ; neutralisation : retirer `--tools`, ou `--no-extensions` seul, dans une copie doit rougir ; valeur au commit lu (`7644bfd`, relevé le 2026-10-24) : aucune option `--tools`, le lancement de `pi` ayant été déplacé par WP-2 de `fanout.ts` à `tools/review/fanout-run.ts:126-138`, critère rouge à ce commit. - CA-27 **INV-b : provenance par valeur, politique sur l'argument** (ADR-0045 décision 1, `:85`). Tout argument d'un effet de l'orchestrateur (chemin du worktree, commit extrait, chemin du verdict, chemin du journal, ligne de commande de `pi`) dérive du plan et du rôle, jamais du contenu revu ni de la sortie du modèle ; la sortie du modèle n'alimente que `extractVerdict` puis `validateVerdict`, et un verdict refusé ne produit aucune écriture hors de l'événement `verdict_rejected`. Validation : `bun test tools/review/vectors` vert (vecteur 1 d'ADR-0045 : un verdict piégé qui porte un chemin de sortie, une commande ou une adresse n'obtient aucun effet, le refus vient de la validation et de la construction des arguments, pas du modèle) ; `bun run check:review-tools` vert sur les tests `buildJobs` et `validateVerdict` ; neutralisation : faire dériver `outputPath` d'un champ du verdict dans une copie doit rougir. - CA-28 **INV-c : sortie de quarantaine à vocabulaire fermé** (ADR-0045 décision 1, `:86`). Le nœud d'agrégation ne lit du verdict que ses champs à vocabulaire fermé : `verdict` (trois valeurs), `commitSha` (40 hexadécimaux), `reviewPassId` et `role` (égalité exacte avec le travail), `mode` (deux valeurs), les comptes de `findings` par gravité ; les textes libres (`title`, `detail`, `residualRisks`, `commands`) sont des données non fiables écrites dans le fichier de verdict pour une lecture humaine, jamais lues par un nœud qui décide ni rendues à un modèle. Validation : `bun test tools/review/vectors` vert (vecteur 3 d'ADR-0045 : un verdict dont `verdict` est un texte hors des trois valeurs échoue à la validation et ne produit aucun appel ni écriture ; un verdict valide dont `detail` porte une consigne n'a aucun effet sur l'agrégation) ; neutralisation : faire lire `detail` par le nœud d'agrégation dans une copie doit rougir. - CA-29 **Preuve de retrait** (ADR-0058 section 4 ; ADR-0032 D5 étapes 8 et 9). Le fan-out sans le paquet reste exécutable dans le dépôt et rend les mêmes résultats : sur PLAN-MESURE (CA-24) et sur le plan historique, c'est-à-dire `tools/review/plan.orchestrator-ca44eb3.json` dont le champ `commit` est remplacé de la même façon (le commit `ca44eb33` qu'il nomme n'est plus dans l'historique de la branche servie : `git rev-list --all` n'en rend aucune occurrence au 2026-10-23), la sortie de `--dry-run` (liste des passes et chemins de sortie) est identique à l'octet entre le chemin LangGraph et le chemin sans le paquet ; sur le journal historique `docs/reviews/orchestrator/ca44eb3/events.jsonl`, `replayPassStatuses` rend le même résultat ; sur les deux verdicts enregistrés de ce dossier, `validateVerdict` rend la même liste d'erreurs (vide). Une campagne réelle rejouée des deux côtés est une mesure versée dans `docs/evidence/`, pas un test de la chaîne requise (R-5 : binaire `pi` et authentification hors manifeste). Validation : `bun run check:review-tools` vert avec un test de parité qui exécute les deux chemins sur les deux plans et le journal ; neutralisation : altérer un chemin de sortie dans le chemin LangGraph d'une copie doit rougir ; le retrait du paquet est alors une pull request qui supprime le chemin LangGraph, ses gates et ses vecteurs, sans migration d'un verdict ni d'un journal. - CA-30 **Fiche à jour** (ADR-0058 section 4, critère de sortie de phase, I-08 ; ADR-0058 section 3 condition 6). `project.v1.yaml` porte, pour `tools/review`, l'adoption du runtime LangGraph avec sa date, l'écart de valeur de CA-24 avec sa date, la preuve de retrait de CA-29 comme critère de sortie de phase, et le non-objectif « faire de LangGraph une autorité » reformulé pour ne plus couvrir la dépendance de l'outillage ; `last_verified_on` est postérieur à cette écriture, et la section statut du README est régénérée depuis la fiche. Validation : `grep -c -i 'langgraph' project.v1.yaml` rend au moins 3 ; `grep -n 'last_verified_on' project.v1.yaml` rend une date postérieure au 2026-10-23 ; `bun run heal:card-status` de `project-governance` ne produit aucune différence (CA-14). - CA-31 **Vecteur 4 d'ADR-0045 : suite d'attaques adaptatives, utilité et attaque rapportées ensemble** (ADR-0045 Conséquences, vecteur 4, `docs/adr/0045-*.md:116` de `project-governance` ; arbitrage du propriétaire du 2026-10-23, R-15). Un corpus versionné sous `tools/review/vectors/adaptive/` réunit des contenus revus hostiles qui cherchent chacun à produire un effet hors de la liste fermée de CA-26 à CA-28 (chemin de sortie, commande, adresse, consigne lue par le nœud d'agrégation, verdict hors vocabulaire) et des contenus bénins de contrôle. Le rejeu applique le corpus aux contrôles déterministes de l'orchestrateur (`buildJobs`, `validateVerdict`, nœud d'agrégation) avec, pour chaque cas, la sortie de modèle enregistrée qui tente l'effet, et imprime sur une seule ligne le taux d'utilité (cas bénins qui rendent le résultat attendu, sur le total des bénins) et le taux d'attaque réussie (cas hostiles qui produisent un effet hors liste, sur le total des hostiles), chacun avec son numérateur et son dénominateur ; une ligne qui n'imprime qu'un des deux taux, ou un dénominateur nul, est un échec. Validation : `bun test tools/review/vectors` vert avec un taux d'attaque réussie de 0 sur le total des hostiles et un taux d'utilité égal à celui du corpus bénin sans les contrôles hostiles ; neutralisation : retirer dans une copie un contrôle de CA-26, CA-27 ou CA-28 doit faire monter le taux d'attaque réussie au-dessus de 0 et rougir le test. Un rejeu avec un modèle réel (AgentDojo ou équivalent) est une mesure versée dans `docs/evidence/`, pas un test de la chaîne requise (R-5) ; les chiffres d'ADR-0045 (77 % contre 84 %, `:77`) sont ceux des auteurs de la méthode, pas une mesure de ce dépôt. Valeur au commit lu : aucun corpus, critère rouge aujourd'hui. +- CA-32 **Passe confinée à son worktree** (décision du propriétaire du 2026-10-24 : sixième condition d'activation, appliquée aux deux pilotes ; prolonge INV-a d'ADR-0045, `docs/adr/0045-*.md:84` de `project-governance`, au canal que sa lettre ne couvre pas ; R-16). Une passe de revue ne lit rien hors du worktree détaché de son commit : l'outil `read` de `pi`, comme `grep`, `find` et `ls`, résout les chemins absolus et `~` hors du répertoire de la passe (`pi` 1.4.0, `dist/core/tools/path-utils.js:38-44` : « Handles ~ expansion and absolute paths »), et le verdict, texte libre compris, est publié sous `docs/reviews/` d'un dépôt public ; un contenu revu qui obtient la copie d'un fichier de l'hôte dans `detail` le publierait. La frontière est appliquée hors du processus de `pi`, par le mécanisme de R-13 (espace de noms Linux, conteneur qui ne monte que le worktree de la passe), jamais par une consigne au modèle ; aucun secret de fournisseur n'est lisible depuis la passe, ni par un fichier monté, ni par une variable d'environnement qu'un pseudo-fichier du processus exposerait. Tant que la condition n'est pas tenue, les deux pilotes, avec et sans le paquet, refusent les passes réelles ; `--dry-run` reste disponible. Validation : `bun test tools/review/vectors` vert (vecteur `pass-read-confined`) : sur chacun des deux pilotes, une passe dont le faux `pi` tente de lire un fichier sentinelle placé hors de son worktree, par chemin absolu, par `~` et par chemin relatif qui remonte, ainsi que l'emplacement du secret du fournisseur, n'obtient aucun octet ; la sortie imprime le mécanisme, les chemins visés et le compte des lectures refusées ; neutralisation : monter le répertoire du sentinelle dans une copie doit faire aboutir la lecture, donc rougir ; `bun run check:review-tools` vert avec un test qui montre, pour chacun des deux pilotes, le refus des passes réelles tant que la condition est fausse. Valeur au commit lu (`7644bfd`, relevé le 2026-10-24) : aucun confinement, aucun refus sur le pilote sans le paquet, critère rouge. ## 10. Critères d'abandon @@ -573,9 +580,10 @@ transitive `@langchain/core` et `@langchain/langgraph-checkpoint`, aux condition | R-10 | Cinq types à `Debug` dérivé portent des identifiants et deux énumérations les portent par référence | `grep -n 'derive(.*Debug' -A2 src/control.rs src/budget.rs` | un diagnostic de l'appelant qui formate l'un de ces types journalise des identifiants de fil ; CA-5 ne les couvre pas, CA-16 les couvre | | R-11 | La valeur de LangGraph sur un orchestrateur de sous-processus est nulle ou négative (M8, H7) | écart de CA-24 défavorable sur les trois nombres | l'écart s'écrit dans la fiche, il n'interdit pas (arbitrage du 2026-10-23) ; le propriétaire relit la décision sur ce relevé ; CA-29 garde le retrait possible | | R-12 | L'échéance ADR-0045 (2026-12-31) arrive avant WP-3 et WP-4 | M9 à 0 vecteur à la date | l'orchestrateur de revues est suspendu (ADR-0045 décision 4), avec ou sans LangGraph ; le passage en lecture seule des passes (CA-26) ne dépend pas du paquet et peut précéder WP-2 | -| R-13 | Le mécanisme réseau qui applique CA-22 n'existe pas sur la machine de forge (aucun plan `execution-plan-body` ne couvre `tools/review` au commit lu) | vecteur `telemetry-egress-refused` sans mécanisme nommé | l'orchestrateur sous LangGraph ne s'active pas (ADR-0058 section 3 condition 4 : « un harness qui ne produit pas ce refus n'active pas le worker ») ; le mécanisme est une décision à prendre avant WP-4 (question fermée dans la pull request du PRD) | +| R-13 | Le mécanisme réseau qui applique CA-22 n'existe pas sur la machine de forge (aucun plan `execution-plan-body` ne couvre `tools/review` au commit lu) | vecteur `telemetry-egress-refused` sans mécanisme nommé | l'orchestrateur sous LangGraph ne s'active pas (ADR-0058 section 3 condition 4 : « un harness qui ne produit pas ce refus n'active pas le worker ») ; décision du propriétaire du 2026-10-24 : espace de noms réseau Linux et règles nftables à compteur, le même en CI (runner `ubuntu-24.04`) et sur la machine de forge, où les campagnes réelles tournent dans un conteneur Linux ; écartés le même jour : Seatbelt sur macOS (refus mesuré, aucun compte natif, `sandbox-exec` déprécié), le harness d'`execution-sandbox` (macOS non pris en charge, Linux non qualifié), le report de CA-22 | | R-14 | Un avis de l'éditeur absent de la base `osv-scanner` | date de base et nombre d'entrées imprimés par CA-18 | le fichier `tools/review/advisories.v1.json` le porte ; l'ajout d'une entrée est une édition revue | | R-15 | Le corpus du vecteur 4 d'ADR-0045 (suite d'attaques adaptatives, taux d'utilité et taux d'attaque réussie rapportés ensemble) est borné par ce que des cas enregistrés peuvent exercer : il prouve les contrôles déterministes, pas la robustesse d'un modèle réel | `grep -n 'AgentDojo' docs/adr/0045-*.md` dans `project-governance` rend la ligne du vecteur ; `grep -n 'CA-31' docs/cahier-des-charges.md` rend le critère | décision du propriétaire du 2026-10-23 : le vecteur est ajouté à WP-4 (CA-31) ; un rejeu avec un modèle réel est une mesure versée dans `docs/evidence/`, jamais un seuil ; M9 compte quatre vecteurs | +| R-16 | L'outil `read` de `pi` n'est pas confiné au worktree de la passe et le verdict, texte libre compris, est publié : un contenu revu peut faire recopier un fichier de l'hôte dans un artefact public (relevé par les relectures de second modèle de WP-3, le 2026-10-24) | condition `pass-read-confined` de `tools/review/fanout-activation.ts` à `satisfied: false` ; vecteur `pass-read-confined` absent | décision du propriétaire du 2026-10-24 : sixième condition d'activation (CA-32), appliquée aux deux pilotes ; aucune campagne réelle de `tools/review` avant WP-4 (la dernière versée est `docs/evidence/2026-08-19-first-real-fanout-campaign.md`) ; le mécanisme est celui de R-13 | ## 13. Table de conformité à SPECIFICATION-STANDARD diff --git a/docs/prd.md b/docs/prd.md index 07673f6..86ace2f 100644 --- a/docs/prd.md +++ b/docs/prd.md @@ -10,7 +10,8 @@ qui déclare le niveau de ce texte (ci-dessus) ; le scénario S6 (section 4), le l'hypothèse H7, le quatrième défaut de la section 2 (ADR-0045), la ligne LangGraph de la section 7, deux phrases de la section 8 (déplacement de `tools/review`, validation du jalon par M8 et M9) et la section 9 (paquets de travail), qui portent la décision du propriétaire du 2026-10-23 (ADR-0058, -premier consommateur LangGraph JS de la flotte, sous ADR-0045) ; et des +premier consommateur LangGraph JS de la flotte, sous ADR-0045) ; la ligne `WP-4` de la section 9 et +trois phrases de son préambule, qui portent la décision du propriétaire du 2026-10-24 (CA-32) ; et des réécritures de forme exigées par la norme des paquets de travail de `libre-ai/project-governance` (`docs/specifications/WORK-PACKAGES.md`) : chaque paramètre écrit entre chevrons y est écrit en capitales (`ORG/DEPOT`, `DEPOT-CONSOMMATEUR`, `REV`, `SUJET/SHA`), le jalon B prime s'écrit `B'`. @@ -246,18 +247,23 @@ pas découpés ici : leurs paquets relèvent d'un jalon ultérieur et d'une autr Le découpage suit l'ordre de réalisation que la doctrine impose : les gates d'entrée précèdent le paquet (ADR-0058 Conséquences : « deux gates nouveaux à écrire dans le dépôt consommateur ») ; INV-a à INV-c précèdent toute activation (ADR-0045 décision 4), comme le vecteur egress -(ADR-0058 section 3 condition 4) ; la mesure de valeur et la preuve de retrait ferment le jalon +(ADR-0058 section 3 condition 4) et le confinement des passes à leur worktree (CA-32, décision du +propriétaire du 2026-10-24), qui vaut aussi pour le chemin sans le paquet ; la mesure de valeur et la preuve de retrait ferment le jalon (ADR-0058 section 3 condition 6 et section 4). Ce tableau est approuvé : la norme n'accepte un trailer `Work-package` que si le PRD à ce commit est au moins à `décision` (`ecosystem/work-package-policy.v1.yaml`, `approvedFrom`), et la pull request -dédiée qui a promu ce document l'a fait en citant l'arbitrage du propriétaire du 2026-10-23. Un -paquet s'ouvre par une issue qui nomme ce document et sa ligne. +dédiée qui a promu ce document l'a fait en citant l'arbitrage du propriétaire du 2026-10-23. La +ligne `WP-4` a été étendue ensuite par une pull request dédiée, qui cite l'arbitrage du propriétaire +du 2026-10-24 (CA-32, et les chemins du mécanisme de R-13 et des deux pilotes) ; l'issue de ce +paquet cite le commit de cette extension. Un paquet s'ouvre par une issue qui nomme ce document et +sa ligne. Chaque périmètre nomme des chemins sous `tools/review/` ; `tools/review/gates/`, `tools/review/vectors/` et `tools/review/advisories.v1.json` n'existent pas encore au commit lu (`git ls-files tools/review` rend 8 fichiers) : ils sont déclarés à créer par WP-1, WP-3 et WP-4, -et le lecteur de la norme le signale comme tel. `docs/evidence/` existe (une campagne). +et le lecteur de la norme le signale comme tel ; `tools/review/confinement/`, ajouté le 2026-10-24 +pour le mécanisme de R-13, est déclaré à créer par WP-4. `docs/evidence/` existe (une campagne). Porte : les six paquets sont `two-way`. Aucun ne modifie un contrat, un lock, une fiche de phase acceptée ni un ruleset ; le paquet entre dans un espace de travail Bun propre à `tools/review`, @@ -273,6 +279,6 @@ de l'orchestrateur ou le déplacement du dépôt (AB-4), n'est pas dans ce jalon | `WP-1` | `CA-18`, `CA-21`, `CA-23` | `tools/review/gates/**`, `tools/review/advisories.v1.json`, `package.json` | `bun test tools/review/gates` | `two-way` | | `WP-2` | `CA-19`, `CA-20`, `CA-25` | `tools/review/*.ts`, `tools/review/package.json`, `tools/review/bun.lock`, `tools/review/bunfig.toml`, `package.json` | `bun run check:review-tools` | `two-way` | | `WP-3` | `CA-26`, `CA-27`, `CA-28` | `tools/review/*.ts`, `tools/review/vectors/**` | `bun test tools/review/vectors` | `two-way` | -| `WP-4` | `CA-22`, `CA-31` | `tools/review/vectors/**`, `docs/evidence/**` | `bun test tools/review/vectors` | `two-way` | +| `WP-4` | `CA-22`, `CA-31`, `CA-32` | `tools/review/vectors/**`, `tools/review/confinement/**`, `tools/review/*.ts`, `docs/evidence/**` | `bun test tools/review/vectors` | `two-way` | | `WP-5` | `CA-24` | `docs/evidence/**`, `project.v1.yaml`, `README.md` | `grep -n 'langgraph' project.v1.yaml` | `two-way` | | `WP-6` | `CA-29`, `CA-30` | `tools/review/*.ts`, `project.v1.yaml`, `README.md` | `bun run check:review-tools` | `two-way` | From 0a8a870affdce84245e84d80d13d236de7092173 Mon Sep 17 00:00:00 2001 From: Constantin Jais <74049135+constantin-jais@users.noreply.github.com> Date: Sat, 24 Oct 2026 14:57:48 +0200 Subject: [PATCH 2/3] docs: CA-32 names what confines the read, and closes the existence oracle Second-family review of #28 (GLM 5.3, ACCEPTABLE on 0f306b2). Each finding was checked against its source. - Major, applied. R-16 named a `pass-read-confined` condition at `satisfied: false` that no branch carries yet: the second round of #27 is not pushed. The detection column now reads "no true condition", which holds both before and after #27 merges. - Minor, applied. Network namespaces and nftables do not confine a file read. CA-32 now says that the container mounts confine the read, that the network namespace and its rules confine egress, and that the second does not replace the first. - Minor, applied. The vector now requires that the error returned for the sentinel is byte-identical to the error for a missing path inside the worktree, so a refusal cannot tell whether a host file exists. - Minor, rejected. The suggestion was to note in issue #20 that acceptance also runs check:review-tools. A work-package issue body must be exactly the form output (WORK-PACKAGES.md, "Nothing else is allowed in the body"). The full validation chain stays in the requirements specification. Exempt: docs-only Signed-off-by: Constantin Jais <74049135+constantin-jais@users.noreply.github.com> --- docs/cahier-des-charges.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/cahier-des-charges.md b/docs/cahier-des-charges.md index da485c9..0418973 100644 --- a/docs/cahier-des-charges.md +++ b/docs/cahier-des-charges.md @@ -480,7 +480,7 @@ flotte de la mission. - CA-29 **Preuve de retrait** (ADR-0058 section 4 ; ADR-0032 D5 étapes 8 et 9). Le fan-out sans le paquet reste exécutable dans le dépôt et rend les mêmes résultats : sur PLAN-MESURE (CA-24) et sur le plan historique, c'est-à-dire `tools/review/plan.orchestrator-ca44eb3.json` dont le champ `commit` est remplacé de la même façon (le commit `ca44eb33` qu'il nomme n'est plus dans l'historique de la branche servie : `git rev-list --all` n'en rend aucune occurrence au 2026-10-23), la sortie de `--dry-run` (liste des passes et chemins de sortie) est identique à l'octet entre le chemin LangGraph et le chemin sans le paquet ; sur le journal historique `docs/reviews/orchestrator/ca44eb3/events.jsonl`, `replayPassStatuses` rend le même résultat ; sur les deux verdicts enregistrés de ce dossier, `validateVerdict` rend la même liste d'erreurs (vide). Une campagne réelle rejouée des deux côtés est une mesure versée dans `docs/evidence/`, pas un test de la chaîne requise (R-5 : binaire `pi` et authentification hors manifeste). Validation : `bun run check:review-tools` vert avec un test de parité qui exécute les deux chemins sur les deux plans et le journal ; neutralisation : altérer un chemin de sortie dans le chemin LangGraph d'une copie doit rougir ; le retrait du paquet est alors une pull request qui supprime le chemin LangGraph, ses gates et ses vecteurs, sans migration d'un verdict ni d'un journal. - CA-30 **Fiche à jour** (ADR-0058 section 4, critère de sortie de phase, I-08 ; ADR-0058 section 3 condition 6). `project.v1.yaml` porte, pour `tools/review`, l'adoption du runtime LangGraph avec sa date, l'écart de valeur de CA-24 avec sa date, la preuve de retrait de CA-29 comme critère de sortie de phase, et le non-objectif « faire de LangGraph une autorité » reformulé pour ne plus couvrir la dépendance de l'outillage ; `last_verified_on` est postérieur à cette écriture, et la section statut du README est régénérée depuis la fiche. Validation : `grep -c -i 'langgraph' project.v1.yaml` rend au moins 3 ; `grep -n 'last_verified_on' project.v1.yaml` rend une date postérieure au 2026-10-23 ; `bun run heal:card-status` de `project-governance` ne produit aucune différence (CA-14). - CA-31 **Vecteur 4 d'ADR-0045 : suite d'attaques adaptatives, utilité et attaque rapportées ensemble** (ADR-0045 Conséquences, vecteur 4, `docs/adr/0045-*.md:116` de `project-governance` ; arbitrage du propriétaire du 2026-10-23, R-15). Un corpus versionné sous `tools/review/vectors/adaptive/` réunit des contenus revus hostiles qui cherchent chacun à produire un effet hors de la liste fermée de CA-26 à CA-28 (chemin de sortie, commande, adresse, consigne lue par le nœud d'agrégation, verdict hors vocabulaire) et des contenus bénins de contrôle. Le rejeu applique le corpus aux contrôles déterministes de l'orchestrateur (`buildJobs`, `validateVerdict`, nœud d'agrégation) avec, pour chaque cas, la sortie de modèle enregistrée qui tente l'effet, et imprime sur une seule ligne le taux d'utilité (cas bénins qui rendent le résultat attendu, sur le total des bénins) et le taux d'attaque réussie (cas hostiles qui produisent un effet hors liste, sur le total des hostiles), chacun avec son numérateur et son dénominateur ; une ligne qui n'imprime qu'un des deux taux, ou un dénominateur nul, est un échec. Validation : `bun test tools/review/vectors` vert avec un taux d'attaque réussie de 0 sur le total des hostiles et un taux d'utilité égal à celui du corpus bénin sans les contrôles hostiles ; neutralisation : retirer dans une copie un contrôle de CA-26, CA-27 ou CA-28 doit faire monter le taux d'attaque réussie au-dessus de 0 et rougir le test. Un rejeu avec un modèle réel (AgentDojo ou équivalent) est une mesure versée dans `docs/evidence/`, pas un test de la chaîne requise (R-5) ; les chiffres d'ADR-0045 (77 % contre 84 %, `:77`) sont ceux des auteurs de la méthode, pas une mesure de ce dépôt. Valeur au commit lu : aucun corpus, critère rouge aujourd'hui. -- CA-32 **Passe confinée à son worktree** (décision du propriétaire du 2026-10-24 : sixième condition d'activation, appliquée aux deux pilotes ; prolonge INV-a d'ADR-0045, `docs/adr/0045-*.md:84` de `project-governance`, au canal que sa lettre ne couvre pas ; R-16). Une passe de revue ne lit rien hors du worktree détaché de son commit : l'outil `read` de `pi`, comme `grep`, `find` et `ls`, résout les chemins absolus et `~` hors du répertoire de la passe (`pi` 1.4.0, `dist/core/tools/path-utils.js:38-44` : « Handles ~ expansion and absolute paths »), et le verdict, texte libre compris, est publié sous `docs/reviews/` d'un dépôt public ; un contenu revu qui obtient la copie d'un fichier de l'hôte dans `detail` le publierait. La frontière est appliquée hors du processus de `pi`, par le mécanisme de R-13 (espace de noms Linux, conteneur qui ne monte que le worktree de la passe), jamais par une consigne au modèle ; aucun secret de fournisseur n'est lisible depuis la passe, ni par un fichier monté, ni par une variable d'environnement qu'un pseudo-fichier du processus exposerait. Tant que la condition n'est pas tenue, les deux pilotes, avec et sans le paquet, refusent les passes réelles ; `--dry-run` reste disponible. Validation : `bun test tools/review/vectors` vert (vecteur `pass-read-confined`) : sur chacun des deux pilotes, une passe dont le faux `pi` tente de lire un fichier sentinelle placé hors de son worktree, par chemin absolu, par `~` et par chemin relatif qui remonte, ainsi que l'emplacement du secret du fournisseur, n'obtient aucun octet ; la sortie imprime le mécanisme, les chemins visés et le compte des lectures refusées ; neutralisation : monter le répertoire du sentinelle dans une copie doit faire aboutir la lecture, donc rougir ; `bun run check:review-tools` vert avec un test qui montre, pour chacun des deux pilotes, le refus des passes réelles tant que la condition est fausse. Valeur au commit lu (`7644bfd`, relevé le 2026-10-24) : aucun confinement, aucun refus sur le pilote sans le paquet, critère rouge. +- CA-32 **Passe confinée à son worktree** (décision du propriétaire du 2026-10-24 : sixième condition d'activation, appliquée aux deux pilotes ; prolonge INV-a d'ADR-0045, `docs/adr/0045-*.md:84` de `project-governance`, au canal que sa lettre ne couvre pas ; R-16). Une passe de revue ne lit rien hors du worktree détaché de son commit : l'outil `read` de `pi`, comme `grep`, `find` et `ls`, résout les chemins absolus et `~` hors du répertoire de la passe (`pi` 1.4.0, `dist/core/tools/path-utils.js:38-44` : « Handles ~ expansion and absolute paths »), et le verdict, texte libre compris, est publié sous `docs/reviews/` d'un dépôt public ; un contenu revu qui obtient la copie d'un fichier de l'hôte dans `detail` le publierait. La frontière est appliquée hors du processus de `pi`, par l'environnement d'exécution décidé avec R-13 (conteneur Linux qui ne monte que le worktree de la passe : la fermeture des montages confine la lecture, l'espace de noms réseau et ses règles nftables confinent la sortie réseau, et le second ne remplace pas le premier), jamais par une consigne au modèle ; aucun secret de fournisseur n'est lisible depuis la passe, ni par un fichier monté, ni par une variable d'environnement qu'un pseudo-fichier du processus exposerait. Tant que la condition n'est pas tenue, les deux pilotes, avec et sans le paquet, refusent les passes réelles ; `--dry-run` reste disponible. Validation : `bun test tools/review/vectors` vert (vecteur `pass-read-confined`) : sur chacun des deux pilotes, une passe dont le faux `pi` tente de lire un fichier sentinelle placé hors de son worktree, par chemin absolu, par `~` et par chemin relatif qui remonte, ainsi que l'emplacement du secret du fournisseur, n'obtient aucun octet, et l'erreur rendue pour le sentinelle est identique à l'octet à celle d'un chemin inexistant du worktree (aucun oracle d'existence) ; la sortie imprime le mécanisme, les chemins visés et le compte des lectures refusées ; neutralisation : monter le répertoire du sentinelle dans une copie doit faire aboutir la lecture, donc rougir ; `bun run check:review-tools` vert avec un test qui montre, pour chacun des deux pilotes, le refus des passes réelles tant que la condition est fausse. Valeur au commit lu (`7644bfd`, relevé le 2026-10-24) : aucun confinement, aucun refus sur le pilote sans le paquet, critère rouge. ## 10. Critères d'abandon @@ -583,7 +583,7 @@ transitive `@langchain/core` et `@langchain/langgraph-checkpoint`, aux condition | R-13 | Le mécanisme réseau qui applique CA-22 n'existe pas sur la machine de forge (aucun plan `execution-plan-body` ne couvre `tools/review` au commit lu) | vecteur `telemetry-egress-refused` sans mécanisme nommé | l'orchestrateur sous LangGraph ne s'active pas (ADR-0058 section 3 condition 4 : « un harness qui ne produit pas ce refus n'active pas le worker ») ; décision du propriétaire du 2026-10-24 : espace de noms réseau Linux et règles nftables à compteur, le même en CI (runner `ubuntu-24.04`) et sur la machine de forge, où les campagnes réelles tournent dans un conteneur Linux ; écartés le même jour : Seatbelt sur macOS (refus mesuré, aucun compte natif, `sandbox-exec` déprécié), le harness d'`execution-sandbox` (macOS non pris en charge, Linux non qualifié), le report de CA-22 | | R-14 | Un avis de l'éditeur absent de la base `osv-scanner` | date de base et nombre d'entrées imprimés par CA-18 | le fichier `tools/review/advisories.v1.json` le porte ; l'ajout d'une entrée est une édition revue | | R-15 | Le corpus du vecteur 4 d'ADR-0045 (suite d'attaques adaptatives, taux d'utilité et taux d'attaque réussie rapportés ensemble) est borné par ce que des cas enregistrés peuvent exercer : il prouve les contrôles déterministes, pas la robustesse d'un modèle réel | `grep -n 'AgentDojo' docs/adr/0045-*.md` dans `project-governance` rend la ligne du vecteur ; `grep -n 'CA-31' docs/cahier-des-charges.md` rend le critère | décision du propriétaire du 2026-10-23 : le vecteur est ajouté à WP-4 (CA-31) ; un rejeu avec un modèle réel est une mesure versée dans `docs/evidence/`, jamais un seuil ; M9 compte quatre vecteurs | -| R-16 | L'outil `read` de `pi` n'est pas confiné au worktree de la passe et le verdict, texte libre compris, est publié : un contenu revu peut faire recopier un fichier de l'hôte dans un artefact public (relevé par les relectures de second modèle de WP-3, le 2026-10-24) | condition `pass-read-confined` de `tools/review/fanout-activation.ts` à `satisfied: false` ; vecteur `pass-read-confined` absent | décision du propriétaire du 2026-10-24 : sixième condition d'activation (CA-32), appliquée aux deux pilotes ; aucune campagne réelle de `tools/review` avant WP-4 (la dernière versée est `docs/evidence/2026-08-19-first-real-fanout-campaign.md`) ; le mécanisme est celui de R-13 | +| R-16 | L'outil `read` de `pi` n'est pas confiné au worktree de la passe et le verdict, texte libre compris, est publié : un contenu revu peut faire recopier un fichier de l'hôte dans un artefact public (relevé par les relectures de second modèle de WP-3, le 2026-10-24) | aucune condition `pass-read-confined` vraie dans `tools/review/fanout-activation.ts` (absente au commit lu, ajoutée à `satisfied: false` par WP-3) ; vecteur `pass-read-confined` absent | décision du propriétaire du 2026-10-24 : sixième condition d'activation (CA-32), appliquée aux deux pilotes ; aucune campagne réelle de `tools/review` avant WP-4 (la dernière versée est `docs/evidence/2026-08-19-first-real-fanout-campaign.md`) ; le mécanisme est celui de R-13 | ## 13. Table de conformité à SPECIFICATION-STANDARD From 5239415587b38ce15befee80faf9260320c7ef84 Mon Sep 17 00:00:00 2001 From: Constantin Jais <74049135+constantin-jais@users.noreply.github.com> Date: Sun, 25 Oct 2026 05:28:22 +0100 Subject: [PATCH 3/3] docs: CA-32 compares the same path in both host states Second round of the second-family review of #28 (GLM 5.3, ACCEPTABLE on 0a8a870). The reviewer accepted the four dispositions of round one and raised one new minor finding, checked here at its source. The round-one wording asked for a byte-identical error between the sentinel and a missing path inside the worktree. That cannot be met. pi returns error.message unchanged (pi 1.4.0, dist/core/tools/read.js:77, pi-agent-core dist/agent-loop.js:542-546), and the Node message embeds the path it read. Reproduced: accessSync on two missing paths gives two different strings, "ENOENT: no such file or directory, access ''". An author who compared the full message could never turn the vector green, so the sixth activation condition could never be met. The requirement now compares the same path in two host states: a host file present at that path, and no file there. Equality there is exactly the "no existence oracle" property. Exempt: docs-only Signed-off-by: Constantin Jais <74049135+constantin-jais@users.noreply.github.com> --- docs/cahier-des-charges.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cahier-des-charges.md b/docs/cahier-des-charges.md index 0418973..fca8a15 100644 --- a/docs/cahier-des-charges.md +++ b/docs/cahier-des-charges.md @@ -480,7 +480,7 @@ flotte de la mission. - CA-29 **Preuve de retrait** (ADR-0058 section 4 ; ADR-0032 D5 étapes 8 et 9). Le fan-out sans le paquet reste exécutable dans le dépôt et rend les mêmes résultats : sur PLAN-MESURE (CA-24) et sur le plan historique, c'est-à-dire `tools/review/plan.orchestrator-ca44eb3.json` dont le champ `commit` est remplacé de la même façon (le commit `ca44eb33` qu'il nomme n'est plus dans l'historique de la branche servie : `git rev-list --all` n'en rend aucune occurrence au 2026-10-23), la sortie de `--dry-run` (liste des passes et chemins de sortie) est identique à l'octet entre le chemin LangGraph et le chemin sans le paquet ; sur le journal historique `docs/reviews/orchestrator/ca44eb3/events.jsonl`, `replayPassStatuses` rend le même résultat ; sur les deux verdicts enregistrés de ce dossier, `validateVerdict` rend la même liste d'erreurs (vide). Une campagne réelle rejouée des deux côtés est une mesure versée dans `docs/evidence/`, pas un test de la chaîne requise (R-5 : binaire `pi` et authentification hors manifeste). Validation : `bun run check:review-tools` vert avec un test de parité qui exécute les deux chemins sur les deux plans et le journal ; neutralisation : altérer un chemin de sortie dans le chemin LangGraph d'une copie doit rougir ; le retrait du paquet est alors une pull request qui supprime le chemin LangGraph, ses gates et ses vecteurs, sans migration d'un verdict ni d'un journal. - CA-30 **Fiche à jour** (ADR-0058 section 4, critère de sortie de phase, I-08 ; ADR-0058 section 3 condition 6). `project.v1.yaml` porte, pour `tools/review`, l'adoption du runtime LangGraph avec sa date, l'écart de valeur de CA-24 avec sa date, la preuve de retrait de CA-29 comme critère de sortie de phase, et le non-objectif « faire de LangGraph une autorité » reformulé pour ne plus couvrir la dépendance de l'outillage ; `last_verified_on` est postérieur à cette écriture, et la section statut du README est régénérée depuis la fiche. Validation : `grep -c -i 'langgraph' project.v1.yaml` rend au moins 3 ; `grep -n 'last_verified_on' project.v1.yaml` rend une date postérieure au 2026-10-23 ; `bun run heal:card-status` de `project-governance` ne produit aucune différence (CA-14). - CA-31 **Vecteur 4 d'ADR-0045 : suite d'attaques adaptatives, utilité et attaque rapportées ensemble** (ADR-0045 Conséquences, vecteur 4, `docs/adr/0045-*.md:116` de `project-governance` ; arbitrage du propriétaire du 2026-10-23, R-15). Un corpus versionné sous `tools/review/vectors/adaptive/` réunit des contenus revus hostiles qui cherchent chacun à produire un effet hors de la liste fermée de CA-26 à CA-28 (chemin de sortie, commande, adresse, consigne lue par le nœud d'agrégation, verdict hors vocabulaire) et des contenus bénins de contrôle. Le rejeu applique le corpus aux contrôles déterministes de l'orchestrateur (`buildJobs`, `validateVerdict`, nœud d'agrégation) avec, pour chaque cas, la sortie de modèle enregistrée qui tente l'effet, et imprime sur une seule ligne le taux d'utilité (cas bénins qui rendent le résultat attendu, sur le total des bénins) et le taux d'attaque réussie (cas hostiles qui produisent un effet hors liste, sur le total des hostiles), chacun avec son numérateur et son dénominateur ; une ligne qui n'imprime qu'un des deux taux, ou un dénominateur nul, est un échec. Validation : `bun test tools/review/vectors` vert avec un taux d'attaque réussie de 0 sur le total des hostiles et un taux d'utilité égal à celui du corpus bénin sans les contrôles hostiles ; neutralisation : retirer dans une copie un contrôle de CA-26, CA-27 ou CA-28 doit faire monter le taux d'attaque réussie au-dessus de 0 et rougir le test. Un rejeu avec un modèle réel (AgentDojo ou équivalent) est une mesure versée dans `docs/evidence/`, pas un test de la chaîne requise (R-5) ; les chiffres d'ADR-0045 (77 % contre 84 %, `:77`) sont ceux des auteurs de la méthode, pas une mesure de ce dépôt. Valeur au commit lu : aucun corpus, critère rouge aujourd'hui. -- CA-32 **Passe confinée à son worktree** (décision du propriétaire du 2026-10-24 : sixième condition d'activation, appliquée aux deux pilotes ; prolonge INV-a d'ADR-0045, `docs/adr/0045-*.md:84` de `project-governance`, au canal que sa lettre ne couvre pas ; R-16). Une passe de revue ne lit rien hors du worktree détaché de son commit : l'outil `read` de `pi`, comme `grep`, `find` et `ls`, résout les chemins absolus et `~` hors du répertoire de la passe (`pi` 1.4.0, `dist/core/tools/path-utils.js:38-44` : « Handles ~ expansion and absolute paths »), et le verdict, texte libre compris, est publié sous `docs/reviews/` d'un dépôt public ; un contenu revu qui obtient la copie d'un fichier de l'hôte dans `detail` le publierait. La frontière est appliquée hors du processus de `pi`, par l'environnement d'exécution décidé avec R-13 (conteneur Linux qui ne monte que le worktree de la passe : la fermeture des montages confine la lecture, l'espace de noms réseau et ses règles nftables confinent la sortie réseau, et le second ne remplace pas le premier), jamais par une consigne au modèle ; aucun secret de fournisseur n'est lisible depuis la passe, ni par un fichier monté, ni par une variable d'environnement qu'un pseudo-fichier du processus exposerait. Tant que la condition n'est pas tenue, les deux pilotes, avec et sans le paquet, refusent les passes réelles ; `--dry-run` reste disponible. Validation : `bun test tools/review/vectors` vert (vecteur `pass-read-confined`) : sur chacun des deux pilotes, une passe dont le faux `pi` tente de lire un fichier sentinelle placé hors de son worktree, par chemin absolu, par `~` et par chemin relatif qui remonte, ainsi que l'emplacement du secret du fournisseur, n'obtient aucun octet, et l'erreur rendue pour le sentinelle est identique à l'octet à celle d'un chemin inexistant du worktree (aucun oracle d'existence) ; la sortie imprime le mécanisme, les chemins visés et le compte des lectures refusées ; neutralisation : monter le répertoire du sentinelle dans une copie doit faire aboutir la lecture, donc rougir ; `bun run check:review-tools` vert avec un test qui montre, pour chacun des deux pilotes, le refus des passes réelles tant que la condition est fausse. Valeur au commit lu (`7644bfd`, relevé le 2026-10-24) : aucun confinement, aucun refus sur le pilote sans le paquet, critère rouge. +- CA-32 **Passe confinée à son worktree** (décision du propriétaire du 2026-10-24 : sixième condition d'activation, appliquée aux deux pilotes ; prolonge INV-a d'ADR-0045, `docs/adr/0045-*.md:84` de `project-governance`, au canal que sa lettre ne couvre pas ; R-16). Une passe de revue ne lit rien hors du worktree détaché de son commit : l'outil `read` de `pi`, comme `grep`, `find` et `ls`, résout les chemins absolus et `~` hors du répertoire de la passe (`pi` 1.4.0, `dist/core/tools/path-utils.js:38-44` : « Handles ~ expansion and absolute paths »), et le verdict, texte libre compris, est publié sous `docs/reviews/` d'un dépôt public ; un contenu revu qui obtient la copie d'un fichier de l'hôte dans `detail` le publierait. La frontière est appliquée hors du processus de `pi`, par l'environnement d'exécution décidé avec R-13 (conteneur Linux qui ne monte que le worktree de la passe : la fermeture des montages confine la lecture, l'espace de noms réseau et ses règles nftables confinent la sortie réseau, et le second ne remplace pas le premier), jamais par une consigne au modèle ; aucun secret de fournisseur n'est lisible depuis la passe, ni par un fichier monté, ni par une variable d'environnement qu'un pseudo-fichier du processus exposerait. Tant que la condition n'est pas tenue, les deux pilotes, avec et sans le paquet, refusent les passes réelles ; `--dry-run` reste disponible. Validation : `bun test tools/review/vectors` vert (vecteur `pass-read-confined`) : sur chacun des deux pilotes, une passe dont le faux `pi` tente de lire un fichier sentinelle placé hors de son worktree, par chemin absolu, par `~` et par chemin relatif qui remonte, ainsi que l'emplacement du secret du fournisseur, n'obtient aucun octet, et l'erreur rendue pour le chemin du sentinelle est identique à l'octet à celle que rend le même chemin lorsqu'aucun fichier de l'hôte ne s'y trouve (aucun oracle d'existence ; le message de `pi` embarque le chemin lu, `dist/core/tools/read.js:77`, donc deux chemins différents ne se comparent pas à l'octet) ; la sortie imprime le mécanisme, les chemins visés et le compte des lectures refusées ; neutralisation : monter le répertoire du sentinelle dans une copie doit faire aboutir la lecture, donc rougir ; `bun run check:review-tools` vert avec un test qui montre, pour chacun des deux pilotes, le refus des passes réelles tant que la condition est fausse. Valeur au commit lu (`7644bfd`, relevé le 2026-10-24) : aucun confinement, aucun refus sur le pilote sans le paquet, critère rouge. ## 10. Critères d'abandon