diff --git a/BACKLOG.md b/BACKLOG.md index 6cc0fcd..acc576a 100644 --- a/BACKLOG.md +++ b/BACKLOG.md @@ -480,6 +480,48 @@ Option : ajouter un paramètre `install_shim` pour mode "exec" vs "source", ou d **Validé le :** 2026-07-21 — dry-run Phase A sans mention Context7 (S-ctx-1). Code inspecté pour persistence zshenv + runtime.sh au setup (S-ctx-2). Réponse N → pas de prompt (S-ctx-3). Idempotence ensure_vm_runtime (S-ctx-4). `bash -n lib/phases.sh` OK. ### T6.16 🟡 Une ligne d'explication avant chaque question d'option du setup `<- AC-R039` ✅ implémenté + +--- + +## EPIC 8 — Rafraîchir les fichiers projet figés au setup + +**Problème de fond :** Au `albert-code setup`, les fichiers posés dans le projet (AGENTS.md, opencode.json, .agent-vm.runtime.sh) sont **conservés** s'ils existent — les évolutions du template ne redescendent **jamais** aux projets déjà configurés. Symptômes déjà vus : opencode.json conservé sans provider Albert (T1.6/T7.7), runtime projet ancien + OpenCode 1.2.9 dans la VM → `opencode --auto` inconnu → écran de help au lieu du TUI (AC-R041). + +**Fait de code clé :** Le SEUL fichier rafraîchi à l'existant est le runtime USER `~/.agent-vm/runtime.sh` : son bloc marqué (`$AC_MARKER … $AC_MARKER_END`) est réécrit par `ensure_vm_runtime()` à chaque phase A ET B. Toute logique évolutive doit passer par là. + +### T8.1 🟡 (doc) Convention zone gérée vs fichiers figés + +**But :** Documenter dans ce ticket l'architecture de rafraîchissement du bundle, pour que tout contributeur sache où et comment ajouter une logique évolutive. + +**Architecture de rafraîchissement :** +| Fichier | Rafraîchi ? | Mécanisme | Pourquoi | +|---|---|---|---| +| `~/.agent-vm/runtime.sh` bloc marqué | ✅ Oui, à chaque phase A ET B | `ensure_vm_runtime()` supprime le bloc `$AC_MARKER … $AC_MARKER_END` et le réécrit | Clés API + GH_TOKEN doivent être à jour au boot VM | +| `./opencode.json` | 🟡 Merge conditionnel (T8.2) | Si `jq` présent → merge provider.albert dans existant, sinon avertissement | Provider Albert doit pouvoir être ajouté sans écraser les MCP/permissions | +| `./AGENTS.md` | ❌ Non (jamais écrasé) | `copy_template()` → non-destructif pur | Le projet peut avoir un AGENTS.md personnalisé | +| `./.agent-vm.runtime.sh` (projet) | ❌ Non (jamais écrasé) | `copy_template()` → non-destructif pur | Idem, fichier figé chez l'existant | +| `vendor/vm/` (moteur VM) | ❌ Non (vendored figé) | Versionné dans le repo albert-code | Mis à jour par `git pull` du dépôt albert-code | +| `templates/` | ❌ Non (versionnés) | Versionné dans le repo | Utilisés uniquement au premier `setup` | + +**DoD :** cette documentation est présente dans `BACKLOG.md`. + +### T8.2 🟠 opencode.json — réparer, pas seulement avertir (généralise T1.6/T7.7) + +**But :** Dans `scaffold_opencode_json()`, branche « fichier existe sans albert » (ajoutée par T7.7) : si `jq` est présent, proposer (confirm) puis **merger** `provider.albert` + `model` + `small_model` du template dans l'`opencode.json` existant, sans toucher les autres clés (MCP, permissions, autres providers). Sauvegarder l'original (`.bak`). Si `jq` absent → garder l'avertissement T7.7 (fallback). Non-destructif. + +**DoD :** `opencode.json` perso avec provider scaleway → après setup, provider albert ajouté ET scaleway préservé. → `TESTS.md` S46. + +### T8.3 🟠 Garde-fou version OpenCode dans le bloc marqué du runtime USER (absorbe AC-R041) + +**But :** `ensure_vm_runtime()` écrit un garde-fou exécuté dans la VM au boot, à l'intérieur du bloc marqué `~/.agent-vm/runtime.sh` (après les exports, avant `$AC_MARKER_END`). Logique : capture `opencode --help`, si `--auto` absent → upgrade auto. + +**DoD :** VM avec opencode trop vieux → au boot, upgrade auto → `run` ouvre le TUI, plus de help. → `TESTS.md` S47 `<- AC-R041`. + +### T8.4 🟡 (OPTION, nice-to-have) Verbe `albert-code update` pour rafraîchir un projet existant + +**But :** Proposer un verbe simple pour rafraîchir un projet existant sans repasser tout le setup interactif. `albert-code update` (ou `--update`) exécute les logiques de rafraîchissement (T8.2 merge opencode.json + T8.3 garde-fou runtime) sans les questions MCP/skills. Non implémenté dans cette EPIC, ouvert pour le futur. + +**DoD :** ticket documenté mais pas implémenté. **But :** chaque option d'installation doit être compréhensible sans contexte préalable. Format cible : « Installer Context7 ? Context7 est un MCP qui permet de [...]. Y/n ». **Tâches :** diff --git a/FEEDBACK.md b/FEEDBACK.md index f4bab55..6f50467 100644 --- a/FEEDBACK.md +++ b/FEEDBACK.md @@ -61,6 +61,7 @@ | AC-R038 | 🎛️ | 🟠 | La clé Context7 est demandée dès `albert-code install` (phase A.4), avant toute explication du MCP et avant même que l'utilisateur ait choisi de le brancher. À déplacer : ne la demander qu'au `setup`, si l'utilisateur répond Y à « Installer le MCP context7 ». Aggravant découvert à l'analyse : une clé saisie au `setup` (chemin AC-R028) est persistée dans le `~/.zshenv` hôte mais jamais propagée dans `~/.agent-vm/runtime.sh` (`ensure_vm_runtime` n'est appelé qu'en phase A) : la VM garde `CONTEXT7_API_KEY=''`. | Onboarding alpha Adrien Carpentier 2026-07-21 | ✅ traité | `BACKLOG.md` T6.15 · `TESTS.md` S-ctx-1 à S-ctx-4 | | AC-R039 | 🎛️ | 🟡 | Les questions Y/n du `setup` (MCP notamment) n'expliquent pas assez chaque option : un libellé court entre parenthèses, pas de vraie phrase. Un testeur demande deux fois « c'est quoi Context7 ? » pendant l'onboarding. Format cible : une ligne d'explication avant chaque question, « Installer Context7 ? Context7 est un MCP qui permet de [...]. Y/n ». | Onboarding alpha Adrien Carpentier 2026-07-21 | ✅ traité | `BACKLOG.md` T6.16 · `TESTS.md` S-ctx-5 | | AC-R040 | 🎛️ | 🟠 | `albert-code run` dans un dossier jamais `setup` (pas de `opencode.json`) — ou avec un `opencode.json` sans provider `albert` — ouvre OpenCode sur ses modèles par défaut, **Albert absent de `/models`, sans aucune alerte**. 2 bêta-testeurs piégés (dont un power user), longue session de debug à chaque fois. Généralise T1.6 au cas « pas de fichier ». Fix : garde-fou au `run` (avertit + confirmation avant de lancer sans Albert) + avertissement au `setup` sur fichier existant sans `albert`. | Bêta-testeur 2026-07-21 (Julien Bouquillon, dossier pres-gafam) | ✅ traité | `BACKLOG.md` T7.7 · `TESTS.md` S44, S45 | +| AC-R041 | 🐛 | 🟠 | `albert-code run` ouvre l'écran de help d'OpenCode au lieu du TUI quand la VM a une version d'OpenCode trop ancienne (ex 1.2.9) qui ne connaît pas le flag `--auto` lancé par le moteur VM (`vendor/vm/agent-vm.sh:1167`). Le runtime valide la présence d'OpenCode mais pas la compatibilité `--auto`. Source : bêta-testeur Julien Bouquillon, VM betabot2, 2026-07-21. | Bêta-testeur 2026-07-21 (Julien Bouquillon, VM betabot2) | ✅ traité | `BACKLOG.md` T8.3 · `TESTS.md` S47 | ## Notes diff --git a/TESTS.md b/TESTS.md index 160f487..f4f214c 100644 --- a/TESTS.md +++ b/TESTS.md @@ -557,3 +557,62 @@ L'opération est idempotente. ceux du ticket T6.16. Aucune ligne ne dépasse 80 colonnes. Les accents sont corrects. **Validé le :** 2026-07-21 — dry-run confirme les 4 paires explication+confirm. Textes correspondant au ticket. Aucun tiret cadratin. `bash -n lib/phases.sh` OK. + +--- + +## S46 — Merge provider Albert dans opencode.json existant (T8.2) + +**Préconditions :** `jq` installé sur le PATH ; un dossier projet avec un `opencode.json` contenant +un provider non-Albert (ex. Scaleway) et des MCP + permissions, sans `"albert"`. + +**Étapes :** +1. Créer un fichier `opencode.json` de test avec un provider Scaleway : + ```json + { + "provider": { "scaleway": { "npm": "@ai-sdk/scaleway", "name": "Scaleway", "options": { "baseURL": "https://api.scaleway.ai/v1", "apiKey": "{env:SCW_API_KEY}" } } }, + "mcp": { "data-gouv": { "type": "remote", "url": "https://mcp.data.gouv.fr/mcp", "enabled": true } }, + "permission": { "edit": "allow", "bash": {".*":"allow"} } + } + ``` +2. Lancer `bash bin/albert-code setup --dry-run` dans ce dossier. +3. Observer la sortie : proposition de merge, dry-run affiche `[dry-run] merge du provider albert dans opencode.json`. +4. Relancer en réel (`DRY_RUN=0` avec un HOME sandboxé) et répondre `o` au confirm. +5. Vérifier le fichier résultant : + - Provider `albert` présent avec ses 3 modèles + - Provider `scaleway` toujours présent (non écrasé) + - MCP `data-gouv` toujours présent + - La sauvegarde `.bak` existe et contient l'original +6. Relancer le setup : le fichier est détecté comme ayant `"albert"` → message « conservé (non écrasé) ». + +**Attendu :** (3) proposition affichée. (4) jq merge réussi. (5) les 3 vérifications passent — albert ajouté, scaleway intact, MCP intact, .bak présent. (6) idempotent, pas de duplication. Sans `jq` → avertissement T7.7 inchangé + info « installe jq ». + +## S47 — Garde-fou OpenCode --auto dans la VM (T8.3, AC-R041) + +**Préconditions :** `~/.agent-vm/runtime.sh` existe (post-install) ; une VM avec une version +ancienne d'OpenCode (ex. 1.2.9) ne supportant pas `--auto`. + +**Étapes (validation du code injecté) :** +1. Lancer `bash bin/albert-code install --dry-run` (ou setup) → le bloc marqué de `ensure_vm_runtime` + est réécrit avec le garde-fou T8.3. +2. Inspecter `~/.agent-vm/runtime.sh` (ou `/tmp/ac-test/.agent-vm/runtime.sh` sandboxé) : + le bloc `$AC_MARKER … $AC_MARKER_END` contient les lignes : + ``` + _oc_help="$(opencode --help 2>&1 || true)" + case "$_oc_help" in + *--auto*) : ;; + *) echo "OpenCode trop ancien pour --auto - mise a jour..."; opencode upgrade || true ;; + esac + unset _oc_help + ``` +3. Vérifier qu'elles sont placées APRÈS les exports et AVANT `$AC_MARKER_END`. + +**Validation réelle (VM) :** +4. Démarrer une VM où opencode est une ancienne version (simulable en renommant `opencode` ou + en installant une version antérieure) → au boot, le runtime `~/.agent-vm/runtime.sh` s'exécute : + `opencode --help` ne contient pas `--auto` → le garde-fou déclenche `opencode upgrade` → après + upgrade, `opencode --auto` fonctionne → le TUI s'affiche au lieu du help. +5. Même VM avec opencode à jour → le `case` matche `--auto` → no-op, pas d'upgrade. +6. Vérifier que le garde-fou est en `|| true` : un échec d'upgrade ne casse pas le boot. + +**Attendu :** (2)(3) le code injecté est correctement placé. (4) opencode 1.2.9 → upgrade auto → TUI. +(5) opencode à jour → no-op. (6) un échec (hors-ligne, etc.) n'empêche pas le runtime de continuer. diff --git a/lib/phases.sh b/lib/phases.sh index 64fe30e..bdc00e3 100644 --- a/lib/phases.sh +++ b/lib/phases.sh @@ -427,6 +427,20 @@ ensure_vm_runtime() { apply_append "export AC_GIT_USER_EMAIL dans runtime.sh" "$RUNTIME_VM_FILE" \ "export AC_GIT_USER_EMAIL='${safe_email}'" fi + # T8.3: Garde-fou OpenCode --auto execute dans la VM au boot + # Si opencode est trop ancien pour --auto, upgrade auto. + apply_append "garde-fou OpenCode --auto (T8.3)" "$RUNTIME_VM_FILE" \ + " _oc_help=\"\$(opencode --help 2>&1 || true)\"" + apply_append "garde-fou OpenCode --auto (T8.3)" "$RUNTIME_VM_FILE" \ + " case \"\$_oc_help\" in" + apply_append "garde-fou OpenCode --auto (T8.3)" "$RUNTIME_VM_FILE" \ + " *--auto*) : ;;" + apply_append "garde-fou OpenCode --auto (T8.3)" "$RUNTIME_VM_FILE" \ + " *) echo \"OpenCode trop ancien pour --auto - mise a jour...\"; opencode upgrade || true ;;" + apply_append "garde-fou OpenCode --auto (T8.3)" "$RUNTIME_VM_FILE" \ + " esac" + apply_append "garde-fou OpenCode --auto (T8.3)" "$RUNTIME_VM_FILE" \ + " unset _oc_help" apply_append "albert-code block end" "$RUNTIME_VM_FILE" "$AC_MARKER_END" fi @@ -682,9 +696,43 @@ scaffold_opencode_json() { if grep -q '"albert"' "$dest" 2>/dev/null; then warn "%s existe déjà — conservé (non écrasé)" "$dest" else - # T1.6 : fichier existant sans provider Albert → footgun silencieux. - warn "%s existe déjà mais ne déclare pas le provider Albert — conservé (non écrasé)." "$dest" - info "Albert ne sera pas câblé dans ce projet. Ajoute le bloc provider \"albert\" (voir README), ou renomme/supprime ce fichier puis relance albert-code setup." + # T8.2 (generalise T1.6/T7.7) : tenter le merge du provider albert + if command -v jq >/dev/null 2>&1; then + warn "%s existe déjà sans provider Albert — proposition de merge." "$dest" + info "Le provider Albert (et model/small_model) peuvent etre ajoutes sans ecraser" + info "tes MCP, permissions ou autres providers (ex. Scaleway)." + if confirm "Ajouter le provider Albert dans opencode.json ?"; then + local _bak="${dest}.bak" + apply_cp "sauvegarder ${dest} vers ${_bak}" "$dest" "$_bak" + if [ "$DRY_RUN" -eq 1 ]; then + info "[dry-run] merge du provider albert dans ${dest}" + # jq : ajoute provider.albert + model + small_model, preserve le reste. + # Le message de succes n'est affiche que si le merge a reellement abouti. + elif jq '.provider.albert = { + "npm": "@ai-sdk/openai-compatible", + "name": "Albert API (État)", + "options": {"baseURL": "https://albert.api.etalab.gouv.fr/v1", "apiKey": "{env:ALBERT_API_KEY}"}, + "models": { + "mistralai/Mistral-Medium-3.5-128B": {"name": "Mistral Medium 3.5 (Albert)", "limit": {"context": 131072, "output": 65536}}, + "deepseek-ai/DeepSeek-V4-Flash": {"name": "DeepSeek V4 Flash (Albert)", "limit": {"context": 393216, "output": 65536}}, + "Qwen/Qwen3.6-27B": {"name": "Qwen 3.6 27B (Albert)", "limit": {"context": 262144, "output": 65536}} + } + } | .model = "albert/mistralai/Mistral-Medium-3.5-128B" | .small_model = "albert/deepseek-ai/DeepSeek-V4-Flash"' "$dest" > "${dest}.tmp" 2>/dev/null && mv "${dest}.tmp" "$dest"; then + ok "Provider Albert ajoute dans ${dest}. Sauvegarde dans ${_bak}" + else + warn "Echec du merge jq (JSON invalide ? ex. commentaires) - fichier restaure, Albert non cable." + mv "$_bak" "$dest" 2>/dev/null || true + info "Ajoute le bloc provider \"albert\" manuellement (voir README)." + fi + else + info "Provider Albert non ajoute. Utilise albert-code setup plus tard ou ajoute-le manuellement." + fi + else + # Fallback T7.7 : jq absent, simple avertissement + warn "%s existe déjà mais ne déclare pas le provider Albert — conservé (non écrasé)." "$dest" + info "Albert ne sera pas câblé dans ce projet. Ajoute le bloc provider \"albert\" (voir README), ou renomme/supprime ce fichier puis relance albert-code setup." + info "Astuce : installe jq pour que albert-code puisse merger automatiquement." + fi fi return 0 fi