chore(typo): retire tous les tirets cadratins/demi-cadratins + garde CI
Some checks failed
CI / Build & test (Node 24) (push) Has been cancelled
CI / Pack & boot smoke (Node 22) (push) Has been cancelled
CI / No em/en dashes (push) Has been cancelled
CI / Build & test (Node 22) (push) Has been cancelled
Deploy site (production) / build-and-deploy (push) Successful in 20s

Remplace les 547 tirets cadratins (U+2014) et demi-cadratins (U+2013) des fichiers versionnés par la ponctuation contextuelle adaptée (point médian, deux-points, virgule, parenthèses ; tiret simple pour les plages), sur 122 fichiers (appli, vitrine, doc, tests, workflows, scripts).

Ajoute le job CI « lint-dashes » (git grep -P) qui échoue si un tiret cadratin/demi-cadratin réapparaît, hors logo binaire et captures brutes du terminal (fidélité des fixtures de détection de dialogue).
This commit is contained in:
2026-07-17 16:44:00 +02:00
parent 985531a986
commit 65ef616867
122 changed files with 538 additions and 521 deletions

View File

@@ -1,4 +1,4 @@
# Spike S3 dialogues TUI, frappes, flood VERDICT : ✅ GO (partiel documenté)
# Spike S3 · dialogues TUI, frappes, flood · VERDICT : ✅ GO (partiel documenté)
Exécuté le 2026-06-11 (CLI 2.1.173). Scripts : `run.mjs` (passe 1), `run2.mjs` (passe 2), `flood.mjs`. Résultats bruts dans `captures/`.
@@ -6,21 +6,21 @@ Exécuté le 2026-06-11 (CLI 2.1.173). Scripts : `run.mjs` (passe 1), `run2.mjs`
| Dialogue | Détection | Options extraites | Réponse validée |
|---|---|---|---|
| **Trust dialog** (nouveau dossier) | écran seul (précède le registre) motif `trust` + `` sur texte aplati | `1. Yes, I trust this folder / 2. No, exit` « Enter to confirm · Esc to cancel » | **Entrée** ✅ (10+ occurrences via S1/S3) |
| **Permission Write** (création/édition de fichier) | registre `status: waiting`, `waitingFor: "permission prompt"` + écran | `1. Yes / 2. Yes, allow all edits during this session (shift+tab) / 3. No` « Esc to cancel · Tab to amend » | chiffre seul **insuffisant** ; **chiffre puis Entrée** ✅ (fichier créé) |
| **AskUserQuestion** | registre `waiting` (même `waitingFor: "permission prompt"` le registre ne distingue pas les types) | `1. Rouge / 2. Bleu / 3. Type something. / 4. Chat about this` « Enter to select · ↑/↓ to navigate · Esc to cancel » | **↓ + Entrée** ✅ (tour complété, bonne option choisie) |
| **Permission Bash** | **non déclenchée** : `echo` est dans l'allowlist par défaut ET `node -e` s'exécute dans le **bash sandboxé** du CLI 2.x sans prompt | | |
| **Refus par Esc** | non exercé (quota) ; « Esc to cancel » affiché sur les deux dialogues observés | | à valider en P4 |
| **Trust dialog** (nouveau dossier) | écran seul (précède le registre) : motif `trust` + `` sur texte aplati | `1. Yes, I trust this folder / 2. No, exit` : « Enter to confirm · Esc to cancel » | **Entrée** ✅ (10+ occurrences via S1/S3) |
| **Permission Write** (création/édition de fichier) | registre `status: waiting`, `waitingFor: "permission prompt"` + écran | `1. Yes / 2. Yes, allow all edits during this session (shift+tab) / 3. No` : « Esc to cancel · Tab to amend » | chiffre seul **insuffisant** ; **chiffre puis Entrée** ✅ (fichier créé) |
| **AskUserQuestion** | registre `waiting` (même `waitingFor: "permission prompt"`, le registre ne distingue pas les types) | `1. Rouge / 2. Bleu / 3. Type something. / 4. Chat about this` : « Enter to select · ↑/↓ to navigate · Esc to cancel » | **↓ + Entrée** ✅ (tour complété, bonne option choisie) |
| **Permission Bash** | **non déclenchée** : `echo` est dans l'allowlist par défaut ET `node -e` s'exécute dans le **bash sandboxé** du CLI 2.x sans prompt | - | - |
| **Refus par Esc** | non exercé (quota) ; « Esc to cancel » affiché sur les deux dialogues observés | - | à valider en P4 |
## Enseignements structurants
1. **Le protocole de réponse est : (chiffre pour positionner) + Entrée pour confirmer.** Entrée seule valide l'option pré-sélectionnée (``). Flèches ↑/↓ + Entrée fonctionnent partout. → L'UI mobile enverra `<chiffre>` + `\r`.
2. **Le registre `~/.claude/sessions` détecte les états d'attente des dialogues outils** (permission ET AskUserQuestion) avec le même `waitingFor: "permission prompt"` → le typage fin du dialogue (question vs permission, options) exige la lecture d'écran (`@xterm/headless` en prod ; le strip ANSI naïf mange les espaces voir VERDICT S1).
2. **Le registre `~/.claude/sessions` détecte les états d'attente des dialogues outils** (permission ET AskUserQuestion) avec le même `waitingFor: "permission prompt"` → le typage fin du dialogue (question vs permission, options) exige la lecture d'écran (`@xterm/headless` en prod ; le strip ANSI naïf mange les espaces, voir VERDICT S1).
3. **Il y a moins de prompts de permission que prévu** : le bash sandboxé exécute sans prompt les commandes sans réseau/écriture hors workspace. Les vrais `waiting` viennent des éditions de fichiers, du réseau, d'AskUserQuestion et des plans. Bonne nouvelle pour l'UX (moins d'interruptions), à intégrer aux fixtures P4.
4. **Pré-trust programmatique plausible** : accepter le dialogue écrit `projects["<dir>"].hasTrustDialogAccepted: true` dans `~/.claude.json` (structure complète capturée). À la création d'un worktree, écrire cette entrée (avec consentement explicite dans la modal) éviterait le dialogue à valider en P2/P3.
4. **Pré-trust programmatique plausible** : accepter le dialogue écrit `projects["<dir>"].hasTrustDialogAccepted: true` dans `~/.claude.json` (structure complète capturée). À la création d'un worktree, écrire cette entrée (avec consentement explicite dans la modal) éviterait le dialogue, à valider en P2/P3.
5. La regex d'options `^\s*(\s*)?(\d+)\.\s*(.+)$` fonctionne sur le texte aplati (labels concaténés mais exploitables).
## Volet flood (node-pty pause/resume) ✅ GO complet
## Volet flood (node-pty pause/resume) · ✅ GO complet
21,5 Mo streamés ; pause de 10 s → **0 octet** reçu pendant la pause, bash survit, flux complet après resume (marqueur final intact, aucun octet perdu) ; écho 4,7 ms après le flood ; ~21 Mo en ~0,7 s hors pause.
@@ -28,5 +28,5 @@ Exécuté le 2026-06-11 (CLI 2.1.173). Scripts : `run.mjs` (passe 1), `run2.mjs`
- Exercer le refus par Esc et le dialogue « No, and tell Claude… » (champ texte).
- Approbation de plan (plan mode) non testée.
- Campagne de fiabilité 10/10 par type de dialogue (le critère complet du spike) à automatiser comme tests d'intégration du claude-adapter.
- Campagne de fiabilité 10/10 par type de dialogue (le critère complet du spike), à automatiser comme tests d'intégration du claude-adapter.
- Le MVP (P3) n'a PAS besoin de tout ça : les boutons d'approbation sont en P4 ; le fallback « ouvrir le terminal » couvre tous les cas.

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env node
// Spike S3 (volet flood) pause()/resume() node-pty sous 20 Mo de débit, sans claude (zéro quota).
// Spike S3 (volet flood) · pause()/resume() node-pty sous 20 Mo de débit, sans claude (zéro quota).
// Critères : aucune perte (marqueur final reçu, volume >= attendu), 0 octet pendant pause,
// process bash survit à 10 s de pause, écho < 100 ms après le flood.
import ptyMod from '@homebridge/node-pty-prebuilt-multiarch';
@@ -11,7 +11,7 @@ const CAPTURES = new URL('./captures/', import.meta.url).pathname;
mkdirSync(CAPTURES, { recursive: true });
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
// 20 Mo de base64 (lignes de 76 chars) la conversion LF→CRLF du TTY est prise en compte
// 20 Mo de base64 (lignes de 76 chars) : la conversion LF→CRLF du TTY est prise en compte
execSync('head -c 15728640 /dev/urandom | base64 > /tmp/spike-flood.txt && wc -c < /tmp/spike-flood.txt');
const fileBytes = parseInt(execSync('wc -c < /tmp/spike-flood.txt').toString().trim(), 10);
const fileLines = parseInt(execSync('wc -l < /tmp/spike-flood.txt').toString().trim(), 10);

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env node
// Spike S3 dialogues TUI : détection (registre + écran), extraction des options, frappes de réponse.
// Spike S3 · dialogues TUI : détection (registre + écran), extraction des options, frappes de réponse.
// Types couverts : trust dialog (nouveau dossier), permission Bash, permission Write (édition),
// AskUserQuestion, refus par Esc. (Approbation de plan : suivi manuel, voir VERDICT.)
import { execSync } from 'node:child_process';

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env node
// Spike S3 (2e passe) D2..D5 avec commandes hors allowlist, scénarios isolés,
// Spike S3 (2e passe) · D2..D5 avec commandes hors allowlist, scénarios isolés,
// détection registre + fallback écran. D1 (trust) déjà validé en 1re passe.
import { execSync } from 'node:child_process';
import { mkdirSync, writeFileSync, rmSync, existsSync } from 'node:fs';
@@ -78,7 +78,7 @@ await scenario('d2_permBash', async () => {
const S = new ClaudeSession([], d, 'perm-bash2', CAPTURES);
try {
await S.waitReady();
await S.type(`Run this exact bash command and show me its output: node -e "console.log('spike-s3-marker-xyz')" do not do anything else.`);
await S.type(`Run this exact bash command and show me its output: node -e "console.log('spike-s3-marker-xyz')", do not do anything else.`);
const det = await waitDialog(S);
await sleep(1200);
const screen = S.tail(2500);
@@ -119,7 +119,7 @@ await scenario('d4_denyEsc', async () => {
const S = new ClaudeSession([], d, 'deny-esc2', CAPTURES);
try {
await S.waitReady();
await S.type(`Run this exact bash command and show me its output: node -e "console.log('should-be-denied')" do not do anything else.`);
await S.type(`Run this exact bash command and show me its output: node -e "console.log('should-be-denied')", do not do anything else.`);
await waitDialog(S);
await sleep(1200);
const screenBefore = S.tail(1200);