Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,45 @@ Config MCP de référence :
**But :** l'utilisateur ne voit plus « agent-vm » dans les messages, juste des références à « la VM isolée » / « le moteur de VM d'Albert Code ».
**DoD :** tous les messages utilisateur dans `lib/phases.sh`, `lib/ui.sh`, `install.sh`, `README.md` sont reformulés. Le nom « agent-vm » reste dans les commentaires de code, la LICENSE et `vendor/vm/`.

### T7.6 🟠 Ne pas repasser les flags ressources au `run` si la VM projet tourne déjà `<- AC-R037`
**But :** sur une VM projet déjà `Running`, chaque `albert-code run` affiche le prompt agent-vm « VM is currently running. It must be stopped to apply new resource settings. Stop the VM and apply changes? [y/N] » alors que rien ne change (ressources demandées = ressources courantes). Faux positif agaçant à chaque lancement.

**Cause :** `phase_run` passe toujours `--cpus/--memory/--disk` (`lib/phases.sh:236`), et le vendored ne compare jamais aux valeurs courantes : `vendor/vm/agent-vm.sh:374` déclenche dès qu'un flag ressource est présent ET que la VM tourne (le commentaire « if ... changed » est faux). Le vendored est figé (pin 6f20194) → correction côté Albert Code uniquement.

> ⚠️ **Post-mortem v1 (tentative du 21/07 — NE PAS refaire) :** premier correctif = `if _agent_vm_running "$(_agent_vm_name)"; then _vm opencode; else _vm --cpus … ; fi`. **Échec en run réel** : le prompt revenait. Trace `bash -x` (`/tmp/tr.txt`) : le nom dérivé était bon (`agent-vm-albert-code-b9ed5d09`), mais `_agent_vm_running` renvoyait **faux** → branche `else` → flags → prompt. Cause : `_agent_vm_running` fait `limactl list --format … | grep -q`, et `bin/albert-code` tourne sous `set -o pipefail` (l.11). Quand `grep -q` matche la 1re ligne et ferme le pipe, `limactl` (encore en écriture de la 2e VM) prend un SIGPIPE, sort en 141, `pipefail` fait échouer tout le pipe → **faux négatif intermittent** (course, dépend de la charge). Preuve dans le trace : la MÊME fonction, même VM, renvoie **vrai** 100 lignes plus loin (`agent-vm.sh:376`) car appelée **dans** le sous-shell `_vm` où `set +o pipefail` est actif (l.31). C'est exactement pourquoi `_vm()` enveloppe le vendored dans `( set +u +e +o pipefail; … )`.
> **Leçon : ne jamais appeler un helper vendored en `… | grep -q` directement sous `pipefail`.**

**Tâches (v2 — correctif retenu) :**
1. Dans `phase_run()` (`lib/phases.sh`), remplacer l'appel unique `_vm --cpus … --memory … --disk … opencode` par une détection **sans pipe** : capturer la liste des VM d'abord, puis test bash pur. Basculer de « déjà **Running** » vers « VM projet **déjà présente** » (les ressources sont figées à la création → inutile de les repasser quel que soit le statut ; évite aussi de dépendre du format `Name Status`).
2. Ne PAS réutiliser `_agent_vm_running` (fragile sous `pipefail`, cf. post-mortem). Réutiliser `_agent_vm_name` (`vendor/vm/agent-vm.sh:137`) pour le nom (OK, pas de pipe fragile).
3. Forme cible :
```sh
local _vm_name _vm_list
_vm_name="$(_agent_vm_name)"
# Capture d'abord (aucun pipe → immunisé au SIGPIPE/pipefail, cf. post-mortem v1),
# puis test en bash pur. `|| true` : une sortie limactl vide ne casse pas set -e.
_vm_list="$(limactl list -q 2>/dev/null || true)"
case $'\n'"$_vm_list"$'\n' in
*$'\n'"$_vm_name"$'\n'*)
info "VM déjà créée — rattachement sans re-réglage des ressources."
apply "lancer la VM isolée" _vm opencode ;;
*)
apply "lancer la VM isolée" _vm --cpus "${EFF_CPUS}" --memory "${EFF_MEM}" --disk "${AC_VM_DISK}" opencode ;;
esac
```
4. Garder `compute_effective_vm_resources` appelé avant (message « Ressources hôte détectées ») : on ne fait que conditionner l'usage de `EFF_*`.

**Règles :**
- Ne PAS toucher `vendor/vm/` (vendored figé).
- **Aucun `… | grep` dans le chemin de décision** : capture-first + `case`. C'est le cœur du fix.
- Bash 3.2 compatible / `set -euo pipefail` : `_agent_vm_name` (`${1:-$(pwd)}`) sûr ; `limactl … || true` neutralise `set -e` ; `case` en bash pur, pas de pipe.
- Ne pas canonicaliser `pwd` à la main (laisser `_agent_vm_name` faire).
- Accents FR corrects, pas de tiret cadratin, non-destructif, dry-run OK.

**DoD :** VM projet **présente** (Running **ou** Stopped) → `albert-code run` n'affiche plus « must be stopped » et lance OpenCode directement (message « rattachement »). VM **inexistante** (1er run du projet) → flags passés, la VM projet est créée dimensionnée. Validation en run **réel** (le bug ne se voit pas en test isolé à froid : course SIGPIPE, cf. post-mortem). → `TESTS.md` S41 (VM running → plus de prompt, message rattachement) + S42 (VM inexistante → flags passés, création OK).

**Extension (même racine SIGPIPE) :** `base_vm_exists()` portait le même anti-pattern `limactl list -q | grep -q '^agent-vm-base$'`, et il est appelé par `phase_run`/`check_base_vm` en amont → un faux négatif reproposait la création de la VM de base à chaque `run` (symptôme observé chez un bêta-testeur). Aligné sur le même correctif capture-first + `case` bash pur. → `TESTS.md` S43.

---

## EPIC 6 — Interface 3 verbes & simplification profils `<- AC-R014, AC-R015, AC-R016, AC-R017`
Expand Down
52 changes: 52 additions & 0 deletions TESTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,58 @@ et le bloc marqueur a disparu.
4. Coller un token valide.
**Attendu :** le script dérive l'identité, affiche `✓ Compte GitHub : <login> <<id>+<login>@users.noreply.github.com>` et persiste. Pas de fallback manuel, pas de prompts nom/email.

## S41 — VM présente → rattachement sans prompt ressource (T7.6, AC-R037, correctif v2)

**Préconditions :** VM projet `agent-vm-<projet>-<hash>` déjà présente dans `limactl list -q`
(Running **ou** Stopped). `bin/albert-code run` exécutable depuis un dossier projet.

**Étapes :**
1. S'assurer que la VM projet est listée : `limactl list -q | grep 'agent-vm-<projet>'`.
2. Lancer `bash bin/albert-code run --dry-run`.
3. Observer la sortie.

**Attendu :** la sortie contient `✓ lancer la VM isolée : _vm opencode` **sans** `--cpus`,
`--memory`, ni `--disk`. Le message `VM déjà créée — rattachement sans re-réglage des ressources.`
est affiché. Aucun prompt « must be stopped to apply new resource settings » ne provient
d'agent-vm — car la condition testée est **présence** (pas `Running`), et le chemin de décision
est capture-first + `case` bash pur (zéro pipe, immunisé SIGPIPE/pipefail). Exit 0.
**Validation réelle** (le bug est une course SIGPIPE, pas reproductible à froid) :
`albert-code run` sur la VM déjà Running ne doit **plus** afficher le prompt « must be stopped ».
Si le prompt réapparaît, tracer avec `bash -x bin/albert-code run 2>/tmp/tr.txt` → vérifier
que la branche `*)` (flags) n'est pas prise.

## S42 — VM inexistante → flags ressources passés (T7.6, AC-R037, correctif v2)

**Préconditions :** VM projet absente de `limactl list -q` (jamais créée pour ce projet).
`bin/albert-code run` exécutable depuis un dossier projet.

**Étapes :**
1. Confirmer que la VM projet n'apparaît pas : `limactl list -q | grep -c 'agent-vm-<projet>'` → 0.
2. Lancer `bash bin/albert-code run --dry-run`.
3. Observer la sortie.

**Attendu :** la sortie contient
`✓ lancer la VM isolée : _vm --cpus "${EFF_CPUS}" --memory "${EFF_MEM}" --disk "${AC_VM_DISK}" opencode`
avec les valeurs calculées de `EFF_CPUS`, `EFF_MEM`, `AC_VM_DISK`. Pas de message
« VM déjà créée ». Comportement identique à celui d'avant T7.6.

## S43 — `base_vm_exists` détecte la VM de base sans faux négatif (T7.6, AC-R037, même racine SIGPIPE)

**Préconditions :** VM de base `agent-vm-base` présente dans `limactl list -q` (Running ou Stopped).

**Étapes :**
1. Vérifier que la base est listée : `limactl list -q | grep -c '^agent-vm-base$'` → 1.
2. Exécuter la fonction dans le contexte réel du binaire, en boucle pour attraper la course :
```sh
source lib/phases.sh 2>/dev/null # ou reproduire base_vm_exists()
r=""; for i in $(seq 20); do ( set -euo pipefail; base_vm_exists ) && r="${r}T" || r="${r}F"; done; echo "$r"
```

**Attendu :** `TTTTTTTTTTTTTTTTTTTT` (20/20). Aucun `F`. Avant correctif (`limactl list -q | grep -q`),
un `F` intermittent sous `set -o pipefail` reproposait la création de la VM de base à chaque `run`.
Le correctif capture d'abord (`_list="$(limactl list -q 2>/dev/null || true)"`) puis teste par `case`
bash pur — zéro pipe, donc pas de course SIGPIPE.

**S40e — Fallback quand token invalide au 3e essai**
**Préconditions :** `install.sh` disponible, token invalide.
**Étapes :**
Expand Down
29 changes: 26 additions & 3 deletions lib/phases.sh
Original file line number Diff line number Diff line change
Expand Up @@ -228,12 +228,24 @@ phase_run() {
fi
fi

# Lancer la VM
# Lancer la VM (ou rattachement si déjà créée — pas de pipe, cf. post-mortem v1)
echo
info "Ouverture de la bulle isolée…"
info " Albert Code lance OpenCode dans la VM"
echo
apply "lancer la VM isolée" _vm --cpus "${EFF_CPUS}" --memory "${EFF_MEM}" --disk "${AC_VM_DISK}" opencode

local _vm_name _vm_list
_vm_name="$(_agent_vm_name)"
# Capture d'abord (aucun pipe → immunisé SIGPIPE/pipefail, cf. T7.6 post-mortem).
# `|| true` : sortie vide ne casse pas set -e.
_vm_list="$(limactl list -q 2>/dev/null || true)"
case $'\n'"$_vm_list"$'\n' in
*$'\n'"$_vm_name"$'\n'*)
info "VM déjà créée — rattachement sans re-réglage des ressources."
apply "lancer la VM isolée" _vm opencode ;;
*)
apply "lancer la VM isolée" _vm --cpus "${EFF_CPUS}" --memory "${EFF_MEM}" --disk "${AC_VM_DISK}" opencode ;;
esac
}

# =============================================================================
Expand All @@ -258,8 +270,19 @@ check_base_vm() {
}

# base_vm_exists — 0 si la VM de base existe
# Détection sans pipe : `limactl list -q | grep -q` est un faux négatif
# intermittent sous set -o pipefail (course SIGPIPE, cf. T7.6 post-mortem) —
# grep -q ferme le pipe, limactl prend un SIGPIPE, pipefail fait échouer le tout.
# base_vm_exists est appelé dans phase_run/check_base_vm : un faux négatif
# reproposait la création de la VM de base à chaque run. Capture d'abord, case pur.
base_vm_exists() {
command -v limactl >/dev/null 2>&1 && limactl list -q 2>/dev/null | grep -q '^agent-vm-base$'
command -v limactl >/dev/null 2>&1 || return 1
local _list
_list="$(limactl list -q 2>/dev/null || true)"
case $'\n'"$_list"$'\n' in
*$'\n'agent-vm-base$'\n'*) return 0 ;;
*) return 1 ;;
esac
}

# install_agent_vm — vérifie que le bundle vendored est présent (plus de clone).
Expand Down
Loading