docs(phase-04): add verification and summary for plan 04-02

This commit is contained in:
Pierre Martin
2026-03-23 23:04:49 +01:00
parent fd246f046b
commit 64e4509241
3 changed files with 211 additions and 1 deletions

View File

@@ -76,7 +76,7 @@ Plans:
Plans:
- [x] 04-01-PLAN.md — Notifications dunst (Working->Needs Input), mode focus avec timer, CLI vmux focus
- [ ] 04-02-PLAN.md — Widget i3bar avec format compact, wrapping i3status, CLI vmux i3bar
- [x] 04-02-PLAN.md — Widget i3bar avec format compact, wrapping i3status, CLI vmux i3bar
## Progress

View File

@@ -0,0 +1,77 @@
---
phase: 04-notifications-et-i3bar
plan: 02
subsystem: cli
tags: [i3bar, i3status, json-protocol]
requires:
- phase: 04-01
provides: shortName function, Notifier, FocusTimer
provides:
- i3bar JSON widget with session status
- i3status wrapping for combined output
- CLI vmux i3bar subcommand
affects: []
tech-stack:
added: []
patterns: [i3bar JSON protocol, i3status wrapper, TTY detection for mode selection]
key-files:
created: [i3bar.go, i3bar_test.go]
modified: [main.go]
key-decisions:
- "TTY detection: standalone mode in terminal, wrapped mode when called by i3bar"
- "Format: vmux: auth[!] portal[W] neia[I] with shortName from notify.go"
patterns-established:
- "i3bar JSON protocol: header + infinite array of block arrays"
requirements-completed: [I3-03]
duration: 5min
completed: 2026-03-23
---
# Plan 04-02 Summary
**Widget i3bar avec liste courte des sessions, wrapping i3status, détection TTY pour mode standalone/wrapped**
## Performance
- **Duration:** 5 min
- **Tasks:** 3 (2 code + 1 human-verify)
- **Files modified:** 3
## Accomplishments
- formatI3BarBlocks : format compact `vmux: auth[!] portal[W]`
- runI3Bar : standalone (TTY) ou wrapped (pipe/i3bar)
- 10 tests unitaires pour le formatage
- TTY detection pour éviter le fallback i3status texte brut
## Task Commits
1. **Task 1: Formatage i3bar + tests** - `a28e8d6`
2. **Task 2: Boucle i3bar + CLI** - `221a444`
3. **Task 3: Human verification** - approved
4. **Fix TTY detection** - uncommitted (sera commité avec la validation globale)
## Files Created/Modified
- `i3bar.go` — I3BarBlock, formatI3BarBlocks, queryVmuxBlock, runI3Bar (standalone + wrapped)
- `i3bar_test.go` — 10 tests unitaires
- `main.go` — case "i3bar" + isTerminal()
## Decisions Made
- Mode standalone quand stdout est un TTY, wrapped quand c'est un pipe
- Pas de dépendance externe pour la détection TTY (os.ModeCharDevice)
## Deviations from Plan
- Ajout de isTerminal() pour fixer le bug i3status plain text
## Next Phase Readiness
- Dernière phase. Projet v1 complet.
---
*Phase: 04-notifications-et-i3bar*
*Completed: 2026-03-23*

View File

@@ -0,0 +1,133 @@
---
phase: 04-notifications-et-i3bar
verified: 2026-03-23T21:00:00Z
status: human_needed
score: 3/3 must-haves verified
human_verification:
- test: "Lancer une session Claude Code active, declencher une transition Working -> Needs Input via un hook reel"
expected: "Une notification dunst apparait dans la barre de notifications"
why_human: "Necessite notify-send + dunst installe et display actif. Impossible a tester sans env graphique."
- test: "Configurer i3bar avec status_command = vmux i3bar, puis recharger i3"
expected: "Un bloc vmux apparait dans la barre i3 avec le statut des sessions. Couleur rouge si une session attend, verte sinon."
why_human: "Necessite un environnement i3 en cours d'execution avec le daemon vmux actif."
- test: "Tester vmux i3bar en standalone: lancer ./vmux i3bar dans un terminal"
expected: "Header {\"version\":1}, puis [, puis lignes JSON avec bloc vmux toutes les 2 secondes"
why_human: "Necessite que le daemon tourne. Verification du format visuel en temps reel."
---
# Phase 4: Notifications et i3bar - Rapport de Verification
**Phase Goal:** L'utilisateur est notifie passivement quand une session a besoin de lui, sans ouvrir vmux
**Verifie:** 2026-03-23
**Statut:** human_needed
**Re-verification:** Non (verification initiale)
## Resultats par critere de succes
### Verites observables
| # | Verite | Statut | Preuve |
|---|--------|--------|--------|
| 1 | Une notification dunst apparait quand une session passe de "Working" a "Needs Input" | VERIFIED | `hook.go:84` — condition `state == "Needs Input" && prevState == "Working" && !d.focus.IsActive()` + `d.notifier.Notify(...)`. Tests `TestNotification_WorkingToNeedsInput` et `TestNotification_FocusExpired` passent. |
| 2 | Le mode focus supprime temporairement les notifications (`vmux focus <minutes>`) | VERIFIED | `main.go:105-130``case "focus"` parse les minutes, envoie `FocusArgs` au daemon. `daemon.go:446-453``d.focus.Set(...)`. `hook.go:84``!d.focus.IsActive()` bloque la notif. Test `TestFocusHandler` + `TestNotification_FocusActive` passent. |
| 3 | Un widget i3bar affiche en temps reel le nombre de sessions et combien attendent de l'input | VERIFIED | `i3bar.go` complet avec `formatI3BarBlocks`, `queryVmuxBlock`, `runI3Bar` (standalone + wrapped). `main.go:145-158``case "i3bar"` avec detection TTY. 10 tests dans `i3bar_test.go` passent. |
**Score:** 3/3 verites verifiees
### Artefacts requis
| Artefact | Fournit | Niveau 1 (Existe) | Niveau 2 (Substantiel) | Niveau 3 (Cable) | Statut |
|----------|---------|-------------------|------------------------|------------------|--------|
| `notify.go` | `Notifier` interface, `ExecNotifier`, `NullNotifier`, `shortName` | Oui | 44 lignes, implementation reelle avec `exec.Command` + timeout 5s | Importe via `d.notifier` dans `daemon.go` et `hook.go` | VERIFIED |
| `notify_test.go` | Tests `ExecNotifier`, `NullNotifier`, `shortName` | Oui | 5 tests substantiels | N/A (test file) | VERIFIED |
| `focus.go` | `FocusTimer` thread-safe (Set/IsActive/Remaining) | Oui | 38 lignes, mutex, logique correcte | Utilise dans `daemon.go` champ `focus *FocusTimer` et `hook.go` | VERIFIED |
| `focus_test.go` | Tests `FocusTimer` | Oui | 4 tests couvrant Set/Expired/Remaining/ZeroValue | N/A (test file) | VERIFIED |
| `i3bar.go` | `I3BarBlock`, `formatI3BarBlocks`, `queryVmuxBlock`, `runI3Bar` | Oui | 177 lignes, protocole i3bar complet, wrapping i3status | Appele depuis `main.go case "i3bar"` | VERIFIED |
| `i3bar_test.go` | Tests formatage i3bar | Oui | 10 tests couvrant tous les cas | N/A (test file) | VERIFIED |
### Verification des liens cles (Niveau 3)
| De | Vers | Via | Statut | Detail |
|----|------|-----|--------|--------|
| `hook.go` | `notify.go` | `d.notifier.Notify()` apres transition Working -> Needs Input | WIRED | `hook.go:88``d.notifier.Notify("vmux: "+shortName(info), ...)` |
| `hook.go` | `focus.go` | `d.focus.IsActive()` pour bloquer les notifications | WIRED | `hook.go:84``!d.focus.IsActive()` dans la condition |
| `main.go` | `daemon.go` | `case "focus"` envoie action focus au daemon | WIRED | `main.go:105``case "focus"`, `daemon.go:446``case "focus"` |
| `i3bar.go` | `protocol.go` | `formatI3BarBlocks` consomme `[]SessionInfo` | WIRED | `i3bar.go:24``func formatI3BarBlocks(sessions []SessionInfo)` |
| `main.go` | `i3bar.go` | `case "i3bar"` appelle `runI3Bar` | WIRED | `main.go:145``case "i3bar"`, `main.go:158``runI3Bar(sockPath, i3statusCmd)` |
### Trace de flux de donnees (Niveau 4)
| Artefact | Variable | Source | Produit des donnees reelles | Statut |
|----------|----------|--------|-----------------------------|--------|
| `i3bar.go` | `sessions []SessionInfo` | `queryVmuxBlock` -> `client.Send(Request{Action: "list"})` -> daemon socket | Oui — le daemon repond avec les sessions actives depuis le registre | FLOWING |
| `hook.go` | `prevState` | `d.registry.sessions[event.SessionID].PrevState` | Oui — lu depuis le registre avant `UpdateFromHook` | FLOWING |
### Verifications comportementales (Spot-checks)
| Comportement | Commande | Resultat | Statut |
|--------------|----------|----------|--------|
| Build propre | `nix-shell -p go --run "go build -o /dev/null ."` | `build OK` | PASS |
| Suite de tests complete avec race detector | `nix-shell -p go --run "go test ./... -count=1 -race"` | `ok github.com/pieMusic/vmux 7.169s` | PASS |
| Tests notification (5 tests) | `go test -run 'TestNotification\|TestFocusHandler'` | Tous PASS | PASS |
| Tests FocusTimer (4 tests) | `go test -run 'TestFocusTimer'` | Tous PASS | PASS |
| Tests shortName (4 tests) | `go test -run 'TestShortName'` | Tous PASS | PASS |
| Tests formatI3Bar (10 tests) | `go test -run 'TestFormatI3Bar'` | Tous PASS | PASS |
| notification dunst reelle | Necessite display actif | Non testable en CI | SKIP |
| widget i3bar dans barre i3 | Necessite i3 + daemon actif | Non testable en CI | SKIP |
### Couverture des requirements
| Requirement | Plan source | Description | Statut | Preuve |
|-------------|-------------|-------------|--------|--------|
| NOTIF-01 | 04-01-PLAN.md | vmux notifie (dunst) quand une session passe de "travaille" a "attend input" | SATISFIED | `hook.go:84-88` — condition + appel `d.notifier.Notify`. Tests `TestNotification_WorkingToNeedsInput`, `TestNotification_IdleToNeedsInput` (negatif). |
| NOTIF-02 | 04-01-PLAN.md | vmux supporte un mode focus qui supprime temporairement les notifications | SATISFIED | `focus.go``FocusTimer`. `daemon.go:446-453` — handler "focus". `main.go:105-130` — CLI `vmux focus <minutes>`. Test `TestNotification_FocusActive`. |
| I3-03 | 04-02-PLAN.md | vmux fournit un widget i3bar affichant le statut des sessions en temps reel | SATISFIED (code OK, verification visuelle necessaire) | `i3bar.go` complet. `main.go:145-158``case "i3bar"`. 10 tests passent. Note: `REQUIREMENTS.md` a encore `[ ]` au lieu de `[x]` pour I3-03 — inconsistance documentaire. |
**Note:** `REQUIREMENTS.md` marque encore I3-03 comme `Pending` (`[ ]` a la ligne 28 et `Pending` dans la table de tracabilite ligne 79). L'implementation est complete mais le fichier n'a pas ete mis a jour. Ce n'est pas un gap fonctionnel.
### Verite non testee du prompt de verification
Le prompt indique `vmux focus on/off` comme critere de succes. Le ROADMAP.md dit `vmux focus <minutes>`. L'implementation utilise un timer (pas un toggle on/off), ce qui est conforme au ROADMAP et aux decisions de conception D-04. La formulation `on/off` dans le prompt est incorrecte — le critere reel est satisfait.
### Anti-patterns detectes
Aucun anti-pattern bloquant. `TestExecNotifier_CallsNotifySend` dans `notify_test.go` est minimaliste (verifie uniquement l'interface, pas l'appel reel a `notify-send`) mais c'est une limitation justifiee : le test reel necessite un display actif, il est delogue en verification manuelle.
### Verification humaine requise
#### 1. Notification dunst lors d'une transition reelle
**Test:** Avec un daemon vmux actif et dunst qui tourne, declencher un hook `Stop` sur une session en etat "Working" (par exemple en laissant Claude Code finir une tache).
**Attendu:** Une notification dunst apparait avec le titre `vmux: <nom-session>` et le corps `Session needs input (question)`.
**Pourquoi humain:** Necessite `notify-send` installe, un display X11/Wayland actif, et dunst en cours d'execution. L'environment de CI n'a pas ces prerequis.
#### 2. Widget i3bar dans la barre i3
**Test:** Ajouter `status_command /chemin/vers/vmux i3bar` dans `~/.config/i3/config`, recharger i3 (`i3-msg reload`), s'assurer que le daemon vmux tourne.
**Attendu:** Un bloc `vmux: all working (N)` (vert) ou `vmux: nom[!]` (rouge) apparait dans la barre i3 aux cotes des infos systeme de i3status. Rafraichissement toutes les 2 secondes.
**Pourquoi humain:** Necessite un environnement i3 actif avec le daemon et au moins une session Claude Code.
#### 3. Mode standalone vmux i3bar
**Test:** Dans un terminal, lancer `./vmux i3bar` (avec le daemon actif).
**Attendu:** Affichage du header `{"version":1}`, puis `[`, puis une ligne JSON toutes les 2 secondes du type `[{"full_text":"vmux: all working (N)","color":"#00ff00","name":"vmux"}]`.
**Pourquoi humain:** Necessite le daemon actif et des sessions detectees. La sortie visuelle est difficile a valider automatiquement en mode interactif.
---
## Resume
Phase 4 atteint son objectif fonctionnel. Les trois mecanismes cles sont implementes, cables et testes :
1. **Notifications** : `ExecNotifier` appelle `notify-send --urgency=critical` sur la seule transition pertinente (Working -> Needs Input). Les 4 scenarios de notification (normal, idle->needsinput, focus actif, focus expire) sont couverts par des tests avec `spyNotifier`.
2. **Mode focus** : `FocusTimer` thread-safe avec Set/IsActive/Remaining. CLI `vmux focus <minutes>` fonctionne de bout en bout via le daemon. Le focus bloque les notifications mais pas l'i3bar (D-05 respecte).
3. **Widget i3bar** : `formatI3BarBlocks` produit le format compact `vmux: auth[!] portal[W]` avec les bonnes couleurs. `runI3Bar` implemente le protocole i3bar v1 en mode standalone et en mode wrapping i3status. La detection TTY evite le fallback texte brut.
La seule chose non verifiable automatiquement est le rendu visuel final dans l'environnement graphique (dunst + i3bar).
---
_Verifie : 2026-03-23_
_Verificateur : Claude (gsd-verifier)_