bots-garden/mini-mepublic Fork
⑂ ad83d0e
Commits
⬇ Clone ▾
git clone https://git.rickub.com/bots-garden/mini-me.git
git clone ssh://git@rickub.com/bots-garden/mini-me.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

🛟 Updated.

k33g committed 2026-10-25T11:29:31+01:00 Browse files
ad83d0e parent: 9a5c4ec
modified .tickets/issues/0152-dev-team.yaml +304 -2
@@ -6,7 +6,7 @@ author:
66 name: k33g
77 email: ph.charriere@gmail.com
88 createdAt: 2026-10-25T04:44:30.351Z
9-updatedAt: 2026-10-25T05:15:11.308Z
9+updatedAt: 2026-10-25T12:30:00.000Z
1010 labels: []
1111 epic: Demos
1212 body: |+
@@ -53,7 +53,52 @@ body: |+
5353
5454
5555
56-tasks: []
56+tasks:
57+ - id: 1
58+ title: "Créer ./dev.team : 5 agents (coordinator, reformulator, tutor, coder, reviewer), hook de déchargement, projet d'essai"
59+ state: closed
60+ priority: none
61+ author:
62+ name: k33g
63+ email: ph.charriere@gmail.com
64+ createdAt: 2026-10-25T07:30:00.000Z
65+ updatedAt: 2026-10-25T07:30:00.000Z
66+ - id: 2
67+ title: "Mesurer chaque agent seul (mm -p) sur des exemples réels, ajuster prompts et réglages"
68+ state: closed
69+ priority: none
70+ author:
71+ name: k33g
72+ email: ph.charriere@gmail.com
73+ createdAt: 2026-10-25T07:30:00.000Z
74+ updatedAt: 2026-10-25T07:30:00.000Z
75+ - id: 3
76+ title: "Mesurer l'équipe de bout en bout via le coordinateur, vérifier le déchargement des modèles (/api/ps)"
77+ state: closed
78+ priority: none
79+ author:
80+ name: k33g
81+ email: ph.charriere@gmail.com
82+ createdAt: 2026-10-25T07:30:00.000Z
83+ updatedAt: 2026-10-25T07:30:00.000Z
84+ - id: 4
85+ title: "Répondre à la question du modèle de décision (mesure du routage du coordinateur)"
86+ state: closed
87+ priority: none
88+ author:
89+ name: k33g
90+ email: ph.charriere@gmail.com
91+ createdAt: 2026-10-25T07:30:00.000Z
92+ updatedAt: 2026-10-25T07:30:00.000Z
93+ - id: 5
94+ title: "README EN + FR avec diagrammes fancy et exemples d'utilisation ; ligne dans le README racine"
95+ state: closed
96+ priority: none
97+ author:
98+ name: k33g
99+ email: ph.charriere@gmail.com
100+ createdAt: 2026-10-25T07:30:00.000Z
101+ updatedAt: 2026-10-25T07:30:00.000Z
57102 comments:
58103 - id: 1
59104 author:
@@ -172,3 +217,260 @@ comments:
172217 name.
173218
174219 ```
220+ - id: 2
221+ author:
222+ name: k33g
223+ email: ph.charriere@gmail.com
224+ createdAt: 2026-10-25T07:30:00.000Z
225+ body: |
226+ Plan (2026-10-25), avant de coder.
227+
228+ ## Ce qui existe déjà et que je réutilise
229+ - demos/team : un coordinateur + 3 spécialistes via `ask_agent` (mmConfigs).
230+ - demos/unload-models/hooks/unload.js : hooks `preAsk` (décharge le modèle
231+ de l'agent qui demande, juste avant de lancer l'agent demandé) et
232+ `postAsk` (décharge le modèle de l'agent demandé une fois sa réponse
233+ reçue). C'est une garantie, pas une consigne au modèle : la doc
234+ unload-a-model mesure qu'un petit modèle oublie d'appeler unload_model
235+ (4 fois sur 5 avec MiniCPM5 2B). Deux modèles ne sont donc jamais
236+ chargés ensemble.
237+ - Les trois modèles demandés sont déjà dans llmman : Qwen3.5-4B Q4_K_M
238+ (3,4 Go), gemma-4-12b-it Q4_K_M (7,3 Go), Mellum2-12B-A2.5B-Instruct
239+ Q4_K_M (8,1 Go). Avec le déchargement, le pic mémoire est un seul
240+ modèle de 12B (~8 Go + cache KV), à l'aise sur 32 Go.
241+
242+ ## Agents (noms et fichiers en anglais, titres avec emoji)
243+ - coordinator (Qwen3.5-4B, sans raisonnement) : seulement `ask_agent`.
244+ - reformulator (gemma-4-12b) : `ask_user` activé (et `askUser: true` dans
245+ mmConfigs pour que ses questions remontent à l'utilisateur), aucun outil
246+ qui modifie.
247+ - tutor (Mellum2 Instruct Q4_K_M) : la config « prof » du commentaire 1,
248+ adaptée (modèle Q4_K_M au lieu de Q8_0 comme le ticket le demande).
249+ - coder (Mellum2 Instruct Q4_K_M) : bash, fichiers, plan.
250+ - reviewer (gemma-4-12b) : bash pour lancer les tests, écrit des tests,
251+ ne modifie pas le code de l'application.
252+
253+ Tutor et coder partagent le même modèle, reformulator et reviewer aussi :
254+ passer de l'un à l'autre ne recharge rien.
255+
256+ ## Question du modèle de décision
257+ À trancher par la mesure : si le coordinateur 4B route bien (prof / code /
258+ reformulation), un décideur n'apporte rien ; s'il se trompe, un décideur
259+ de choix dans un hook preGeneration (comme demos/decision-chat) peut lui
260+ donner la route.
261+ - id: 3
262+ author:
263+ name: k33g
264+ email: ph.charriere@gmail.com
265+ createdAt: 2026-10-25T07:45:00.000Z
266+ body: |
267+ Tâche 1 faite (non commitée) : ./dev.team
268+ - agent.yaml (coordinator, 🧭 Coordinator), agent.reformulator.yaml
269+ (📝), agent.coder.yaml (🛠️), agent.reviewer.yaml (🔎),
270+ agent.tutor.yaml (📘, la config « prof » adaptée : Q4_K_M, sans lsp
271+ Rust ni docSearch — pas de code Rust ni de docs/ ici).
272+ - hooks/unload.js (copie de demos/unload-models) branché sur preAsk
273+ (self) et postAsk (asked) du coordinateur ; unloadModels: false pour
274+ que le modèle n'ait pas à y penser.
275+ - starter/textstats : un petit paquet Go avec tests, un bug connu
276+ (WordCount et les espaces multiples) et de la place pour une
277+ fonctionnalité ; `sh reset.sh` le copie dans playground/ et en fait
278+ un dépôt git, pour que le reviewer lise `git diff`.
279+ - Deux diagrammes (dev-team-flow, dev-team-memory) générés par
280+ .claude/skills/drawio-fancy-diagram/generator/examples/dev_team.py.
281+
282+ Tâche 2 en cours. Première mesure du coder seul (bug WordCount) :
283+ Mellum2 Q4_K_M a produit un appel d'outil coupé à max_tokens
284+ (« tool call arguments did not parse … unexpected end of JSON
285+ input ») après un edit_file raté — à suivre.
286+ - id: 4
287+ author:
288+ name: k33g
289+ email: ph.charriere@gmail.com
290+ createdAt: 2026-10-25T08:30:00.000Z
291+ body: |
292+ Tâche 2 faite — chaque agent seul, mesuré sur llmman :
293+ - coder (Mellum2) : bug WordCount corrigé (strings.Fields) + 4 cas de
294+ test, tests verts, 351 s. Un appel d'outil jamais refermé a couru
295+ jusqu'à max_tokens (8192) : max_tokens ramené à 4096 ; gofmt ajouté au
296+ prompt (le test ajouté n'était pas formaté).
297+ - reviewer (Gemma 4 12B) : git diff, tests, review_test.go à lui,
298+ « VERDICT: PASS », 151 s.
299+ - tutor (Mellum2) : 24 s, mais il a inventé « un fichier typique » au
300+ lieu de lire playground/textstats_test.go. Règle ajoutée (read_file
301+ d'abord, citer le vrai code) : 3 sur 3 lisent le fichier et citent la
302+ vraie table map[string]int, 12 à 19 s.
303+ - reformulator (Gemma 4 12B) : appelle bien ask_user ; avec `-p`
304+ personne ne peut répondre, et la 1re version décidait seule (et
305+ mettait « top N » hors périmètre pour « les mots les plus
306+ fréquents »). Prompt resserré + section ASSUMPTIONS : il liste
307+ maintenant ses choix (top 5, casse ignorée…), 62 s.
308+ - unload_model retiré des 4 membres (unloadModels: false) : ce sont les
309+ hooks du coordinateur qui déchargent.
310+
311+ Tâche 4 faite — faut-il un modèle de décision ?
312+ Mesure du routage du coordinateur Qwen3.5-4B sur 12 demandes (5 code,
313+ 5 leçons dont 2 en français, 2 revues) : 12/12 vers le bon agent. Un
314+ décideur pour router n'apporterait donc rien ici, et coûterait un modèle
315+ et un appel de plus. Où il serait utile, à mesurer si besoin : détecter
316+ une demande vague avant d'appeler le reformulateur (noul), ou trier le
317+ verdict du reviewer — pas nécessaire tant que le prompt suffit.
318+ Effet de bord vu pendant la mesure : quand ask_agent est refusé, le 4B
319+ essaie un autre agent au lieu de s'arrêter (sans conséquence avec
320+ yolo: true).
321+ - id: 5
322+ author:
323+ name: k33g
324+ email: ph.charriere@gmail.com
325+ createdAt: 2026-10-25T10:00:00.000Z
326+ body: |
327+ Tâche 3 en cours — l'équipe de bout en bout (coordinateur, `-p`,
328+ AGENT_YOLO=true), /api/ps échantillonné toutes les 2 s :
329+
330+ - Déchargement : sur les 4 exécutions, jamais deux modèles chargés en
331+ même temps (0 échantillon avec deux modèles). Séquence typique :
332+ 4B → Gemma → 4B → Mellum2 → 4B → Gemma → 4B.
333+ - Route A, bug WordCount, 1re exécution : 685 s, PASS, mais le coder a
334+ bouclé sur `plan update` (appels en échec) et le coordinateur l'a
335+ rappelé ; son rapport final disait à tort « aucune modification ».
336+ → planTool: false pour le coder (le brief tient lieu de plan), le
337+ reviewer vérifie gofmt et les cas de test supprimés, le rapport
338+ reprend les fichiers des réponses du coder et du reviewer.
339+ - Route A, bug WordCount, 2e exécution : 398 s, un appel par agent,
340+ gofmt propre, VERDICT: PASS, rapport exact. Reste : le coder a
341+ réécrit des cas de test existants au lieu d'en ajouter seulement.
342+ - Route B (leçon en français) : 49 s, tutor seul, réponse juste en
343+ français.
344+ - Route A, nouvelle fonction « mots les plus fréquents » : ÉCHEC après
345+ 52 min. Le brief était bon (top 5, ponctuation, égalités par ordre
346+ alphabétique, ASSUMPTIONS) ; puis le reviewer a testé un comportement
347+ absent du brief (garder les apostrophes) et atteint ses 20 tours, le
348+ 4B a rappelé le reviewer puis le coder 4 fois (malgré la consigne),
349+ et les edit_file de Mellum2 échouaient en série. Tests rouges à la
350+ fin.
351+ → reviewer : tests limités à ACCEPTANCE/REQUIREMENTS, maxTurns 12 ;
352+ coordinateur : maxTurns 6, « ne jamais redemander à un agent ».
353+
354+ Je remets en question un choix de modèle, comme le ticket le permet :
355+ je compare le coder Mellum2 à Ornith 1.5 9B Q4_K_M (6,7 Go, 5 sur 5 en
356+ bout en bout dans demos/team) sur le même brief, chacun seul, avec un
357+ test caché tiré de l'ACCEPTANCE du brief.
358+ - id: 6
359+ author:
360+ name: k33g
361+ email: ph.charriere@gmail.com
362+ createdAt: 2026-10-25T10:30:00.000Z
363+ body: |
364+ Tâches 3 et 5 faites — l'état final (non commité, à relire).
365+
366+ ## Comparaison des coders (le ticket permet de discuter les modèles)
367+ Chacun seul sur le même brief (« mots les plus fréquents »), vérifié par
368+ un test caché tiré de l'ACCEPTANCE du brief, go vet et gofmt :
369+ - Mellum2 12B-A2.5B : 2 sur 3, 129 s, 132 s, 639 s (l'échec : appels
370+ d'outil coupés à max_tokens, test qui compare des slices avec !=) ;
371+ jamais formaté → hook postToolCall hooks/gofmt.js (gofmt -w sur chaque
372+ fichier .go écrit ; une erreur de syntaxe revient au modèle).
373+ - Ornith 1.5 9B (raisonnement activé) : 2 sur 2, 783 s et 810 s, formaté.
374+ Mellum2 reste le coder par défaut (ton choix, ~6× plus rapide) ;
375+ agent.coder.ornith.yaml est fourni comme alternative plus régulière.
376+
377+ ## Équipe complète, 2e exécution « mots les plus fréquents »
378+ Échec encore : bon brief (top 3), puis la même coupure du coder, le 4B
379+ le redemande malgré la consigne, le reviewer reste à VERDICT: FAIL
380+ (2 cas sur 7) ; arrêté à 30 min.
381+
382+ ## Bilan
383+ - Fonctionne : corrections de bugs (398 s, PASS), leçons (12–49 s, en
384+ français quand on écrit en français), revues.
385+ - Pas encore fiable : les nouvelles fonctionnalités, à cause du coder
386+ Mellum2 et du coordinateur 4B qui relance un agent après un échec.
387+ - Mémoire : pendant les passages de main, jamais deux modèles chargés.
388+ Seul chevauchement vu : un Mellum2 laissé chargé par une de mes
389+ mesures avant le démarrage — les hooks ne déchargent pas un modèle
390+ chargé par autre chose (noté dans les limites).
391+ - Un membre a écrit un main.go dans dev.team/ (hors playground), ce qui
392+ cassait `go vet ./...` de mm : supprimé, et dev.team/go.mod isole
393+ maintenant le dossier.
394+ - Docs : dev.team/README.en.md et README.fr.md (routes, mémoire,
395+ exemples, mesures, limites), deux diagrammes (dev-team-flow,
396+ dev-team-memory) + ligne dans le README racine.
397+
398+ ## Pistes, non faites
399+ - Faire respecter « une seule correction » par un hook preAsk qui
400+ compte les appels au coder (au lieu du prompt du 4B).
401+ - Essayer l'équipe complète avec agent.coder.ornith.yaml sur la
402+ fonctionnalité.
403+ - Un hook preToolCall pour que le bash du reformulator ne puisse
404+ vraiment que lire.
405+ - id: 7
406+ author:
407+ name: k33g
408+ email: ph.charriere@gmail.com
409+ createdAt: 2026-10-25T11:30:00.000Z
410+ body: |
411+ Demande : « active le lsp go pour les agents ». Fait (non commité).
412+
413+ - Bloc `lsp` (gopls, .go) dans les 4 spécialistes et le coder Ornith ;
414+ pas dans le coordinateur (seulement ask_agent, par choix). Chaque
415+ prompt dit quoi en faire : coder references / rename / diagnostics ;
416+ reviewer references + diagnostics sur ce que touche le diff ;
417+ reformulator references / symbols ; tutor hover / definition. Seul
418+ le coder a le droit de faire `rename`.
419+ - gopls doit être dans le PATH (go install
420+ golang.org/x/tools/gopls@latest) ; sinon avertissement et pas d'outil.
421+ gopls trouve playground/go.mod tout seul depuis dev.team/.
422+
423+ Mesures (gopls v0.24.0) :
424+ - coder, « Rename LineCount to CountLines everywhere in playground » :
425+ 1re version, Mellum2 appelle rename sans new_name puis recopie
426+ l'indication : la fonction devient `new_name`, et il annonce
427+ « renommée en CountLines ». Avec un exemple rempli dans le prompt (et
428+ « vérifie l'ancien ET le nouveau nom avant de répondre ») : 3 sur 3
429+ justes, 5 à 10 s.
430+ - reviewer sur ce renommage : il lançait `git diff` depuis dev.team
431+ (dépôt de mm, où playground/ est ignoré → diff vide) et tournait en
432+ rond jusqu'à maxTurns. Prompt : diff DANS le projet, puis lsp
433+ references + diagnostics ; maxTurns 12 → 16 (12 coupait une revue
434+ normale de 13 appels). Ensuite : 1 exécution avec un appel d'outil
435+ qui se répétait (arrêté par mm), 1 avec references + diagnostics et
436+ un vrai constat (le message d'erreur du test dit encore LineCount :
437+ rename change les identifiants, pas les chaînes), VERDICT: FAIL.
438+ - READMEs EN/FR : section « serveur de langage Go », prérequis gopls,
439+ tableau des mesures.
440+ - id: 8
441+ author:
442+ name: k33g
443+ email: ph.charriere@gmail.com
444+ createdAt: 2026-10-25T12:30:00.000Z
445+ body: |
446+ Demande : protéger les fichiers d'agents dans la sandbox (sbxenv.yaml,
447+ start-zed.sh, stop-zed.sh), mini-me devant voir le dossier de la démo.
448+ Fait (non commité) :
449+
450+ - dev.team/ (jamais monté) : sbxenv.yaml, start-zed.sh, stop-zed.sh,
451+ READMEs, diagrammes — Docker recommande de garder le fichier
452+ d'environnement hors des dossiers montés.
453+ - dev.team/team/ : agent*.yaml + hooks/, monté en
454+ `additionalWorkspaces: [{path: ./team, readOnly: true}]`.
455+ - dev.team/workspace/ : `workspace: ./workspace` (lecture-écriture) —
456+ playground/, ton demo/, starter/, reset.sh, .mm/ (sessions).
457+ - mm trouve le coordinateur par `env: AGENT_CONFIG:
458+ ${{ env.args.DEV_TEAM_DIR }}/team/agent.yaml` ; start-zed.sh passe
459+ `--env-arg DEV_TEAM_DIR="$PWD"` et ouvre
460+ `ssh://zed-dev-team.sbx$PWD/workspace`. Les autres agents et les hooks
461+ sont trouvés à côté de team/agent.yaml.
462+
463+ Vérifié ici (sans sbx, team/ mis en lecture seule par chmod pour
464+ simuler le montage, mm lancé depuis workspace/ avec seulement
465+ AGENT_CONFIG) : route B 55 s ; route A (bug WordCount) VERDICT: PASS en
466+ 359 s, team/ inchangé. Droits rétablis ensuite.
467+
468+ Non vérifié (pas de sbx dans cet environnement) : que la sandbox monte
469+ team/ au même chemin absolu que sur la machine (AGENT_CONFIG en dépend ;
470+ ton `ssh://…sbx$PWD` actuel le laisse penser), et qu'une session Zed
471+ SSH reçoit bien les variables de `env:`. Les commandes de vérification
472+ sont dans les READMEs (section « Lancer »).
473+
474+ Remarque : dev.team/go.mod n'était plus là (supprimé de ton côté ?) —
475+ je ne l'ai pas remis.
476+
@@ -6,7 +6,7 @@ author:
6 name: k33g6 name: k33g
7 email: ph.charriere@gmail.com7 email: ph.charriere@gmail.com
8 createdAt: 2026-10-25T04:44:30.351Z8 createdAt: 2026-10-25T04:44:30.351Z
9-updatedAt: 2026-10-25T05:15:11.308Z9+updatedAt: 2026-10-25T12:30:00.000Z
10 labels: []10 labels: []
11 epic: Demos11 epic: Demos
12 body: |+12 body: |+
@@ -53,7 +53,52 @@ body: |+
53 53
54 54
55 55
56-tasks: []56+tasks:
57+ - id: 1
58+ title: "Créer ./dev.team : 5 agents (coordinator, reformulator, tutor, coder, reviewer), hook de déchargement, projet d'essai"
59+ state: closed
60+ priority: none
61+ author:
62+ name: k33g
63+ email: ph.charriere@gmail.com
64+ createdAt: 2026-10-25T07:30:00.000Z
65+ updatedAt: 2026-10-25T07:30:00.000Z
66+ - id: 2
67+ title: "Mesurer chaque agent seul (mm -p) sur des exemples réels, ajuster prompts et réglages"
68+ state: closed
69+ priority: none
70+ author:
71+ name: k33g
72+ email: ph.charriere@gmail.com
73+ createdAt: 2026-10-25T07:30:00.000Z
74+ updatedAt: 2026-10-25T07:30:00.000Z
75+ - id: 3
76+ title: "Mesurer l'équipe de bout en bout via le coordinateur, vérifier le déchargement des modèles (/api/ps)"
77+ state: closed
78+ priority: none
79+ author:
80+ name: k33g
81+ email: ph.charriere@gmail.com
82+ createdAt: 2026-10-25T07:30:00.000Z
83+ updatedAt: 2026-10-25T07:30:00.000Z
84+ - id: 4
85+ title: "Répondre à la question du modèle de décision (mesure du routage du coordinateur)"
86+ state: closed
87+ priority: none
88+ author:
89+ name: k33g
90+ email: ph.charriere@gmail.com
91+ createdAt: 2026-10-25T07:30:00.000Z
92+ updatedAt: 2026-10-25T07:30:00.000Z
93+ - id: 5
94+ title: "README EN + FR avec diagrammes fancy et exemples d'utilisation ; ligne dans le README racine"
95+ state: closed
96+ priority: none
97+ author:
98+ name: k33g
99+ email: ph.charriere@gmail.com
100+ createdAt: 2026-10-25T07:30:00.000Z
101+ updatedAt: 2026-10-25T07:30:00.000Z
57 comments:102 comments:
58 - id: 1103 - id: 1
59 author:104 author:
@@ -172,3 +217,260 @@ comments:
172 name.217 name.
173 218
174 ```219 ```
220+ - id: 2
221+ author:
222+ name: k33g
223+ email: ph.charriere@gmail.com
224+ createdAt: 2026-10-25T07:30:00.000Z
225+ body: |
226+ Plan (2026-10-25), avant de coder.
227+
228+ ## Ce qui existe déjà et que je réutilise
229+ - demos/team : un coordinateur + 3 spécialistes via `ask_agent` (mmConfigs).
230+ - demos/unload-models/hooks/unload.js : hooks `preAsk` (décharge le modèle
231+ de l'agent qui demande, juste avant de lancer l'agent demandé) et
232+ `postAsk` (décharge le modèle de l'agent demandé une fois sa réponse
233+ reçue). C'est une garantie, pas une consigne au modèle : la doc
234+ unload-a-model mesure qu'un petit modèle oublie d'appeler unload_model
235+ (4 fois sur 5 avec MiniCPM5 2B). Deux modèles ne sont donc jamais
236+ chargés ensemble.
237+ - Les trois modèles demandés sont déjà dans llmman : Qwen3.5-4B Q4_K_M
238+ (3,4 Go), gemma-4-12b-it Q4_K_M (7,3 Go), Mellum2-12B-A2.5B-Instruct
239+ Q4_K_M (8,1 Go). Avec le déchargement, le pic mémoire est un seul
240+ modèle de 12B (~8 Go + cache KV), à l'aise sur 32 Go.
241+
242+ ## Agents (noms et fichiers en anglais, titres avec emoji)
243+ - coordinator (Qwen3.5-4B, sans raisonnement) : seulement `ask_agent`.
244+ - reformulator (gemma-4-12b) : `ask_user` activé (et `askUser: true` dans
245+ mmConfigs pour que ses questions remontent à l'utilisateur), aucun outil
246+ qui modifie.
247+ - tutor (Mellum2 Instruct Q4_K_M) : la config « prof » du commentaire 1,
248+ adaptée (modèle Q4_K_M au lieu de Q8_0 comme le ticket le demande).
249+ - coder (Mellum2 Instruct Q4_K_M) : bash, fichiers, plan.
250+ - reviewer (gemma-4-12b) : bash pour lancer les tests, écrit des tests,
251+ ne modifie pas le code de l'application.
252+
253+ Tutor et coder partagent le même modèle, reformulator et reviewer aussi :
254+ passer de l'un à l'autre ne recharge rien.
255+
256+ ## Question du modèle de décision
257+ À trancher par la mesure : si le coordinateur 4B route bien (prof / code /
258+ reformulation), un décideur n'apporte rien ; s'il se trompe, un décideur
259+ de choix dans un hook preGeneration (comme demos/decision-chat) peut lui
260+ donner la route.
261+ - id: 3
262+ author:
263+ name: k33g
264+ email: ph.charriere@gmail.com
265+ createdAt: 2026-10-25T07:45:00.000Z
266+ body: |
267+ Tâche 1 faite (non commitée) : ./dev.team
268+ - agent.yaml (coordinator, 🧭 Coordinator), agent.reformulator.yaml
269+ (📝), agent.coder.yaml (🛠️), agent.reviewer.yaml (🔎),
270+ agent.tutor.yaml (📘, la config « prof » adaptée : Q4_K_M, sans lsp
271+ Rust ni docSearch — pas de code Rust ni de docs/ ici).
272+ - hooks/unload.js (copie de demos/unload-models) branché sur preAsk
273+ (self) et postAsk (asked) du coordinateur ; unloadModels: false pour
274+ que le modèle n'ait pas à y penser.
275+ - starter/textstats : un petit paquet Go avec tests, un bug connu
276+ (WordCount et les espaces multiples) et de la place pour une
277+ fonctionnalité ; `sh reset.sh` le copie dans playground/ et en fait
278+ un dépôt git, pour que le reviewer lise `git diff`.
279+ - Deux diagrammes (dev-team-flow, dev-team-memory) générés par
280+ .claude/skills/drawio-fancy-diagram/generator/examples/dev_team.py.
281+
282+ Tâche 2 en cours. Première mesure du coder seul (bug WordCount) :
283+ Mellum2 Q4_K_M a produit un appel d'outil coupé à max_tokens
284+ (« tool call arguments did not parse … unexpected end of JSON
285+ input ») après un edit_file raté — à suivre.
286+ - id: 4
287+ author:
288+ name: k33g
289+ email: ph.charriere@gmail.com
290+ createdAt: 2026-10-25T08:30:00.000Z
291+ body: |
292+ Tâche 2 faite — chaque agent seul, mesuré sur llmman :
293+ - coder (Mellum2) : bug WordCount corrigé (strings.Fields) + 4 cas de
294+ test, tests verts, 351 s. Un appel d'outil jamais refermé a couru
295+ jusqu'à max_tokens (8192) : max_tokens ramené à 4096 ; gofmt ajouté au
296+ prompt (le test ajouté n'était pas formaté).
297+ - reviewer (Gemma 4 12B) : git diff, tests, review_test.go à lui,
298+ « VERDICT: PASS », 151 s.
299+ - tutor (Mellum2) : 24 s, mais il a inventé « un fichier typique » au
300+ lieu de lire playground/textstats_test.go. Règle ajoutée (read_file
301+ d'abord, citer le vrai code) : 3 sur 3 lisent le fichier et citent la
302+ vraie table map[string]int, 12 à 19 s.
303+ - reformulator (Gemma 4 12B) : appelle bien ask_user ; avec `-p`
304+ personne ne peut répondre, et la 1re version décidait seule (et
305+ mettait « top N » hors périmètre pour « les mots les plus
306+ fréquents »). Prompt resserré + section ASSUMPTIONS : il liste
307+ maintenant ses choix (top 5, casse ignorée…), 62 s.
308+ - unload_model retiré des 4 membres (unloadModels: false) : ce sont les
309+ hooks du coordinateur qui déchargent.
310+
311+ Tâche 4 faite — faut-il un modèle de décision ?
312+ Mesure du routage du coordinateur Qwen3.5-4B sur 12 demandes (5 code,
313+ 5 leçons dont 2 en français, 2 revues) : 12/12 vers le bon agent. Un
314+ décideur pour router n'apporterait donc rien ici, et coûterait un modèle
315+ et un appel de plus. Où il serait utile, à mesurer si besoin : détecter
316+ une demande vague avant d'appeler le reformulateur (noul), ou trier le
317+ verdict du reviewer — pas nécessaire tant que le prompt suffit.
318+ Effet de bord vu pendant la mesure : quand ask_agent est refusé, le 4B
319+ essaie un autre agent au lieu de s'arrêter (sans conséquence avec
320+ yolo: true).
321+ - id: 5
322+ author:
323+ name: k33g
324+ email: ph.charriere@gmail.com
325+ createdAt: 2026-10-25T10:00:00.000Z
326+ body: |
327+ Tâche 3 en cours — l'équipe de bout en bout (coordinateur, `-p`,
328+ AGENT_YOLO=true), /api/ps échantillonné toutes les 2 s :
329+
330+ - Déchargement : sur les 4 exécutions, jamais deux modèles chargés en
331+ même temps (0 échantillon avec deux modèles). Séquence typique :
332+ 4B → Gemma → 4B → Mellum2 → 4B → Gemma → 4B.
333+ - Route A, bug WordCount, 1re exécution : 685 s, PASS, mais le coder a
334+ bouclé sur `plan update` (appels en échec) et le coordinateur l'a
335+ rappelé ; son rapport final disait à tort « aucune modification ».
336+ → planTool: false pour le coder (le brief tient lieu de plan), le
337+ reviewer vérifie gofmt et les cas de test supprimés, le rapport
338+ reprend les fichiers des réponses du coder et du reviewer.
339+ - Route A, bug WordCount, 2e exécution : 398 s, un appel par agent,
340+ gofmt propre, VERDICT: PASS, rapport exact. Reste : le coder a
341+ réécrit des cas de test existants au lieu d'en ajouter seulement.
342+ - Route B (leçon en français) : 49 s, tutor seul, réponse juste en
343+ français.
344+ - Route A, nouvelle fonction « mots les plus fréquents » : ÉCHEC après
345+ 52 min. Le brief était bon (top 5, ponctuation, égalités par ordre
346+ alphabétique, ASSUMPTIONS) ; puis le reviewer a testé un comportement
347+ absent du brief (garder les apostrophes) et atteint ses 20 tours, le
348+ 4B a rappelé le reviewer puis le coder 4 fois (malgré la consigne),
349+ et les edit_file de Mellum2 échouaient en série. Tests rouges à la
350+ fin.
351+ → reviewer : tests limités à ACCEPTANCE/REQUIREMENTS, maxTurns 12 ;
352+ coordinateur : maxTurns 6, « ne jamais redemander à un agent ».
353+
354+ Je remets en question un choix de modèle, comme le ticket le permet :
355+ je compare le coder Mellum2 à Ornith 1.5 9B Q4_K_M (6,7 Go, 5 sur 5 en
356+ bout en bout dans demos/team) sur le même brief, chacun seul, avec un
357+ test caché tiré de l'ACCEPTANCE du brief.
358+ - id: 6
359+ author:
360+ name: k33g
361+ email: ph.charriere@gmail.com
362+ createdAt: 2026-10-25T10:30:00.000Z
363+ body: |
364+ Tâches 3 et 5 faites — l'état final (non commité, à relire).
365+
366+ ## Comparaison des coders (le ticket permet de discuter les modèles)
367+ Chacun seul sur le même brief (« mots les plus fréquents »), vérifié par
368+ un test caché tiré de l'ACCEPTANCE du brief, go vet et gofmt :
369+ - Mellum2 12B-A2.5B : 2 sur 3, 129 s, 132 s, 639 s (l'échec : appels
370+ d'outil coupés à max_tokens, test qui compare des slices avec !=) ;
371+ jamais formaté → hook postToolCall hooks/gofmt.js (gofmt -w sur chaque
372+ fichier .go écrit ; une erreur de syntaxe revient au modèle).
373+ - Ornith 1.5 9B (raisonnement activé) : 2 sur 2, 783 s et 810 s, formaté.
374+ Mellum2 reste le coder par défaut (ton choix, ~6× plus rapide) ;
375+ agent.coder.ornith.yaml est fourni comme alternative plus régulière.
376+
377+ ## Équipe complète, 2e exécution « mots les plus fréquents »
378+ Échec encore : bon brief (top 3), puis la même coupure du coder, le 4B
379+ le redemande malgré la consigne, le reviewer reste à VERDICT: FAIL
380+ (2 cas sur 7) ; arrêté à 30 min.
381+
382+ ## Bilan
383+ - Fonctionne : corrections de bugs (398 s, PASS), leçons (12–49 s, en
384+ français quand on écrit en français), revues.
385+ - Pas encore fiable : les nouvelles fonctionnalités, à cause du coder
386+ Mellum2 et du coordinateur 4B qui relance un agent après un échec.
387+ - Mémoire : pendant les passages de main, jamais deux modèles chargés.
388+ Seul chevauchement vu : un Mellum2 laissé chargé par une de mes
389+ mesures avant le démarrage — les hooks ne déchargent pas un modèle
390+ chargé par autre chose (noté dans les limites).
391+ - Un membre a écrit un main.go dans dev.team/ (hors playground), ce qui
392+ cassait `go vet ./...` de mm : supprimé, et dev.team/go.mod isole
393+ maintenant le dossier.
394+ - Docs : dev.team/README.en.md et README.fr.md (routes, mémoire,
395+ exemples, mesures, limites), deux diagrammes (dev-team-flow,
396+ dev-team-memory) + ligne dans le README racine.
397+
398+ ## Pistes, non faites
399+ - Faire respecter « une seule correction » par un hook preAsk qui
400+ compte les appels au coder (au lieu du prompt du 4B).
401+ - Essayer l'équipe complète avec agent.coder.ornith.yaml sur la
402+ fonctionnalité.
403+ - Un hook preToolCall pour que le bash du reformulator ne puisse
404+ vraiment que lire.
405+ - id: 7
406+ author:
407+ name: k33g
408+ email: ph.charriere@gmail.com
409+ createdAt: 2026-10-25T11:30:00.000Z
410+ body: |
411+ Demande : « active le lsp go pour les agents ». Fait (non commité).
412+
413+ - Bloc `lsp` (gopls, .go) dans les 4 spécialistes et le coder Ornith ;
414+ pas dans le coordinateur (seulement ask_agent, par choix). Chaque
415+ prompt dit quoi en faire : coder references / rename / diagnostics ;
416+ reviewer references + diagnostics sur ce que touche le diff ;
417+ reformulator references / symbols ; tutor hover / definition. Seul
418+ le coder a le droit de faire `rename`.
419+ - gopls doit être dans le PATH (go install
420+ golang.org/x/tools/gopls@latest) ; sinon avertissement et pas d'outil.
421+ gopls trouve playground/go.mod tout seul depuis dev.team/.
422+
423+ Mesures (gopls v0.24.0) :
424+ - coder, « Rename LineCount to CountLines everywhere in playground » :
425+ 1re version, Mellum2 appelle rename sans new_name puis recopie
426+ l'indication : la fonction devient `new_name`, et il annonce
427+ « renommée en CountLines ». Avec un exemple rempli dans le prompt (et
428+ « vérifie l'ancien ET le nouveau nom avant de répondre ») : 3 sur 3
429+ justes, 5 à 10 s.
430+ - reviewer sur ce renommage : il lançait `git diff` depuis dev.team
431+ (dépôt de mm, où playground/ est ignoré → diff vide) et tournait en
432+ rond jusqu'à maxTurns. Prompt : diff DANS le projet, puis lsp
433+ references + diagnostics ; maxTurns 12 → 16 (12 coupait une revue
434+ normale de 13 appels). Ensuite : 1 exécution avec un appel d'outil
435+ qui se répétait (arrêté par mm), 1 avec references + diagnostics et
436+ un vrai constat (le message d'erreur du test dit encore LineCount :
437+ rename change les identifiants, pas les chaînes), VERDICT: FAIL.
438+ - READMEs EN/FR : section « serveur de langage Go », prérequis gopls,
439+ tableau des mesures.
440+ - id: 8
441+ author:
442+ name: k33g
443+ email: ph.charriere@gmail.com
444+ createdAt: 2026-10-25T12:30:00.000Z
445+ body: |
446+ Demande : protéger les fichiers d'agents dans la sandbox (sbxenv.yaml,
447+ start-zed.sh, stop-zed.sh), mini-me devant voir le dossier de la démo.
448+ Fait (non commité) :
449+
450+ - dev.team/ (jamais monté) : sbxenv.yaml, start-zed.sh, stop-zed.sh,
451+ READMEs, diagrammes — Docker recommande de garder le fichier
452+ d'environnement hors des dossiers montés.
453+ - dev.team/team/ : agent*.yaml + hooks/, monté en
454+ `additionalWorkspaces: [{path: ./team, readOnly: true}]`.
455+ - dev.team/workspace/ : `workspace: ./workspace` (lecture-écriture) —
456+ playground/, ton demo/, starter/, reset.sh, .mm/ (sessions).
457+ - mm trouve le coordinateur par `env: AGENT_CONFIG:
458+ ${{ env.args.DEV_TEAM_DIR }}/team/agent.yaml` ; start-zed.sh passe
459+ `--env-arg DEV_TEAM_DIR="$PWD"` et ouvre
460+ `ssh://zed-dev-team.sbx$PWD/workspace`. Les autres agents et les hooks
461+ sont trouvés à côté de team/agent.yaml.
462+
463+ Vérifié ici (sans sbx, team/ mis en lecture seule par chmod pour
464+ simuler le montage, mm lancé depuis workspace/ avec seulement
465+ AGENT_CONFIG) : route B 55 s ; route A (bug WordCount) VERDICT: PASS en
466+ 359 s, team/ inchangé. Droits rétablis ensuite.
467+
468+ Non vérifié (pas de sbx dans cet environnement) : que la sandbox monte
469+ team/ au même chemin absolu que sur la machine (AGENT_CONFIG en dépend ;
470+ ton `ssh://…sbx$PWD` actuel le laisse penser), et qu'une session Zed
471+ SSH reçoit bien les variables de `env:`. Les commandes de vérification
472+ sont dans les READMEs (section « Lancer »).
473+
474+ Remarque : dev.team/go.mod n'était plus là (supprimé de ton côté ?) —
475+ je ne l'ai pas remis.
476+
added .tickets/issues/0153-add-colored-warning-about-the-status-of-the-agen.yaml +16 -0
new file mode 100644
@@ -0,0 +1,16 @@
1+# Managed by IssueSpec. Hand edits are welcome; keep the schema valid.
2+id: 153
3+title: Add colored warning about the status of the agent (hallucinating, endless loop, ...)
4+state: open
5+author:
6+ name: k33g
7+ email: ph.charriere@gmail.com
8+createdAt: 2026-10-25T08:40:29.067Z
9+updatedAt: 2026-10-25T08:41:17.587Z
10+labels:
11+ - priority::urgent
12+ - kind::feature
13+milestone: v1.0.1
14+body: ""
15+tasks: []
16+comments: []
new file mode 100644
@@ -0,0 +1,16 @@
1+# Managed by IssueSpec. Hand edits are welcome; keep the schema valid.
2+id: 153
3+title: Add colored warning about the status of the agent (hallucinating, endless loop, ...)
4+state: open
5+author:
6+ name: k33g
7+ email: ph.charriere@gmail.com
8+createdAt: 2026-10-25T08:40:29.067Z
9+updatedAt: 2026-10-25T08:41:17.587Z
10+labels:
11+ - priority::urgent
12+ - kind::feature
13+milestone: v1.0.1
14+body: ""
15+tasks: []
16+comments: []
added .tickets/issues/0154-improve-code-edition.yaml +20 -0
new file mode 100644
@@ -0,0 +1,20 @@
1+# Managed by IssueSpec. Hand edits are welcome; keep the schema valid.
2+id: 154
3+title: Improve code edition
4+state: open
5+author:
6+ name: k33g
7+ email: ph.charriere@gmail.com
8+createdAt: 2026-10-25T09:48:22.741Z
9+updatedAt: 2026-10-25T09:48:36.954Z
10+labels: []
11+epic: Debug
12+body: |
13+ How to fix:
14+
15+ ```
16+ ‣ coder/edit_file coder · edit_file: demo/main.go (1 edit(s)) ✗ failed 0.00s ░
17+ /Users/k33g/kDrive/Rickub/bots-garden/mini-me/dev.team/workspace/demo/main.go: edit #1 changes nothing: old and new are identical
18+ ```
19+tasks: []
20+comments: []
new file mode 100644
@@ -0,0 +1,20 @@
1+# Managed by IssueSpec. Hand edits are welcome; keep the schema valid.
2+id: 154
3+title: Improve code edition
4+state: open
5+author:
6+ name: k33g
7+ email: ph.charriere@gmail.com
8+createdAt: 2026-10-25T09:48:22.741Z
9+updatedAt: 2026-10-25T09:48:36.954Z
10+labels: []
11+epic: Debug
12+body: |
13+ How to fix:
14+
15+ ```
16+ ‣ coder/edit_file coder · edit_file: demo/main.go (1 edit(s)) ✗ failed 0.00s ░
17+ /Users/k33g/kDrive/Rickub/bots-garden/mini-me/dev.team/workspace/demo/main.go: edit #1 changes nothing: old and new are identical
18+ ```
19+tasks: []
20+comments: []
added .tickets/issues/0155-when-adding-a-message-don-t-cancel-the-running-g.yaml +16 -0
new file mode 100644
@@ -0,0 +1,16 @@
1+# Managed by IssueSpec. Hand edits are welcome; keep the schema valid.
2+id: 155
3+title: When adding a message don't cancel the running generation, use something like /btw
4+state: open
5+author:
6+ name: k33g
7+ email: ph.charriere@gmail.com
8+createdAt: 2026-10-25T10:00:14.109Z
9+updatedAt: 2026-10-25T10:00:27.072Z
10+labels:
11+ - priority::urgent
12+ - enhancement
13+milestone: v1.0.1
14+body: ""
15+tasks: []
16+comments: []
new file mode 100644
@@ -0,0 +1,16 @@
1+# Managed by IssueSpec. Hand edits are welcome; keep the schema valid.
2+id: 155
3+title: When adding a message don't cancel the running generation, use something like /btw
4+state: open
5+author:
6+ name: k33g
7+ email: ph.charriere@gmail.com
8+createdAt: 2026-10-25T10:00:14.109Z
9+updatedAt: 2026-10-25T10:00:27.072Z
10+labels:
11+ - priority::urgent
12+ - enhancement
13+milestone: v1.0.1
14+body: ""
15+tasks: []
16+comments: []
modified README.md +1 -0
@@ -141,6 +141,7 @@ The `demos/` directory holds ready-made set-ups (most with a README explaining h
141141 | `demos/mini-mes/` | small teams of agents (`riker` coordinating a specifier and a coder, `gopher` a Go specialist) |
142142 | `demos/little-coder/` | a Go + htmx web-app generator for a small local model (Mellum2 12B on llmman, 32 GB RAM): a tested starter, skills, `AGENTS.md`, measured results |
143143 | `demos/team/` | a team of four agents (lead, specifier, coder, tester) on one small model (Ornith 1.5 9B on llmman) building Go + htmx apps through spec and test files, with measured results |
144+| `dev.team/` | a team of five agents for a 32 GB laptop (ticket 152): a Qwen3.5 4B coordinator hands each request to a reformulator, a coder, a reviewer or a tutor (Gemma 4 12B, Mellum2 12B); hooks keep one model in memory at a time; measured: bug fixes, lessons and reviews work, new features not yet |
144145 | `demos/blog-team/` | the smallest team: a lead, a coder and a reviewer in 75 lines of YAML, one model (Mellum2 12B Q8_0 on llmman), written for a blog post |
145146 | `demos/translator/` | a French ↔ English translator (Gemma 4 E4B on llmman): `AGENTS.md` for the style, skills for technical texts, e-mails and idioms, `$styles` for professional / informal / Slack versions, `translate.sh` for files |
146147 | `demos/qwen-style/` | a general coding agent whose system prompt follows Qwen Code's, adapted to mm's tools, in three configurations on llmman: Qwen3.8 27B, Qwen3-Coder-Next, Qwen3.6 35B-A3B — Qwen Code's `new-app` and `simplify` skills, measured results |
@@ -141,6 +141,7 @@ The `demos/` directory holds ready-made set-ups (most with a README explaining h
141 | `demos/mini-mes/` | small teams of agents (`riker` coordinating a specifier and a coder, `gopher` a Go specialist) |141 | `demos/mini-mes/` | small teams of agents (`riker` coordinating a specifier and a coder, `gopher` a Go specialist) |
142 | `demos/little-coder/` | a Go + htmx web-app generator for a small local model (Mellum2 12B on llmman, 32 GB RAM): a tested starter, skills, `AGENTS.md`, measured results |142 | `demos/little-coder/` | a Go + htmx web-app generator for a small local model (Mellum2 12B on llmman, 32 GB RAM): a tested starter, skills, `AGENTS.md`, measured results |
143 | `demos/team/` | a team of four agents (lead, specifier, coder, tester) on one small model (Ornith 1.5 9B on llmman) building Go + htmx apps through spec and test files, with measured results |143 | `demos/team/` | a team of four agents (lead, specifier, coder, tester) on one small model (Ornith 1.5 9B on llmman) building Go + htmx apps through spec and test files, with measured results |
144+| `dev.team/` | a team of five agents for a 32 GB laptop (ticket 152): a Qwen3.5 4B coordinator hands each request to a reformulator, a coder, a reviewer or a tutor (Gemma 4 12B, Mellum2 12B); hooks keep one model in memory at a time; measured: bug fixes, lessons and reviews work, new features not yet |
144 | `demos/blog-team/` | the smallest team: a lead, a coder and a reviewer in 75 lines of YAML, one model (Mellum2 12B Q8_0 on llmman), written for a blog post |145 | `demos/blog-team/` | the smallest team: a lead, a coder and a reviewer in 75 lines of YAML, one model (Mellum2 12B Q8_0 on llmman), written for a blog post |
145 | `demos/translator/` | a French ↔ English translator (Gemma 4 E4B on llmman): `AGENTS.md` for the style, skills for technical texts, e-mails and idioms, `$styles` for professional / informal / Slack versions, `translate.sh` for files |146 | `demos/translator/` | a French ↔ English translator (Gemma 4 E4B on llmman): `AGENTS.md` for the style, skills for technical texts, e-mails and idioms, `$styles` for professional / informal / Slack versions, `translate.sh` for files |
146 | `demos/qwen-style/` | a general coding agent whose system prompt follows Qwen Code's, adapted to mm's tools, in three configurations on llmman: Qwen3.8 27B, Qwen3-Coder-Next, Qwen3.6 35B-A3B — Qwen Code's `new-app` and `simplify` skills, measured results |147 | `demos/qwen-style/` | a general coding agent whose system prompt follows Qwen Code's, adapted to mm's tools, in three configurations on llmman: Qwen3.8 27B, Qwen3-Coder-Next, Qwen3.6 35B-A3B — Qwen Code's `new-app` and `simplify` skills, measured results |
added dev.team/README.en.md +238 -0
new file mode 100644
@@ -0,0 +1,238 @@
1+# dev.team — a team of agents that helps you code
2+
3+*[Version française](README.fr.md)*
4+
5+Five `mm` agents for a 32 GB MacBook Air: a coordinator you talk to, and four specialists it calls with the built-in `ask_agent` tool, one at a time. A hook unloads each model when the team hands over, so **only one model is in memory at a time**.
6+
7+![The coordinator's three routes](dev-team-flow.svg)
8+
9+| Agent | File | Header title | Model (llmman) | Tools | Job |
10+|---|---|---|---|---|---|
11+| coordinator | `agent.yaml` | 🧭 Coordinator | Qwen3.5 4B · 3.4 GB | `ask_agent` only | picks the route, hands over, reports to you |
12+| reformulator | `agent.reformulator.yaml` | 📝 Reformulator | Gemma 4 12B · 7.3 GB | `bash` (reading), `ask_user`, `lsp` (reading) | turns a code request into a task brief; asks you what only you can decide |
13+| coder | `agent.coder.yaml` | 🛠️ Coder | Mellum2 12B-A2.5B · 8.1 GB | `bash`, files, `lsp`; a `gofmt` hook | fixes, adds, changes code; runs the tests |
14+| reviewer | `agent.reviewer.yaml` | 🔎 Reviewer | Gemma 4 12B · 7.3 GB | `bash`, files (tests only), `lsp` (reading) | reads the diff, runs the tests, writes its own edge-case tests, `VERDICT: PASS / FAIL` |
15+| tutor | `agent.tutor.yaml` | 📘 Tutor | Mellum2 12B-A2.5B · 8.1 GB | `fetch_content`, `save_answer`, `bash`, files, `lsp` (reading) | answers questions with a mini tutorial; saves it on request |
16+
17+The full model ids are `hf.co/unsloth/Qwen3.5-4B-GGUF:Q4_K_M`, `hf.co/unsloth/gemma-4-12b-it-GGUF:Q4_K_M` and `hf.co/JetBrains/Mellum2-12B-A2.5B-Instruct-GGUF-Q4_K_M:Q4_K_M`.
18+
19+## The three routes
20+
21+The coordinator reads the request and picks one route (its prompt, in `agent.yaml`):
22+
23+- **A — code to write or change** (a bug, a feature, a rename, tests): reformulator → coder → reviewer. If the reviewer answers `VERDICT: FAIL`, the coder fixes **once** and the reviewer checks again. Then the coordinator reports: who did what, the files, the tests, the verdict.
24+- **B — a question, a lesson, advice** ("how do I…", "what is…", "explique-moi…"): the tutor, and its answer is given as it is.
25+- **C — only a review** of existing code: the reviewer.
26+
27+The team works on the code in **`workspace/`**, which is empty in the repository: put your project there (a git repository, so that the reviewer can read `git diff`), or ask the team to create one. The measurements below were made on `textstats`, a small Go practice package with a known bug (`WordCount` and several spaces), which this folder no longer ships.
28+
29+## One model in memory at a time
30+
31+![Hooks unload a model at each hand-off](dev-team-memory.svg)
32+
33+The ticket's constraint: when the team switches agent, the current model leaves memory. That is not left to the models (a small model forgets to call `unload_model`: 4 times out of 5 with MiniCPM5 2B in `demos/unload-models`). Two hooks of the coordinator do it, with `hooks/unload.js` (copied from `demos/unload-models`):
34+
35+| Hook | When | Unloads |
36+|---|---|---|
37+| `preAsk` → `unload.js self` | the coordinator has decided to call an agent, before that agent starts | the coordinator's own 4B |
38+| `postAsk` → `unload.js asked` | the agent has answered, before the coordinator reads the answer | the agent's 12B |
39+
40+llmman reloads a model by itself on the next request, so the coordinator's 4B comes back alone to read each answer. Both hooks have `onError: warn`: a failed unload costs memory, not the hand-off. The four specialists have `unloadModels: false`: no `unload_model` tool to think about.
41+
42+Two models are shared on purpose: the reformulator and the reviewer use Gemma 4 12B, the coder and the tutor Mellum2. **Peak memory: one 12B model**, about 8 GB plus its KV cache at a 32k window.
43+
44+### The coder's formatter hook
45+
46+`agent.coder.yaml` has its own `postToolCall` hook: after each `write_file` or `edit_file` on a `.go` file, `hooks/gofmt.js` runs `gofmt -w` on it. The prompt asked for `gofmt` and Mellum2 skipped it in every measured run. When gofmt cannot parse the file, its error goes back to the model as context.
47+
48+### The Go language server
49+
50+The four specialists have an `lsp` block with gopls for `.go` files; the coordinator has no tool but `ask_agent`, by design. Each prompt says what to use it for:
51+
52+| Agent | Uses `lsp` for |
53+|---|---|
54+| coder | `references` before changing a signature, `rename` to rename an identifier everywhere, `diagnostics` after a change |
55+| reviewer | `references` on every function the diff touches, `diagnostics` on each changed file |
56+| reformulator | `references` and `symbols`, to name the files a change touches |
57+| tutor | `hover` and `definition` on a symbol of the code a question is about |
58+
59+Only the coder may `rename`; the others' prompts forbid it. gopls finds the `go.mod` of a project in a sub-folder of `workspace/` by itself, although the agents run from `workspace/` (measured with `textstats`).
60+
61+## Prerequisites
62+
63+- **llmman** serving on its default address, with the three models:
64+
65+ ```bash
66+ llmman pull hf.co/unsloth/Qwen3.5-4B-GGUF:Q4_K_M
67+ llmman pull hf.co/unsloth/gemma-4-12b-it-GGUF:Q4_K_M
68+ llmman pull hf.co/JetBrains/Mellum2-12B-A2.5B-Instruct-GGUF-Q4_K_M:Q4_K_M
69+ llmman serve
70+ ```
71+
72+- **Node.js** (the hooks), **Go** ≥ 1.24 and **git** (the reviewer reads `git diff`).
73+- **gopls** on the `PATH`: `go install golang.org/x/tools/gopls@latest` (then `$(go env GOPATH)/bin` in the `PATH`). Without it, each specialist starts with a warning and no `lsp` tool.
74+- **mm** built at the repository root: `go build -o mm .`
75+
76+## Layout: agents read-only, code read-write
77+
78+![team/ read-only, workspace/ read-write](dev-team-sandbox.svg)
79+
80+```
81+dev.team/
82+├── sbxenv.yaml, start-zed.sh, stop-zed.sh the sandbox — never mounted
83+├── README.*, dev-team-*.{drawio,svg,png} this documentation
84+├── team/ the agents and their hooks → mounted READ-ONLY
85+└── workspace/ your code (empty in the repository), .mm/ → the read-write workspace
86+```
87+
88+The agents' files are what decides what the team may do: their prompts, their tools, `yolo`, the hooks. A team member with `bash` and `write_file` could rewrite them, and the next start would run its version. So they live in `team/`, which the sandbox mounts with `readOnly: true`, apart from the code the team changes. Docker also recommends keeping the environment file outside every mounted directory, so that an agent cannot change the commands your host runs: `sbxenv.yaml` and the scripts stay at the root of `dev.team/`, which is not mounted.
89+
90+mm finds the coordinator through `AGENT_CONFIG`, set in `sbxenv.yaml`; the other agents and the hooks are found next to it (`mmConfigs` paths and `$MM_CONFIG_DIR` are relative to `team/agent.yaml`). Sessions and traces go to `workspace/.mm/`, the session's working directory.
91+
92+## Run
93+
94+### In a sandbox, with Zed
95+
96+```bash
97+cd dev.team
98+sbx setup ssh # once
99+./start-zed.sh # sbx env create --env-arg DEV_TEAM_DIR=$PWD, then Zed on workspace/
100+./stop-zed.sh # removes the sandbox; team/ and workspace/ stay on the host
101+```
102+
103+`start-zed.sh` passes the folder of `sbxenv.yaml` as the argument `DEV_TEAM_DIR`; `sbxenv.yaml` builds `AGENT_CONFIG=<DEV_TEAM_DIR>/team/agent.yaml` from it. In a terminal of the sandbox, from `workspace/`: `mm -tui` (it reads `AGENT_CONFIG`).
104+
105+To check, once the sandbox is up: `sbx version` (`sbx env` needs 0.39.0 or newer, which is also past CVE-2026-18171, a way to write into a read-only mount in 0.35.0–0.38.0); in the sandbox, `echo $AGENT_CONFIG`, `ls "$(dirname "$AGENT_CONFIG")"` and `touch "$(dirname "$AGENT_CONFIG")/x"`, which must fail.
106+
107+### On the host
108+
109+```bash
110+cd dev.team/workspace
111+AGENT_CONFIG=$PWD/../team/agent.yaml ../../mm -tui
112+../../mm -config ../team/agent.yaml -tui # the same
113+../../mm -config ../team/agent.yaml -p "…" # one request, then exit (the reformulator cannot ask you anything)
114+```
115+
116+The sandbox publishes ports 8080, 6060 and 5050 to the same ports on the host (`ports` in `sbxenv.yaml`, applied at creation): a server started in the sandbox must listen on `0.0.0.0` to be reached on `http://localhost:8080`.
117+
118+On the host nothing makes `team/` read-only: that protection is the sandbox's.
119+
120+In the window, the header names the agent at work: `mm [🧭 Coordinator] · qwen3.5-4b:Q4_K_M @ llmman`, then `mm [🛠️ Coder] · … · asked by mm [🧭 Coordinator]` during a hand-off.
121+
122+`yolo: true` in `agent.yaml` and in every `mmConfigs` entry: hand-offs, commands and file writes run without asking. To approve each hand-off and read the prompt the coordinator wrote: `AGENT_YOLO=false` before `mm`.
123+
124+Each specialist also works alone, e.g. `mm -config ../team/agent.tutor.yaml` from `workspace/` (alone, they ask before each command and file change, except the tutor).
125+
126+## Examples to try
127+
128+Put a project in `workspace/`, as a git repository with its last state committed, so that the reviewer's `git diff` shows only the team's change. The examples call it `my-app`.
129+
130+| Route | Request | What should happen |
131+|---|---|---|
132+| A | `Fix the bug in my-app: <what goes wrong, with an input and the expected output>.` | brief → fix + a test case → reviewer `VERDICT: PASS` |
133+| A | `Add a function to my-app that returns the most frequent words of a text.` | the reformulator asks you how many, case, punctuation, return type (in `-p`: it chooses and lists its ASSUMPTIONS) |
134+| A | `Rename <OldName> to <NewName> everywhere in my-app.` | brief → `lsp rename` + tests → review |
135+| B | `What is the difference between strings.Split and strings.Fields?` | a mini tutorial |
136+| B | `Explain table-driven tests in Go, using my-app/<file>_test.go as the example.` | the tutor reads the file and builds the lesson on its real code |
137+| B | `Explique-moi les goroutines avec un petit exemple, et sauve-le dans goroutines.md.` | a tutorial in French, saved with `save_answer` |
138+| C | `Review the code in my-app and tell me what is wrong.` | findings with file:line, tests, verdict |
139+
140+## Measured results
141+
142+llmman on the user's 32 GB machine, 2026-10-25, on the `textstats` practice package (`playground/` in the tables), before `workspace/` was emptied.
143+
144+### Each agent alone
145+
146+| Agent | Task | Result | Time |
147+|---|---|---|---|
148+| coder | the `WordCount` bug | fixed with `strings.Fields`, 4 test cases added, tests green | 351 s |
149+| reviewer | review of that fix | `git diff`, tests, its own `review_test.go`, `VERDICT: PASS` | 151 s |
150+| tutor | table-driven tests on `playground/textstats_test.go` | 3 runs out of 3 read the file and teach on its real `map[string]int` table | 12–19 s |
151+| reformulator | "a function that gives the most frequent words" | reads the code, calls `ask_user`; with nobody to answer (`-p`), lists its choices under ASSUMPTIONS | 62 s |
152+
153+What these runs changed in the files:
154+
155+- **coder**: once, after a failed `edit_file`, Mellum2 wrote a tool call that never closed and ran to `max_tokens` (8192): most of the 351 s. `max_tokens` is now 4096, which halves what such a call costs.
156+- **tutor**: the first version answered in 24 s but showed "a typical" test file it had invented. The prompt now makes `read_file` its first action when a file is named.
157+- **reformulator**: the first version did not list its choices, and put "top N" out of scope for "the most frequent words". The prompt now names what only the user decides, and the brief has an ASSUMPTIONS section.
158+
159+### Does the team need a decision model?
160+
161+The question of the ticket. A decider (see `demos/decision-*`) would pay off if the coordinator chose the wrong route. Measured: the coordinator (Qwen3.5 4B, reasoning off) on 12 requests — 5 code changes, 5 questions (2 in French), 2 reviews — **12 out of 12 to the right agent**. A decider would add a model and a call for nothing. Where one could help later, to be measured first: spotting a request too vague to code (a noul question before the reformulator), or reading the reviewer's verdict.
162+
163+### The whole team
164+
165+Through the coordinator, `-p`, `AGENT_YOLO=true`; llmman's loaded models sampled every 2 seconds.
166+
167+| Run | Route | Result | Time |
168+|---|---|---|---|
169+| bug fix, 1st | A | `VERDICT: PASS`, but the coder looped on `plan update` and was called twice; the report said "no changes" | 685 s |
170+| bug fix, 2nd (after the fixes below) | A | one call per agent, `VERDICT: PASS`, formatted, an exact report | 398 s |
171+| "explique-moi strings.Split et strings.Fields" | B | the tutor answers in French, correct | 49 s |
172+| "the most frequent words", 1st | A | **failed**: the reviewer tested a behaviour not in the brief and hit its 20 turns; the coordinator re-asked it, then called the coder 4 times; tests red at the end | 52 min |
173+| "the most frequent words", 2nd (after the fixes below) | A | **failed**: a good brief (top 3), then the coder hit the cut-off failure and the coordinator re-asked it; the reviewer kept `VERDICT: FAIL` (2 of its 7 cases); stopped by the 30-minute limit of the measurement | 30 min |
174+
175+**Memory: within the team's hand-offs, never two models loaded at the same time**: 4B → Gemma → 4B → Mellum2 → 4B → Gemma → 4B. The one overlap seen (the first 3 minutes of the last run) was a Mellum2 left loaded by a measurement run before the team started: the hooks unload the models of the hand-offs, not a model something else loaded.
176+
177+**In short: bug fixes, lessons and reviews work; new features do not yet.** The weak link is the coder: on a feature, Mellum2 writes tool calls that repeat themselves until `max_tokens`, and the 4B coordinator then re-asks it despite its prompt. Try `agent.coder.ornith.yaml` for features (see below).
178+
179+What the runs changed:
180+
181+- **coder**: no `plan` tool (Mellum2 called `plan update` in a loop); "add test cases, keep the existing ones"; the `gofmt` hook.
182+- **reviewer**: its tests cover the brief's ACCEPTANCE and REQUIREMENTS only, a failing case of its own is a finding (no rewriting until it passes), `gofmt -l` and removed test cases are findings, `maxTurns` 20 → 12 (then 16, see below).
183+- **coordinator**: `maxTurns` 8 → 6; "never re-ask an agent with a new prompt"; the report takes the files from the coder's and the reviewer's answers.
184+
185+### With the Go language server
186+
187+Measured after adding `lsp` (gopls v0.24.0):
188+
189+| Agent | Task | Result | Time |
190+|---|---|---|---|
191+| coder, 1st prompt | "Rename LineCount to CountLines everywhere in playground" | called `rename` without `new_name`, then copied the hint literally: the function became `new_name`, and the report said "renamed to CountLines" | 18 s |
192+| coder, with a filled-in example in the prompt | the same | 3 runs out of 3: `lsp rename` → `CountLines`, tests green | 5–10 s |
193+| reviewer | review of that rename | run 1: a tool call that repeated itself, stopped by mm; run 2: `references` + 2 `diagnostics`, a real finding (the test's error message still says `LineCount`: `rename` changes identifiers, not strings), `VERDICT: FAIL` | 200 s, 167 s |
194+
195+The reviewer's prompt also had to say to run `git diff` **inside** `playground/`: from the folder above it, git reads mm's repository, where `playground/` is ignored, and the diff is empty. Its `maxTurns` is now 16: 12 cut a normal review (13 calls) before its verdict.
196+
197+### Which coder?
198+
199+The ticket allows questioning its model choices, so the coder was measured alone on the reformulator's brief for "the most frequent words", against a hidden test made of the brief's acceptance cases (copied in after the run), `go vet` and `gofmt`:
200+
201+| Coder | Runs correct | Time per run | Formatted |
202+|---|---|---|---|
203+| Mellum2 12B-A2.5B Instruct (`agent.coder.yaml`) | 2 out of 3 | 129 s, 132 s, 639 s (the failure) | no (before the hook) |
204+| Ornith 1.5 9B, reasoning on (`agent.coder.ornith.yaml`) | 2 out of 2 | 783 s, 810 s | yes |
205+
206+Mellum2 stays the default, as the ticket chose: about 6 times faster, and right 2 times out of 3. Its failure mode is the one seen above: a tool call that repeats itself until `max_tokens`. Ornith is the steadier choice when time matters less: point `mmConfigs.coder.path` at `agent.coder.ornith.yaml` (6.7 GB, so it changes nothing to the memory peak).
207+
208+## Limits
209+
210+- **The reformulator's bash is read-only by its prompt only.** It has no file tools, but `bash` could write; with `yolo: true` nothing asks. For a guarantee, add a `preToolCall` hook that refuses commands that write (see `demos/hooks`).
211+- **The team can write anywhere in `workspace/`, not only in `playground/`.** Once, before the split, a member wrote a `main.go` next to `agent.yaml`, which broke mm's `go vet ./...`. In the sandbox it can no longer touch `team/`; check `git status` after a run.
212+- **`-p` cannot ask you anything**: the reformulator then chooses and says so.
213+- **Each reload costs time**: unloading at every hand-off trades seconds of loading for memory. On a machine with more memory, remove the `preAsk` hook to keep the 4B loaded.
214+- When `ask_agent` is refused (`yolo: false` and you say no), the 4B coordinator tends to try another agent rather than stop; after a failed hand-off, it re-asks the agent although its prompt says not to (`maxTurns: 6` bounds it).
215+- A model already loaded before the team starts (by another agent, or `llmman run`) stays loaded beside the team's: unload it first (`curl -s http://127.0.0.1:17434/api/generate -d '{"model":"…","keep_alive":0}'`).
216+
217+## Files
218+
219+```
220+dev.team/
221+├── sbxenv.yaml the sandbox: workspace/ read-write, team/ read-only, AGENT_CONFIG
222+├── start-zed.sh, stop-zed.sh create the sandbox and open workspace/ in Zed; remove it
223+├── team/ read-only in the sandbox
224+│ ├── agent.yaml coordinator (🧭)
225+│ ├── agent.reformulator.yaml 📝
226+│ ├── agent.coder.yaml 🛠️
227+│ ├── agent.coder.ornith.yaml 🛠️ the coder on Ornith 1.5 9B (steadier, slower)
228+│ ├── agent.reviewer.yaml 🔎
229+│ ├── agent.tutor.yaml 📘 (the "prof" configuration of ticket 152, adapted)
230+│ └── hooks/ unload.js (one model in memory at a time), gofmt.js (the coder's formatter)
231+├── workspace/ read-write: empty in the repository (.gitkeep) — your project goes here;
232+│ .mm/ (sessions, traces) is created on the first run
233+├── dev-team-flow.* diagram: the three routes (.drawio .svg .png)
234+├── dev-team-memory.* diagram: the unloads (.drawio .svg .png)
235+└── dev-team-sandbox.* diagram: the sandbox mounts (.drawio .svg .png)
236+```
237+
238+The diagrams are generated by `.claude/skills/drawio-fancy-diagram/generator/examples/dev_team.py dev.team`.
new file mode 100644
@@ -0,0 +1,238 @@
1+# dev.team — a team of agents that helps you code
2+
3+*[Version française](README.fr.md)*
4+
5+Five `mm` agents for a 32 GB MacBook Air: a coordinator you talk to, and four specialists it calls with the built-in `ask_agent` tool, one at a time. A hook unloads each model when the team hands over, so **only one model is in memory at a time**.
6+
7+![The coordinator's three routes](dev-team-flow.svg)
8+
9+| Agent | File | Header title | Model (llmman) | Tools | Job |
10+|---|---|---|---|---|---|
11+| coordinator | `agent.yaml` | 🧭 Coordinator | Qwen3.5 4B · 3.4 GB | `ask_agent` only | picks the route, hands over, reports to you |
12+| reformulator | `agent.reformulator.yaml` | 📝 Reformulator | Gemma 4 12B · 7.3 GB | `bash` (reading), `ask_user`, `lsp` (reading) | turns a code request into a task brief; asks you what only you can decide |
13+| coder | `agent.coder.yaml` | 🛠️ Coder | Mellum2 12B-A2.5B · 8.1 GB | `bash`, files, `lsp`; a `gofmt` hook | fixes, adds, changes code; runs the tests |
14+| reviewer | `agent.reviewer.yaml` | 🔎 Reviewer | Gemma 4 12B · 7.3 GB | `bash`, files (tests only), `lsp` (reading) | reads the diff, runs the tests, writes its own edge-case tests, `VERDICT: PASS / FAIL` |
15+| tutor | `agent.tutor.yaml` | 📘 Tutor | Mellum2 12B-A2.5B · 8.1 GB | `fetch_content`, `save_answer`, `bash`, files, `lsp` (reading) | answers questions with a mini tutorial; saves it on request |
16+
17+The full model ids are `hf.co/unsloth/Qwen3.5-4B-GGUF:Q4_K_M`, `hf.co/unsloth/gemma-4-12b-it-GGUF:Q4_K_M` and `hf.co/JetBrains/Mellum2-12B-A2.5B-Instruct-GGUF-Q4_K_M:Q4_K_M`.
18+
19+## The three routes
20+
21+The coordinator reads the request and picks one route (its prompt, in `agent.yaml`):
22+
23+- **A — code to write or change** (a bug, a feature, a rename, tests): reformulator → coder → reviewer. If the reviewer answers `VERDICT: FAIL`, the coder fixes **once** and the reviewer checks again. Then the coordinator reports: who did what, the files, the tests, the verdict.
24+- **B — a question, a lesson, advice** ("how do I…", "what is…", "explique-moi…"): the tutor, and its answer is given as it is.
25+- **C — only a review** of existing code: the reviewer.
26+
27+The team works on the code in **`workspace/`**, which is empty in the repository: put your project there (a git repository, so that the reviewer can read `git diff`), or ask the team to create one. The measurements below were made on `textstats`, a small Go practice package with a known bug (`WordCount` and several spaces), which this folder no longer ships.
28+
29+## One model in memory at a time
30+
31+![Hooks unload a model at each hand-off](dev-team-memory.svg)
32+
33+The ticket's constraint: when the team switches agent, the current model leaves memory. That is not left to the models (a small model forgets to call `unload_model`: 4 times out of 5 with MiniCPM5 2B in `demos/unload-models`). Two hooks of the coordinator do it, with `hooks/unload.js` (copied from `demos/unload-models`):
34+
35+| Hook | When | Unloads |
36+|---|---|---|
37+| `preAsk` → `unload.js self` | the coordinator has decided to call an agent, before that agent starts | the coordinator's own 4B |
38+| `postAsk` → `unload.js asked` | the agent has answered, before the coordinator reads the answer | the agent's 12B |
39+
40+llmman reloads a model by itself on the next request, so the coordinator's 4B comes back alone to read each answer. Both hooks have `onError: warn`: a failed unload costs memory, not the hand-off. The four specialists have `unloadModels: false`: no `unload_model` tool to think about.
41+
42+Two models are shared on purpose: the reformulator and the reviewer use Gemma 4 12B, the coder and the tutor Mellum2. **Peak memory: one 12B model**, about 8 GB plus its KV cache at a 32k window.
43+
44+### The coder's formatter hook
45+
46+`agent.coder.yaml` has its own `postToolCall` hook: after each `write_file` or `edit_file` on a `.go` file, `hooks/gofmt.js` runs `gofmt -w` on it. The prompt asked for `gofmt` and Mellum2 skipped it in every measured run. When gofmt cannot parse the file, its error goes back to the model as context.
47+
48+### The Go language server
49+
50+The four specialists have an `lsp` block with gopls for `.go` files; the coordinator has no tool but `ask_agent`, by design. Each prompt says what to use it for:
51+
52+| Agent | Uses `lsp` for |
53+|---|---|
54+| coder | `references` before changing a signature, `rename` to rename an identifier everywhere, `diagnostics` after a change |
55+| reviewer | `references` on every function the diff touches, `diagnostics` on each changed file |
56+| reformulator | `references` and `symbols`, to name the files a change touches |
57+| tutor | `hover` and `definition` on a symbol of the code a question is about |
58+
59+Only the coder may `rename`; the others' prompts forbid it. gopls finds the `go.mod` of a project in a sub-folder of `workspace/` by itself, although the agents run from `workspace/` (measured with `textstats`).
60+
61+## Prerequisites
62+
63+- **llmman** serving on its default address, with the three models:
64+
65+ ```bash
66+ llmman pull hf.co/unsloth/Qwen3.5-4B-GGUF:Q4_K_M
67+ llmman pull hf.co/unsloth/gemma-4-12b-it-GGUF:Q4_K_M
68+ llmman pull hf.co/JetBrains/Mellum2-12B-A2.5B-Instruct-GGUF-Q4_K_M:Q4_K_M
69+ llmman serve
70+ ```
71+
72+- **Node.js** (the hooks), **Go** ≥ 1.24 and **git** (the reviewer reads `git diff`).
73+- **gopls** on the `PATH`: `go install golang.org/x/tools/gopls@latest` (then `$(go env GOPATH)/bin` in the `PATH`). Without it, each specialist starts with a warning and no `lsp` tool.
74+- **mm** built at the repository root: `go build -o mm .`
75+
76+## Layout: agents read-only, code read-write
77+
78+![team/ read-only, workspace/ read-write](dev-team-sandbox.svg)
79+
80+```
81+dev.team/
82+├── sbxenv.yaml, start-zed.sh, stop-zed.sh the sandbox — never mounted
83+├── README.*, dev-team-*.{drawio,svg,png} this documentation
84+├── team/ the agents and their hooks → mounted READ-ONLY
85+└── workspace/ your code (empty in the repository), .mm/ → the read-write workspace
86+```
87+
88+The agents' files are what decides what the team may do: their prompts, their tools, `yolo`, the hooks. A team member with `bash` and `write_file` could rewrite them, and the next start would run its version. So they live in `team/`, which the sandbox mounts with `readOnly: true`, apart from the code the team changes. Docker also recommends keeping the environment file outside every mounted directory, so that an agent cannot change the commands your host runs: `sbxenv.yaml` and the scripts stay at the root of `dev.team/`, which is not mounted.
89+
90+mm finds the coordinator through `AGENT_CONFIG`, set in `sbxenv.yaml`; the other agents and the hooks are found next to it (`mmConfigs` paths and `$MM_CONFIG_DIR` are relative to `team/agent.yaml`). Sessions and traces go to `workspace/.mm/`, the session's working directory.
91+
92+## Run
93+
94+### In a sandbox, with Zed
95+
96+```bash
97+cd dev.team
98+sbx setup ssh # once
99+./start-zed.sh # sbx env create --env-arg DEV_TEAM_DIR=$PWD, then Zed on workspace/
100+./stop-zed.sh # removes the sandbox; team/ and workspace/ stay on the host
101+```
102+
103+`start-zed.sh` passes the folder of `sbxenv.yaml` as the argument `DEV_TEAM_DIR`; `sbxenv.yaml` builds `AGENT_CONFIG=<DEV_TEAM_DIR>/team/agent.yaml` from it. In a terminal of the sandbox, from `workspace/`: `mm -tui` (it reads `AGENT_CONFIG`).
104+
105+To check, once the sandbox is up: `sbx version` (`sbx env` needs 0.39.0 or newer, which is also past CVE-2026-18171, a way to write into a read-only mount in 0.35.0–0.38.0); in the sandbox, `echo $AGENT_CONFIG`, `ls "$(dirname "$AGENT_CONFIG")"` and `touch "$(dirname "$AGENT_CONFIG")/x"`, which must fail.
106+
107+### On the host
108+
109+```bash
110+cd dev.team/workspace
111+AGENT_CONFIG=$PWD/../team/agent.yaml ../../mm -tui
112+../../mm -config ../team/agent.yaml -tui # the same
113+../../mm -config ../team/agent.yaml -p "…" # one request, then exit (the reformulator cannot ask you anything)
114+```
115+
116+The sandbox publishes ports 8080, 6060 and 5050 to the same ports on the host (`ports` in `sbxenv.yaml`, applied at creation): a server started in the sandbox must listen on `0.0.0.0` to be reached on `http://localhost:8080`.
117+
118+On the host nothing makes `team/` read-only: that protection is the sandbox's.
119+
120+In the window, the header names the agent at work: `mm [🧭 Coordinator] · qwen3.5-4b:Q4_K_M @ llmman`, then `mm [🛠️ Coder] · … · asked by mm [🧭 Coordinator]` during a hand-off.
121+
122+`yolo: true` in `agent.yaml` and in every `mmConfigs` entry: hand-offs, commands and file writes run without asking. To approve each hand-off and read the prompt the coordinator wrote: `AGENT_YOLO=false` before `mm`.
123+
124+Each specialist also works alone, e.g. `mm -config ../team/agent.tutor.yaml` from `workspace/` (alone, they ask before each command and file change, except the tutor).
125+
126+## Examples to try
127+
128+Put a project in `workspace/`, as a git repository with its last state committed, so that the reviewer's `git diff` shows only the team's change. The examples call it `my-app`.
129+
130+| Route | Request | What should happen |
131+|---|---|---|
132+| A | `Fix the bug in my-app: <what goes wrong, with an input and the expected output>.` | brief → fix + a test case → reviewer `VERDICT: PASS` |
133+| A | `Add a function to my-app that returns the most frequent words of a text.` | the reformulator asks you how many, case, punctuation, return type (in `-p`: it chooses and lists its ASSUMPTIONS) |
134+| A | `Rename <OldName> to <NewName> everywhere in my-app.` | brief → `lsp rename` + tests → review |
135+| B | `What is the difference between strings.Split and strings.Fields?` | a mini tutorial |
136+| B | `Explain table-driven tests in Go, using my-app/<file>_test.go as the example.` | the tutor reads the file and builds the lesson on its real code |
137+| B | `Explique-moi les goroutines avec un petit exemple, et sauve-le dans goroutines.md.` | a tutorial in French, saved with `save_answer` |
138+| C | `Review the code in my-app and tell me what is wrong.` | findings with file:line, tests, verdict |
139+
140+## Measured results
141+
142+llmman on the user's 32 GB machine, 2026-10-25, on the `textstats` practice package (`playground/` in the tables), before `workspace/` was emptied.
143+
144+### Each agent alone
145+
146+| Agent | Task | Result | Time |
147+|---|---|---|---|
148+| coder | the `WordCount` bug | fixed with `strings.Fields`, 4 test cases added, tests green | 351 s |
149+| reviewer | review of that fix | `git diff`, tests, its own `review_test.go`, `VERDICT: PASS` | 151 s |
150+| tutor | table-driven tests on `playground/textstats_test.go` | 3 runs out of 3 read the file and teach on its real `map[string]int` table | 12–19 s |
151+| reformulator | "a function that gives the most frequent words" | reads the code, calls `ask_user`; with nobody to answer (`-p`), lists its choices under ASSUMPTIONS | 62 s |
152+
153+What these runs changed in the files:
154+
155+- **coder**: once, after a failed `edit_file`, Mellum2 wrote a tool call that never closed and ran to `max_tokens` (8192): most of the 351 s. `max_tokens` is now 4096, which halves what such a call costs.
156+- **tutor**: the first version answered in 24 s but showed "a typical" test file it had invented. The prompt now makes `read_file` its first action when a file is named.
157+- **reformulator**: the first version did not list its choices, and put "top N" out of scope for "the most frequent words". The prompt now names what only the user decides, and the brief has an ASSUMPTIONS section.
158+
159+### Does the team need a decision model?
160+
161+The question of the ticket. A decider (see `demos/decision-*`) would pay off if the coordinator chose the wrong route. Measured: the coordinator (Qwen3.5 4B, reasoning off) on 12 requests — 5 code changes, 5 questions (2 in French), 2 reviews — **12 out of 12 to the right agent**. A decider would add a model and a call for nothing. Where one could help later, to be measured first: spotting a request too vague to code (a noul question before the reformulator), or reading the reviewer's verdict.
162+
163+### The whole team
164+
165+Through the coordinator, `-p`, `AGENT_YOLO=true`; llmman's loaded models sampled every 2 seconds.
166+
167+| Run | Route | Result | Time |
168+|---|---|---|---|
169+| bug fix, 1st | A | `VERDICT: PASS`, but the coder looped on `plan update` and was called twice; the report said "no changes" | 685 s |
170+| bug fix, 2nd (after the fixes below) | A | one call per agent, `VERDICT: PASS`, formatted, an exact report | 398 s |
171+| "explique-moi strings.Split et strings.Fields" | B | the tutor answers in French, correct | 49 s |
172+| "the most frequent words", 1st | A | **failed**: the reviewer tested a behaviour not in the brief and hit its 20 turns; the coordinator re-asked it, then called the coder 4 times; tests red at the end | 52 min |
173+| "the most frequent words", 2nd (after the fixes below) | A | **failed**: a good brief (top 3), then the coder hit the cut-off failure and the coordinator re-asked it; the reviewer kept `VERDICT: FAIL` (2 of its 7 cases); stopped by the 30-minute limit of the measurement | 30 min |
174+
175+**Memory: within the team's hand-offs, never two models loaded at the same time**: 4B → Gemma → 4B → Mellum2 → 4B → Gemma → 4B. The one overlap seen (the first 3 minutes of the last run) was a Mellum2 left loaded by a measurement run before the team started: the hooks unload the models of the hand-offs, not a model something else loaded.
176+
177+**In short: bug fixes, lessons and reviews work; new features do not yet.** The weak link is the coder: on a feature, Mellum2 writes tool calls that repeat themselves until `max_tokens`, and the 4B coordinator then re-asks it despite its prompt. Try `agent.coder.ornith.yaml` for features (see below).
178+
179+What the runs changed:
180+
181+- **coder**: no `plan` tool (Mellum2 called `plan update` in a loop); "add test cases, keep the existing ones"; the `gofmt` hook.
182+- **reviewer**: its tests cover the brief's ACCEPTANCE and REQUIREMENTS only, a failing case of its own is a finding (no rewriting until it passes), `gofmt -l` and removed test cases are findings, `maxTurns` 20 → 12 (then 16, see below).
183+- **coordinator**: `maxTurns` 8 → 6; "never re-ask an agent with a new prompt"; the report takes the files from the coder's and the reviewer's answers.
184+
185+### With the Go language server
186+
187+Measured after adding `lsp` (gopls v0.24.0):
188+
189+| Agent | Task | Result | Time |
190+|---|---|---|---|
191+| coder, 1st prompt | "Rename LineCount to CountLines everywhere in playground" | called `rename` without `new_name`, then copied the hint literally: the function became `new_name`, and the report said "renamed to CountLines" | 18 s |
192+| coder, with a filled-in example in the prompt | the same | 3 runs out of 3: `lsp rename` → `CountLines`, tests green | 5–10 s |
193+| reviewer | review of that rename | run 1: a tool call that repeated itself, stopped by mm; run 2: `references` + 2 `diagnostics`, a real finding (the test's error message still says `LineCount`: `rename` changes identifiers, not strings), `VERDICT: FAIL` | 200 s, 167 s |
194+
195+The reviewer's prompt also had to say to run `git diff` **inside** `playground/`: from the folder above it, git reads mm's repository, where `playground/` is ignored, and the diff is empty. Its `maxTurns` is now 16: 12 cut a normal review (13 calls) before its verdict.
196+
197+### Which coder?
198+
199+The ticket allows questioning its model choices, so the coder was measured alone on the reformulator's brief for "the most frequent words", against a hidden test made of the brief's acceptance cases (copied in after the run), `go vet` and `gofmt`:
200+
201+| Coder | Runs correct | Time per run | Formatted |
202+|---|---|---|---|
203+| Mellum2 12B-A2.5B Instruct (`agent.coder.yaml`) | 2 out of 3 | 129 s, 132 s, 639 s (the failure) | no (before the hook) |
204+| Ornith 1.5 9B, reasoning on (`agent.coder.ornith.yaml`) | 2 out of 2 | 783 s, 810 s | yes |
205+
206+Mellum2 stays the default, as the ticket chose: about 6 times faster, and right 2 times out of 3. Its failure mode is the one seen above: a tool call that repeats itself until `max_tokens`. Ornith is the steadier choice when time matters less: point `mmConfigs.coder.path` at `agent.coder.ornith.yaml` (6.7 GB, so it changes nothing to the memory peak).
207+
208+## Limits
209+
210+- **The reformulator's bash is read-only by its prompt only.** It has no file tools, but `bash` could write; with `yolo: true` nothing asks. For a guarantee, add a `preToolCall` hook that refuses commands that write (see `demos/hooks`).
211+- **The team can write anywhere in `workspace/`, not only in `playground/`.** Once, before the split, a member wrote a `main.go` next to `agent.yaml`, which broke mm's `go vet ./...`. In the sandbox it can no longer touch `team/`; check `git status` after a run.
212+- **`-p` cannot ask you anything**: the reformulator then chooses and says so.
213+- **Each reload costs time**: unloading at every hand-off trades seconds of loading for memory. On a machine with more memory, remove the `preAsk` hook to keep the 4B loaded.
214+- When `ask_agent` is refused (`yolo: false` and you say no), the 4B coordinator tends to try another agent rather than stop; after a failed hand-off, it re-asks the agent although its prompt says not to (`maxTurns: 6` bounds it).
215+- A model already loaded before the team starts (by another agent, or `llmman run`) stays loaded beside the team's: unload it first (`curl -s http://127.0.0.1:17434/api/generate -d '{"model":"…","keep_alive":0}'`).
216+
217+## Files
218+
219+```
220+dev.team/
221+├── sbxenv.yaml the sandbox: workspace/ read-write, team/ read-only, AGENT_CONFIG
222+├── start-zed.sh, stop-zed.sh create the sandbox and open workspace/ in Zed; remove it
223+├── team/ read-only in the sandbox
224+│ ├── agent.yaml coordinator (🧭)
225+│ ├── agent.reformulator.yaml 📝
226+│ ├── agent.coder.yaml 🛠️
227+│ ├── agent.coder.ornith.yaml 🛠️ the coder on Ornith 1.5 9B (steadier, slower)
228+│ ├── agent.reviewer.yaml 🔎
229+│ ├── agent.tutor.yaml 📘 (the "prof" configuration of ticket 152, adapted)
230+│ └── hooks/ unload.js (one model in memory at a time), gofmt.js (the coder's formatter)
231+├── workspace/ read-write: empty in the repository (.gitkeep) — your project goes here;
232+│ .mm/ (sessions, traces) is created on the first run
233+├── dev-team-flow.* diagram: the three routes (.drawio .svg .png)
234+├── dev-team-memory.* diagram: the unloads (.drawio .svg .png)
235+└── dev-team-sandbox.* diagram: the sandbox mounts (.drawio .svg .png)
236+```
237+
238+The diagrams are generated by `.claude/skills/drawio-fancy-diagram/generator/examples/dev_team.py dev.team`.
added dev.team/README.fr.md +238 -0
new file mode 100644
@@ -0,0 +1,238 @@
1+# dev.team — une équipe d'agents qui aide à coder
2+
3+*[English version](README.en.md)*
4+
5+Cinq agents `mm` pour un MacBook Air de 32 Go : un coordinateur à qui vous parlez, et quatre spécialistes qu'il appelle avec l'outil intégré `ask_agent`, un à la fois. Un hook décharge chaque modèle quand l'équipe passe la main : **un seul modèle en mémoire à la fois**.
6+
7+![Les trois routes du coordinateur](dev-team-flow.svg)
8+
9+| Agent | Fichier | Titre dans l'en-tête | Modèle (llmman) | Outils | Rôle |
10+|---|---|---|---|---|---|
11+| coordinator | `agent.yaml` | 🧭 Coordinator | Qwen3.5 4B · 3,4 Go | `ask_agent` seulement | choisit la route, passe la main, vous fait le compte rendu |
12+| reformulator | `agent.reformulator.yaml` | 📝 Reformulator | Gemma 4 12B · 7,3 Go | `bash` (lecture), `ask_user`, `lsp` (lecture) | transforme une demande de code en cahier de tâche (brief) ; vous demande ce que vous seul pouvez décider |
13+| coder | `agent.coder.yaml` | 🛠️ Coder | Mellum2 12B-A2.5B · 8,1 Go | `bash`, fichiers, `lsp` ; un hook `gofmt` | corrige, ajoute, modifie le code ; lance les tests |
14+| reviewer | `agent.reviewer.yaml` | 🔎 Reviewer | Gemma 4 12B · 7,3 Go | `bash`, fichiers (tests seulement), `lsp` (lecture) | lit le diff, lance les tests, écrit ses propres tests de cas limites, `VERDICT: PASS / FAIL` |
15+| tutor | `agent.tutor.yaml` | 📘 Tutor | Mellum2 12B-A2.5B · 8,1 Go | `fetch_content`, `save_answer`, `bash`, fichiers, `lsp` (lecture) | répond par un mini tutoriel ; l'enregistre sur demande |
16+
17+Les identifiants complets des modèles sont `hf.co/unsloth/Qwen3.5-4B-GGUF:Q4_K_M`, `hf.co/unsloth/gemma-4-12b-it-GGUF:Q4_K_M` et `hf.co/JetBrains/Mellum2-12B-A2.5B-Instruct-GGUF-Q4_K_M:Q4_K_M`. Les noms, les fichiers et les prompts sont en anglais ; les agents vous répondent dans votre langue.
18+
19+## Les trois routes
20+
21+Le coordinateur lit la demande et choisit une route (son prompt, dans `agent.yaml`) :
22+
23+- **A — du code à écrire ou modifier** (un bug, une fonctionnalité, un renommage, des tests) : reformulator → coder → reviewer. Si le reviewer répond `VERDICT: FAIL`, le coder corrige **une fois** et le reviewer vérifie à nouveau. Puis le coordinateur fait le compte rendu : qui a fait quoi, les fichiers, les tests, le verdict.
24+- **B — une question, une leçon, un conseil** (« comment… », « c'est quoi… », « explique-moi… ») : le tutor, dont la réponse est transmise telle quelle.
25+- **C — seulement une revue** de code existant : le reviewer.
26+
27+L'équipe travaille sur le code de **`workspace/`**, vide dans le dépôt : mettez-y votre projet (un dépôt git, pour que le reviewer puisse lire `git diff`), ou demandez à l'équipe d'en créer un. Les mesures plus bas ont été faites sur `textstats`, un petit paquet Go d'entraînement avec un bug connu (`WordCount` et les espaces multiples), que ce dossier ne fournit plus.
28+
29+## Un seul modèle en mémoire à la fois
30+
31+![Les hooks déchargent un modèle à chaque passage de main](dev-team-memory.svg)
32+
33+La contrainte du ticket : quand l'équipe change d'agent, le modèle en cours quitte la mémoire. Ce n'est pas laissé aux modèles (un petit modèle oublie d'appeler `unload_model` : 4 fois sur 5 avec MiniCPM5 2B dans `demos/unload-models`). Deux hooks du coordinateur s'en chargent, avec `hooks/unload.js` (copié de `demos/unload-models`) :
34+
35+| Hook | Quand | Décharge |
36+|---|---|---|
37+| `preAsk` → `unload.js self` | le coordinateur a décidé d'appeler un agent, avant que cet agent démarre | le 4B du coordinateur |
38+| `postAsk` → `unload.js asked` | l'agent a répondu, avant que le coordinateur lise la réponse | le 12B de l'agent |
39+
40+llmman recharge un modèle de lui-même à la requête suivante : le 4B du coordinateur revient donc seul pour lire chaque réponse. Les deux hooks ont `onError: warn` : un déchargement raté coûte de la mémoire, pas le passage de main. Les quatre spécialistes ont `unloadModels: false` : pas d'outil `unload_model` auquel penser.
41+
42+Deux modèles sont partagés exprès : le reformulator et le reviewer utilisent Gemma 4 12B, le coder et le tutor Mellum2. **Pic mémoire : un seul modèle de 12B**, environ 8 Go plus son cache KV avec une fenêtre de 32k.
43+
44+### Le hook de formatage du coder
45+
46+`agent.coder.yaml` a son propre hook `postToolCall` : après chaque `write_file` ou `edit_file` sur un fichier `.go`, `hooks/gofmt.js` lance `gofmt -w` dessus. Le prompt demandait `gofmt`, et Mellum2 l'a sauté à chaque exécution mesurée. Quand gofmt ne peut pas lire le fichier, son erreur revient au modèle comme contexte.
47+
48+### Le serveur de langage Go
49+
50+Les quatre spécialistes ont un bloc `lsp` avec gopls pour les fichiers `.go` ; le coordinateur n'a pas d'autre outil qu'`ask_agent`, par choix. Chaque prompt dit à quoi il sert :
51+
52+| Agent | Utilise `lsp` pour |
53+|---|---|
54+| coder | `references` avant de changer une signature, `rename` pour renommer un identifiant partout, `diagnostics` après une modification |
55+| reviewer | `references` sur chaque fonction touchée par le diff, `diagnostics` sur chaque fichier modifié |
56+| reformulator | `references` et `symbols`, pour nommer les fichiers qu'une modification touche |
57+| tutor | `hover` et `definition` sur un symbole du code dont parle la question |
58+
59+Seul le coder peut faire `rename` ; les prompts des autres l'interdisent. gopls trouve tout seul le `go.mod` d'un projet placé dans un sous-dossier de `workspace/`, bien que les agents tournent depuis `workspace/` (mesuré avec `textstats`).
60+
61+## Prérequis
62+
63+- **llmman** sur son adresse par défaut, avec les trois modèles :
64+
65+ ```bash
66+ llmman pull hf.co/unsloth/Qwen3.5-4B-GGUF:Q4_K_M
67+ llmman pull hf.co/unsloth/gemma-4-12b-it-GGUF:Q4_K_M
68+ llmman pull hf.co/JetBrains/Mellum2-12B-A2.5B-Instruct-GGUF-Q4_K_M:Q4_K_M
69+ llmman serve
70+ ```
71+
72+- **Node.js** (les hooks), **Go** ≥ 1.24 et **git** (le reviewer lit `git diff`).
73+- **gopls** dans le `PATH` : `go install golang.org/x/tools/gopls@latest` (puis `$(go env GOPATH)/bin` dans le `PATH`). Sans lui, chaque spécialiste démarre avec un avertissement et sans outil `lsp`.
74+- **mm** compilé à la racine du dépôt : `go build -o mm .`
75+
76+## Organisation : agents en lecture seule, code en lecture-écriture
77+
78+![team/ en lecture seule, workspace/ en lecture-écriture](dev-team-sandbox.svg)
79+
80+```
81+dev.team/
82+├── sbxenv.yaml, start-zed.sh, stop-zed.sh la sandbox — jamais montés
83+├── README.*, dev-team-*.{drawio,svg,png} cette documentation
84+├── team/ les agents et leurs hooks → monté en LECTURE SEULE
85+└── workspace/ votre code (vide dans le dépôt), .mm/ → l'espace de travail en lecture-écriture
86+```
87+
88+Les fichiers des agents décident de ce que l'équipe a le droit de faire : leurs prompts, leurs outils, `yolo`, les hooks. Un membre de l'équipe qui a `bash` et `write_file` pourrait les réécrire, et le prochain démarrage exécuterait sa version. Ils sont donc dans `team/`, que la sandbox monte avec `readOnly: true`, à part du code que l'équipe modifie. Docker recommande aussi de garder le fichier d'environnement hors de tout dossier monté, pour qu'un agent ne puisse pas changer les commandes que lance votre machine : `sbxenv.yaml` et les scripts restent à la racine de `dev.team/`, qui n'est pas montée.
89+
90+mm trouve le coordinateur grâce à `AGENT_CONFIG`, défini dans `sbxenv.yaml` ; les autres agents et les hooks sont trouvés à côté (les chemins de `mmConfigs` et `$MM_CONFIG_DIR` sont relatifs à `team/agent.yaml`). Les sessions et les traces vont dans `workspace/.mm/`, le dossier de travail de la session.
91+
92+## Lancer
93+
94+### Dans une sandbox, avec Zed
95+
96+```bash
97+cd dev.team
98+sbx setup ssh # une fois
99+./start-zed.sh # sbx env create --env-arg DEV_TEAM_DIR=$PWD, puis Zed sur workspace/
100+./stop-zed.sh # supprime la sandbox ; team/ et workspace/ restent sur la machine
101+```
102+
103+`start-zed.sh` passe le dossier de `sbxenv.yaml` comme argument `DEV_TEAM_DIR` ; `sbxenv.yaml` en construit `AGENT_CONFIG=<DEV_TEAM_DIR>/team/agent.yaml`. Dans un terminal de la sandbox, depuis `workspace/` : `mm -tui` (il lit `AGENT_CONFIG`).
104+
105+Pour vérifier, une fois la sandbox lancée : `sbx version` (`sbx env` demande la 0.39.0 ou plus, ce qui place aussi après la CVE-2026-18171, qui permettait d'écrire dans un montage en lecture seule en 0.35.0–0.38.0) ; dans la sandbox, `echo $AGENT_CONFIG`, `ls "$(dirname "$AGENT_CONFIG")"` et `touch "$(dirname "$AGENT_CONFIG")/x"`, qui doit échouer.
106+
107+### Sur la machine
108+
109+```bash
110+cd dev.team/workspace
111+AGENT_CONFIG=$PWD/../team/agent.yaml ../../mm -tui
112+../../mm -config ../team/agent.yaml -tui # la même chose
113+../../mm -config ../team/agent.yaml -p "…" # une demande, puis sortie (le reformulator ne peut rien vous demander)
114+```
115+
116+La sandbox publie les ports 8080, 6060 et 5050 sur les mêmes ports de la machine (`ports` dans `sbxenv.yaml`, appliqué à la création) : un serveur lancé dans la sandbox doit écouter sur `0.0.0.0` pour être joignable sur `http://localhost:8080`.
117+
118+Sur la machine, rien ne met `team/` en lecture seule : cette protection est celle de la sandbox.
119+
120+Dans la fenêtre, l'en-tête nomme l'agent au travail : `mm [🧭 Coordinator] · qwen3.5-4b:Q4_K_M @ llmman`, puis `mm [🛠️ Coder] · … · asked by mm [🧭 Coordinator]` pendant un passage de main.
121+
122+`yolo: true` dans `agent.yaml` et dans chaque entrée de `mmConfigs` : les passages de main, les commandes et les écritures de fichiers se font sans demander. Pour approuver chaque passage de main et lire le prompt écrit par le coordinateur : `AGENT_YOLO=false` devant `mm`.
123+
124+Chaque spécialiste fonctionne aussi seul, par exemple `mm -config ../team/agent.tutor.yaml` depuis `workspace/` (seuls, ils demandent avant chaque commande et chaque modification de fichier, sauf le tutor).
125+
126+## Exemples à essayer
127+
128+Mettez un projet dans `workspace/`, en dépôt git avec son dernier état commité, pour que le `git diff` du reviewer ne montre que la modification de l'équipe. Les exemples l'appellent `my-app`.
129+
130+| Route | Demande | Ce qui doit se passer |
131+|---|---|---|
132+| A | `Corrige le bug de my-app : <ce qui ne va pas, avec une entrée et la sortie attendue>.` | brief → correction + un cas de test → reviewer `VERDICT: PASS` |
133+| A | `Ajoute à my-app une fonction qui renvoie les mots les plus fréquents d'un texte.` | le reformulator vous demande combien, la casse, la ponctuation, le type de retour (avec `-p` : il choisit et liste ses ASSUMPTIONS) |
134+| A | `Renomme <AncienNom> en <NouveauNom> partout dans my-app.` | brief → `lsp rename` + tests → revue |
135+| B | `Quelle est la différence entre strings.Split et strings.Fields ?` | un mini tutoriel en français |
136+| B | `Explique les tests en table en Go, avec my-app/<fichier>_test.go comme exemple.` | le tutor lit le fichier et bâtit la leçon sur son vrai code |
137+| B | `Explique-moi les goroutines avec un petit exemple, et sauve-le dans goroutines.md.` | un tutoriel en français, enregistré avec `save_answer` |
138+| C | `Fais la revue du code de my-app et dis-moi ce qui ne va pas.` | des constats avec fichier:ligne, les tests, le verdict |
139+
140+## Résultats mesurés
141+
142+llmman sur la machine de 32 Go de l'utilisateur, le 2026-10-25, sur le paquet d'entraînement `textstats` (`playground/` dans les tableaux), avant que `workspace/` soit vidé.
143+
144+### Chaque agent seul
145+
146+| Agent | Tâche | Résultat | Temps |
147+|---|---|---|---|
148+| coder | le bug de `WordCount` | corrigé avec `strings.Fields`, 4 cas de test ajoutés, tests au vert | 351 s |
149+| reviewer | revue de cette correction | `git diff`, tests, son propre `review_test.go`, `VERDICT: PASS` | 151 s |
150+| tutor | les tests en table sur `playground/textstats_test.go` | 3 exécutions sur 3 lisent le fichier et enseignent sur sa vraie table `map[string]int` | 12–19 s |
151+| reformulator | « une fonction qui donne les mots les plus fréquents » | lit le code, appelle `ask_user` ; sans personne pour répondre (`-p`), liste ses choix sous ASSUMPTIONS | 62 s |
152+
153+Ce que ces exécutions ont changé dans les fichiers :
154+
155+- **coder** : une fois, après un `edit_file` raté, Mellum2 a écrit un appel d'outil jamais refermé, qui a couru jusqu'à `max_tokens` (8192) : l'essentiel des 351 s. `max_tokens` vaut maintenant 4096, ce qui divise par deux le coût d'un tel appel.
156+- **tutor** : la première version répondait en 24 s mais montrait un fichier de test « typique » qu'elle avait inventé. Le prompt fait maintenant de `read_file` sa première action quand un fichier est nommé.
157+- **reformulator** : la première version ne listait pas ses choix, et mettait « top N » hors périmètre pour « les mots les plus fréquents ». Le prompt nomme maintenant ce que seul l'utilisateur décide, et le brief a une section ASSUMPTIONS.
158+
159+### L'équipe a-t-elle besoin d'un modèle de décision ?
160+
161+La question du ticket. Un décideur (voir `demos/decision-*`) serait utile si le coordinateur se trompait de route. Mesuré : le coordinateur (Qwen3.5 4B, sans raisonnement) sur 12 demandes — 5 modifications de code, 5 questions (dont 2 en français), 2 revues — **12 sur 12 vers le bon agent**. Un décideur ajouterait un modèle et un appel pour rien. Là où il pourrait servir plus tard, à mesurer d'abord : repérer une demande trop vague pour être codée (une question noul avant le reformulator), ou lire le verdict du reviewer.
162+
163+### L'équipe entière
164+
165+Par le coordinateur, `-p`, `AGENT_YOLO=true` ; les modèles chargés dans llmman relevés toutes les 2 secondes.
166+
167+| Exécution | Route | Résultat | Temps |
168+|---|---|---|---|
169+| correction du bug, 1re | A | `VERDICT: PASS`, mais le coder a bouclé sur `plan update` et a été appelé deux fois ; le compte rendu disait « aucune modification » | 685 s |
170+| correction du bug, 2e (après les corrections ci-dessous) | A | un appel par agent, `VERDICT: PASS`, code formaté, compte rendu exact | 398 s |
171+| « explique-moi strings.Split et strings.Fields » | B | le tutor répond en français, juste | 49 s |
172+| « les mots les plus fréquents », 1re | A | **échec** : le reviewer a testé un comportement absent du brief et atteint ses 20 tours ; le coordinateur l'a redemandé, puis a appelé le coder 4 fois ; tests rouges à la fin | 52 min |
173+| « les mots les plus fréquents », 2e (après les corrections ci-dessous) | A | **échec** : un bon brief (top 3), puis le coder a eu la même coupure et le coordinateur l'a redemandé ; le reviewer est resté à `VERDICT: FAIL` (2 de ses 7 cas) ; arrêté par la limite de 30 minutes de la mesure | 30 min |
174+
175+**Mémoire : pendant les passages de main de l'équipe, jamais deux modèles chargés en même temps** : 4B → Gemma → 4B → Mellum2 → 4B → Gemma → 4B. Le seul chevauchement vu (les 3 premières minutes de la dernière exécution) était un Mellum2 laissé chargé par une mesure lancée avant l'équipe : les hooks déchargent les modèles des passages de main, pas un modèle chargé par autre chose.
176+
177+**En bref : les corrections de bugs, les leçons et les revues fonctionnent ; les nouvelles fonctionnalités, pas encore.** Le maillon faible est le coder : sur une fonctionnalité, Mellum2 écrit des appels d'outil qui se répètent jusqu'à `max_tokens`, et le coordinateur 4B le redemande alors malgré son prompt. Essayez `agent.coder.ornith.yaml` pour les fonctionnalités (voir plus bas).
178+
179+Ce que ces exécutions ont changé :
180+
181+- **coder** : plus d'outil `plan` (Mellum2 appelait `plan update` en boucle) ; « ajoute des cas de test, garde les existants » ; le hook `gofmt`.
182+- **reviewer** : ses tests couvrent seulement l'ACCEPTANCE et les REQUIREMENTS du brief ; un de ses cas qui échoue est un constat (pas de réécriture jusqu'à ce qu'il passe) ; `gofmt -l` et des cas de test supprimés sont des constats ; `maxTurns` 20 → 12 (puis 16, voir plus bas).
183+- **coordinateur** : `maxTurns` 8 → 6 ; « ne jamais redemander à un agent avec un nouveau prompt » ; le compte rendu prend les fichiers dans les réponses du coder et du reviewer.
184+
185+### Avec le serveur de langage Go
186+
187+Mesuré après l'ajout de `lsp` (gopls v0.24.0) :
188+
189+| Agent | Tâche | Résultat | Temps |
190+|---|---|---|---|
191+| coder, 1er prompt | « Rename LineCount to CountLines everywhere in playground » | a appelé `rename` sans `new_name`, puis a recopié l'indication mot pour mot : la fonction est devenue `new_name`, et le compte rendu disait « renommée en CountLines » | 18 s |
192+| coder, avec un exemple rempli dans le prompt | la même | 3 exécutions sur 3 : `lsp rename` → `CountLines`, tests au vert | 5–10 s |
193+| reviewer | revue de ce renommage | exécution 1 : un appel d'outil qui se répétait, arrêté par mm ; exécution 2 : `references` + 2 `diagnostics`, un vrai constat (le message d'erreur du test dit encore `LineCount` : `rename` change les identifiants, pas les chaînes), `VERDICT: FAIL` | 200 s, 167 s |
194+
195+Le prompt du reviewer a aussi dû préciser de lancer `git diff` **dans** `playground/` : depuis le dossier au-dessus, git lit le dépôt de mm, où `playground/` est ignoré, et le diff est vide. Son `maxTurns` vaut maintenant 16 : 12 coupait une revue normale (13 appels) avant son verdict.
196+
197+### Quel coder ?
198+
199+Le ticket permet de remettre en question ses choix de modèles : le coder a donc été mesuré seul sur le brief du reformulator pour « les mots les plus fréquents », contre un test caché fait des cas d'acceptation du brief (copié après l'exécution), `go vet` et `gofmt` :
200+
201+| Coder | Exécutions justes | Temps par exécution | Formaté |
202+|---|---|---|---|
203+| Mellum2 12B-A2.5B Instruct (`agent.coder.yaml`) | 2 sur 3 | 129 s, 132 s, 639 s (l'échec) | non (avant le hook) |
204+| Ornith 1.5 9B, raisonnement activé (`agent.coder.ornith.yaml`) | 2 sur 2 | 783 s, 810 s | oui |
205+
206+Mellum2 reste le coder par défaut, comme le ticket l'a choisi : environ 6 fois plus rapide, et juste 2 fois sur 3. Son mode d'échec est celui vu plus haut : un appel d'outil qui se répète jusqu'à `max_tokens`. Ornith est le choix le plus régulier quand le temps compte moins : faites pointer `mmConfigs.coder.path` sur `agent.coder.ornith.yaml` (6,7 Go, donc rien ne change au pic mémoire).
207+
208+## Limites
209+
210+- **Le bash du reformulator n'est en lecture seule que par son prompt.** Il n'a pas d'outils de fichiers, mais `bash` pourrait écrire ; avec `yolo: true`, rien ne demande. Pour une garantie, ajoutez un hook `preToolCall` qui refuse les commandes qui écrivent (voir `demos/hooks`).
211+- **L'équipe peut écrire partout dans `workspace/`, pas seulement dans `playground/`.** Une fois, avant la séparation, un membre a écrit un `main.go` à côté de `agent.yaml`, ce qui cassait le `go vet ./...` de mm. Dans la sandbox, il ne peut plus toucher `team/` ; regardez `git status` après une exécution.
212+- **`-p` ne peut rien vous demander** : le reformulator choisit alors, et le dit.
213+- **Chaque rechargement coûte du temps** : décharger à chaque passage de main échange des secondes de chargement contre de la mémoire. Sur une machine avec plus de mémoire, retirez le hook `preAsk` pour garder le 4B chargé.
214+- Quand `ask_agent` est refusé (`yolo: false` et vous dites non), le coordinateur 4B a tendance à essayer un autre agent plutôt qu'à s'arrêter ; après un passage de main raté, il redemande à l'agent bien que son prompt le lui interdise (`maxTurns: 6` le borne).
215+- Un modèle déjà chargé avant que l'équipe démarre (par un autre agent, ou `llmman run`) reste chargé à côté de ceux de l'équipe : déchargez-le d'abord (`curl -s http://127.0.0.1:17434/api/generate -d '{"model":"…","keep_alive":0}'`).
216+
217+## Fichiers
218+
219+```
220+dev.team/
221+├── sbxenv.yaml la sandbox : workspace/ en lecture-écriture, team/ en lecture seule, AGENT_CONFIG
222+├── start-zed.sh, stop-zed.sh créer la sandbox et ouvrir workspace/ dans Zed ; la supprimer
223+├── team/ en lecture seule dans la sandbox
224+│ ├── agent.yaml coordinateur (🧭)
225+│ ├── agent.reformulator.yaml 📝
226+│ ├── agent.coder.yaml 🛠️
227+│ ├── agent.coder.ornith.yaml 🛠️ le coder sur Ornith 1.5 9B (plus régulier, plus lent)
228+│ ├── agent.reviewer.yaml 🔎
229+│ ├── agent.tutor.yaml 📘 (la configuration « prof » du ticket 152, adaptée)
230+│ └── hooks/ unload.js (un seul modèle en mémoire), gofmt.js (le formatage du coder)
231+├── workspace/ en lecture-écriture : vide dans le dépôt (.gitkeep) — votre projet va ici ;
232+│ .mm/ (sessions, traces) est créé à la première exécution
233+├── dev-team-flow.* diagramme : les trois routes (.drawio .svg .png)
234+├── dev-team-memory.* diagramme : les déchargements (.drawio .svg .png)
235+└── dev-team-sandbox.* diagramme : les montages de la sandbox (.drawio .svg .png)
236+```
237+
238+Les diagrammes sont générés par `.claude/skills/drawio-fancy-diagram/generator/examples/dev_team.py dev.team`.
new file mode 100644
@@ -0,0 +1,238 @@
1+# dev.team — une équipe d'agents qui aide à coder
2+
3+*[English version](README.en.md)*
4+
5+Cinq agents `mm` pour un MacBook Air de 32 Go : un coordinateur à qui vous parlez, et quatre spécialistes qu'il appelle avec l'outil intégré `ask_agent`, un à la fois. Un hook décharge chaque modèle quand l'équipe passe la main : **un seul modèle en mémoire à la fois**.
6+
7+![Les trois routes du coordinateur](dev-team-flow.svg)
8+
9+| Agent | Fichier | Titre dans l'en-tête | Modèle (llmman) | Outils | Rôle |
10+|---|---|---|---|---|---|
11+| coordinator | `agent.yaml` | 🧭 Coordinator | Qwen3.5 4B · 3,4 Go | `ask_agent` seulement | choisit la route, passe la main, vous fait le compte rendu |
12+| reformulator | `agent.reformulator.yaml` | 📝 Reformulator | Gemma 4 12B · 7,3 Go | `bash` (lecture), `ask_user`, `lsp` (lecture) | transforme une demande de code en cahier de tâche (brief) ; vous demande ce que vous seul pouvez décider |
13+| coder | `agent.coder.yaml` | 🛠️ Coder | Mellum2 12B-A2.5B · 8,1 Go | `bash`, fichiers, `lsp` ; un hook `gofmt` | corrige, ajoute, modifie le code ; lance les tests |
14+| reviewer | `agent.reviewer.yaml` | 🔎 Reviewer | Gemma 4 12B · 7,3 Go | `bash`, fichiers (tests seulement), `lsp` (lecture) | lit le diff, lance les tests, écrit ses propres tests de cas limites, `VERDICT: PASS / FAIL` |
15+| tutor | `agent.tutor.yaml` | 📘 Tutor | Mellum2 12B-A2.5B · 8,1 Go | `fetch_content`, `save_answer`, `bash`, fichiers, `lsp` (lecture) | répond par un mini tutoriel ; l'enregistre sur demande |
16+
17+Les identifiants complets des modèles sont `hf.co/unsloth/Qwen3.5-4B-GGUF:Q4_K_M`, `hf.co/unsloth/gemma-4-12b-it-GGUF:Q4_K_M` et `hf.co/JetBrains/Mellum2-12B-A2.5B-Instruct-GGUF-Q4_K_M:Q4_K_M`. Les noms, les fichiers et les prompts sont en anglais ; les agents vous répondent dans votre langue.
18+
19+## Les trois routes
20+
21+Le coordinateur lit la demande et choisit une route (son prompt, dans `agent.yaml`) :
22+
23+- **A — du code à écrire ou modifier** (un bug, une fonctionnalité, un renommage, des tests) : reformulator → coder → reviewer. Si le reviewer répond `VERDICT: FAIL`, le coder corrige **une fois** et le reviewer vérifie à nouveau. Puis le coordinateur fait le compte rendu : qui a fait quoi, les fichiers, les tests, le verdict.
24+- **B — une question, une leçon, un conseil** (« comment… », « c'est quoi… », « explique-moi… ») : le tutor, dont la réponse est transmise telle quelle.
25+- **C — seulement une revue** de code existant : le reviewer.
26+
27+L'équipe travaille sur le code de **`workspace/`**, vide dans le dépôt : mettez-y votre projet (un dépôt git, pour que le reviewer puisse lire `git diff`), ou demandez à l'équipe d'en créer un. Les mesures plus bas ont été faites sur `textstats`, un petit paquet Go d'entraînement avec un bug connu (`WordCount` et les espaces multiples), que ce dossier ne fournit plus.
28+
29+## Un seul modèle en mémoire à la fois
30+
31+![Les hooks déchargent un modèle à chaque passage de main](dev-team-memory.svg)
32+
33+La contrainte du ticket : quand l'équipe change d'agent, le modèle en cours quitte la mémoire. Ce n'est pas laissé aux modèles (un petit modèle oublie d'appeler `unload_model` : 4 fois sur 5 avec MiniCPM5 2B dans `demos/unload-models`). Deux hooks du coordinateur s'en chargent, avec `hooks/unload.js` (copié de `demos/unload-models`) :
34+
35+| Hook | Quand | Décharge |
36+|---|---|---|
37+| `preAsk` → `unload.js self` | le coordinateur a décidé d'appeler un agent, avant que cet agent démarre | le 4B du coordinateur |
38+| `postAsk` → `unload.js asked` | l'agent a répondu, avant que le coordinateur lise la réponse | le 12B de l'agent |
39+
40+llmman recharge un modèle de lui-même à la requête suivante : le 4B du coordinateur revient donc seul pour lire chaque réponse. Les deux hooks ont `onError: warn` : un déchargement raté coûte de la mémoire, pas le passage de main. Les quatre spécialistes ont `unloadModels: false` : pas d'outil `unload_model` auquel penser.
41+
42+Deux modèles sont partagés exprès : le reformulator et le reviewer utilisent Gemma 4 12B, le coder et le tutor Mellum2. **Pic mémoire : un seul modèle de 12B**, environ 8 Go plus son cache KV avec une fenêtre de 32k.
43+
44+### Le hook de formatage du coder
45+
46+`agent.coder.yaml` a son propre hook `postToolCall` : après chaque `write_file` ou `edit_file` sur un fichier `.go`, `hooks/gofmt.js` lance `gofmt -w` dessus. Le prompt demandait `gofmt`, et Mellum2 l'a sauté à chaque exécution mesurée. Quand gofmt ne peut pas lire le fichier, son erreur revient au modèle comme contexte.
47+
48+### Le serveur de langage Go
49+
50+Les quatre spécialistes ont un bloc `lsp` avec gopls pour les fichiers `.go` ; le coordinateur n'a pas d'autre outil qu'`ask_agent`, par choix. Chaque prompt dit à quoi il sert :
51+
52+| Agent | Utilise `lsp` pour |
53+|---|---|
54+| coder | `references` avant de changer une signature, `rename` pour renommer un identifiant partout, `diagnostics` après une modification |
55+| reviewer | `references` sur chaque fonction touchée par le diff, `diagnostics` sur chaque fichier modifié |
56+| reformulator | `references` et `symbols`, pour nommer les fichiers qu'une modification touche |
57+| tutor | `hover` et `definition` sur un symbole du code dont parle la question |
58+
59+Seul le coder peut faire `rename` ; les prompts des autres l'interdisent. gopls trouve tout seul le `go.mod` d'un projet placé dans un sous-dossier de `workspace/`, bien que les agents tournent depuis `workspace/` (mesuré avec `textstats`).
60+
61+## Prérequis
62+
63+- **llmman** sur son adresse par défaut, avec les trois modèles :
64+
65+ ```bash
66+ llmman pull hf.co/unsloth/Qwen3.5-4B-GGUF:Q4_K_M
67+ llmman pull hf.co/unsloth/gemma-4-12b-it-GGUF:Q4_K_M
68+ llmman pull hf.co/JetBrains/Mellum2-12B-A2.5B-Instruct-GGUF-Q4_K_M:Q4_K_M
69+ llmman serve
70+ ```
71+
72+- **Node.js** (les hooks), **Go** ≥ 1.24 et **git** (le reviewer lit `git diff`).
73+- **gopls** dans le `PATH` : `go install golang.org/x/tools/gopls@latest` (puis `$(go env GOPATH)/bin` dans le `PATH`). Sans lui, chaque spécialiste démarre avec un avertissement et sans outil `lsp`.
74+- **mm** compilé à la racine du dépôt : `go build -o mm .`
75+
76+## Organisation : agents en lecture seule, code en lecture-écriture
77+
78+![team/ en lecture seule, workspace/ en lecture-écriture](dev-team-sandbox.svg)
79+
80+```
81+dev.team/
82+├── sbxenv.yaml, start-zed.sh, stop-zed.sh la sandbox — jamais montés
83+├── README.*, dev-team-*.{drawio,svg,png} cette documentation
84+├── team/ les agents et leurs hooks → monté en LECTURE SEULE
85+└── workspace/ votre code (vide dans le dépôt), .mm/ → l'espace de travail en lecture-écriture
86+```
87+
88+Les fichiers des agents décident de ce que l'équipe a le droit de faire : leurs prompts, leurs outils, `yolo`, les hooks. Un membre de l'équipe qui a `bash` et `write_file` pourrait les réécrire, et le prochain démarrage exécuterait sa version. Ils sont donc dans `team/`, que la sandbox monte avec `readOnly: true`, à part du code que l'équipe modifie. Docker recommande aussi de garder le fichier d'environnement hors de tout dossier monté, pour qu'un agent ne puisse pas changer les commandes que lance votre machine : `sbxenv.yaml` et les scripts restent à la racine de `dev.team/`, qui n'est pas montée.
89+
90+mm trouve le coordinateur grâce à `AGENT_CONFIG`, défini dans `sbxenv.yaml` ; les autres agents et les hooks sont trouvés à côté (les chemins de `mmConfigs` et `$MM_CONFIG_DIR` sont relatifs à `team/agent.yaml`). Les sessions et les traces vont dans `workspace/.mm/`, le dossier de travail de la session.
91+
92+## Lancer
93+
94+### Dans une sandbox, avec Zed
95+
96+```bash
97+cd dev.team
98+sbx setup ssh # une fois
99+./start-zed.sh # sbx env create --env-arg DEV_TEAM_DIR=$PWD, puis Zed sur workspace/
100+./stop-zed.sh # supprime la sandbox ; team/ et workspace/ restent sur la machine
101+```
102+
103+`start-zed.sh` passe le dossier de `sbxenv.yaml` comme argument `DEV_TEAM_DIR` ; `sbxenv.yaml` en construit `AGENT_CONFIG=<DEV_TEAM_DIR>/team/agent.yaml`. Dans un terminal de la sandbox, depuis `workspace/` : `mm -tui` (il lit `AGENT_CONFIG`).
104+
105+Pour vérifier, une fois la sandbox lancée : `sbx version` (`sbx env` demande la 0.39.0 ou plus, ce qui place aussi après la CVE-2026-18171, qui permettait d'écrire dans un montage en lecture seule en 0.35.0–0.38.0) ; dans la sandbox, `echo $AGENT_CONFIG`, `ls "$(dirname "$AGENT_CONFIG")"` et `touch "$(dirname "$AGENT_CONFIG")/x"`, qui doit échouer.
106+
107+### Sur la machine
108+
109+```bash
110+cd dev.team/workspace
111+AGENT_CONFIG=$PWD/../team/agent.yaml ../../mm -tui
112+../../mm -config ../team/agent.yaml -tui # la même chose
113+../../mm -config ../team/agent.yaml -p "…" # une demande, puis sortie (le reformulator ne peut rien vous demander)
114+```
115+
116+La sandbox publie les ports 8080, 6060 et 5050 sur les mêmes ports de la machine (`ports` dans `sbxenv.yaml`, appliqué à la création) : un serveur lancé dans la sandbox doit écouter sur `0.0.0.0` pour être joignable sur `http://localhost:8080`.
117+
118+Sur la machine, rien ne met `team/` en lecture seule : cette protection est celle de la sandbox.
119+
120+Dans la fenêtre, l'en-tête nomme l'agent au travail : `mm [🧭 Coordinator] · qwen3.5-4b:Q4_K_M @ llmman`, puis `mm [🛠️ Coder] · … · asked by mm [🧭 Coordinator]` pendant un passage de main.
121+
122+`yolo: true` dans `agent.yaml` et dans chaque entrée de `mmConfigs` : les passages de main, les commandes et les écritures de fichiers se font sans demander. Pour approuver chaque passage de main et lire le prompt écrit par le coordinateur : `AGENT_YOLO=false` devant `mm`.
123+
124+Chaque spécialiste fonctionne aussi seul, par exemple `mm -config ../team/agent.tutor.yaml` depuis `workspace/` (seuls, ils demandent avant chaque commande et chaque modification de fichier, sauf le tutor).
125+
126+## Exemples à essayer
127+
128+Mettez un projet dans `workspace/`, en dépôt git avec son dernier état commité, pour que le `git diff` du reviewer ne montre que la modification de l'équipe. Les exemples l'appellent `my-app`.
129+
130+| Route | Demande | Ce qui doit se passer |
131+|---|---|---|
132+| A | `Corrige le bug de my-app : <ce qui ne va pas, avec une entrée et la sortie attendue>.` | brief → correction + un cas de test → reviewer `VERDICT: PASS` |
133+| A | `Ajoute à my-app une fonction qui renvoie les mots les plus fréquents d'un texte.` | le reformulator vous demande combien, la casse, la ponctuation, le type de retour (avec `-p` : il choisit et liste ses ASSUMPTIONS) |
134+| A | `Renomme <AncienNom> en <NouveauNom> partout dans my-app.` | brief → `lsp rename` + tests → revue |
135+| B | `Quelle est la différence entre strings.Split et strings.Fields ?` | un mini tutoriel en français |
136+| B | `Explique les tests en table en Go, avec my-app/<fichier>_test.go comme exemple.` | le tutor lit le fichier et bâtit la leçon sur son vrai code |
137+| B | `Explique-moi les goroutines avec un petit exemple, et sauve-le dans goroutines.md.` | un tutoriel en français, enregistré avec `save_answer` |
138+| C | `Fais la revue du code de my-app et dis-moi ce qui ne va pas.` | des constats avec fichier:ligne, les tests, le verdict |
139+
140+## Résultats mesurés
141+
142+llmman sur la machine de 32 Go de l'utilisateur, le 2026-10-25, sur le paquet d'entraînement `textstats` (`playground/` dans les tableaux), avant que `workspace/` soit vidé.
143+
144+### Chaque agent seul
145+
146+| Agent | Tâche | Résultat | Temps |
147+|---|---|---|---|
148+| coder | le bug de `WordCount` | corrigé avec `strings.Fields`, 4 cas de test ajoutés, tests au vert | 351 s |
149+| reviewer | revue de cette correction | `git diff`, tests, son propre `review_test.go`, `VERDICT: PASS` | 151 s |
150+| tutor | les tests en table sur `playground/textstats_test.go` | 3 exécutions sur 3 lisent le fichier et enseignent sur sa vraie table `map[string]int` | 12–19 s |
151+| reformulator | « une fonction qui donne les mots les plus fréquents » | lit le code, appelle `ask_user` ; sans personne pour répondre (`-p`), liste ses choix sous ASSUMPTIONS | 62 s |
152+
153+Ce que ces exécutions ont changé dans les fichiers :
154+
155+- **coder** : une fois, après un `edit_file` raté, Mellum2 a écrit un appel d'outil jamais refermé, qui a couru jusqu'à `max_tokens` (8192) : l'essentiel des 351 s. `max_tokens` vaut maintenant 4096, ce qui divise par deux le coût d'un tel appel.
156+- **tutor** : la première version répondait en 24 s mais montrait un fichier de test « typique » qu'elle avait inventé. Le prompt fait maintenant de `read_file` sa première action quand un fichier est nommé.
157+- **reformulator** : la première version ne listait pas ses choix, et mettait « top N » hors périmètre pour « les mots les plus fréquents ». Le prompt nomme maintenant ce que seul l'utilisateur décide, et le brief a une section ASSUMPTIONS.
158+
159+### L'équipe a-t-elle besoin d'un modèle de décision ?
160+
161+La question du ticket. Un décideur (voir `demos/decision-*`) serait utile si le coordinateur se trompait de route. Mesuré : le coordinateur (Qwen3.5 4B, sans raisonnement) sur 12 demandes — 5 modifications de code, 5 questions (dont 2 en français), 2 revues — **12 sur 12 vers le bon agent**. Un décideur ajouterait un modèle et un appel pour rien. Là où il pourrait servir plus tard, à mesurer d'abord : repérer une demande trop vague pour être codée (une question noul avant le reformulator), ou lire le verdict du reviewer.
162+
163+### L'équipe entière
164+
165+Par le coordinateur, `-p`, `AGENT_YOLO=true` ; les modèles chargés dans llmman relevés toutes les 2 secondes.
166+
167+| Exécution | Route | Résultat | Temps |
168+|---|---|---|---|
169+| correction du bug, 1re | A | `VERDICT: PASS`, mais le coder a bouclé sur `plan update` et a été appelé deux fois ; le compte rendu disait « aucune modification » | 685 s |
170+| correction du bug, 2e (après les corrections ci-dessous) | A | un appel par agent, `VERDICT: PASS`, code formaté, compte rendu exact | 398 s |
171+| « explique-moi strings.Split et strings.Fields » | B | le tutor répond en français, juste | 49 s |
172+| « les mots les plus fréquents », 1re | A | **échec** : le reviewer a testé un comportement absent du brief et atteint ses 20 tours ; le coordinateur l'a redemandé, puis a appelé le coder 4 fois ; tests rouges à la fin | 52 min |
173+| « les mots les plus fréquents », 2e (après les corrections ci-dessous) | A | **échec** : un bon brief (top 3), puis le coder a eu la même coupure et le coordinateur l'a redemandé ; le reviewer est resté à `VERDICT: FAIL` (2 de ses 7 cas) ; arrêté par la limite de 30 minutes de la mesure | 30 min |
174+
175+**Mémoire : pendant les passages de main de l'équipe, jamais deux modèles chargés en même temps** : 4B → Gemma → 4B → Mellum2 → 4B → Gemma → 4B. Le seul chevauchement vu (les 3 premières minutes de la dernière exécution) était un Mellum2 laissé chargé par une mesure lancée avant l'équipe : les hooks déchargent les modèles des passages de main, pas un modèle chargé par autre chose.
176+
177+**En bref : les corrections de bugs, les leçons et les revues fonctionnent ; les nouvelles fonctionnalités, pas encore.** Le maillon faible est le coder : sur une fonctionnalité, Mellum2 écrit des appels d'outil qui se répètent jusqu'à `max_tokens`, et le coordinateur 4B le redemande alors malgré son prompt. Essayez `agent.coder.ornith.yaml` pour les fonctionnalités (voir plus bas).
178+
179+Ce que ces exécutions ont changé :
180+
181+- **coder** : plus d'outil `plan` (Mellum2 appelait `plan update` en boucle) ; « ajoute des cas de test, garde les existants » ; le hook `gofmt`.
182+- **reviewer** : ses tests couvrent seulement l'ACCEPTANCE et les REQUIREMENTS du brief ; un de ses cas qui échoue est un constat (pas de réécriture jusqu'à ce qu'il passe) ; `gofmt -l` et des cas de test supprimés sont des constats ; `maxTurns` 20 → 12 (puis 16, voir plus bas).
183+- **coordinateur** : `maxTurns` 8 → 6 ; « ne jamais redemander à un agent avec un nouveau prompt » ; le compte rendu prend les fichiers dans les réponses du coder et du reviewer.
184+
185+### Avec le serveur de langage Go
186+
187+Mesuré après l'ajout de `lsp` (gopls v0.24.0) :
188+
189+| Agent | Tâche | Résultat | Temps |
190+|---|---|---|---|
191+| coder, 1er prompt | « Rename LineCount to CountLines everywhere in playground » | a appelé `rename` sans `new_name`, puis a recopié l'indication mot pour mot : la fonction est devenue `new_name`, et le compte rendu disait « renommée en CountLines » | 18 s |
192+| coder, avec un exemple rempli dans le prompt | la même | 3 exécutions sur 3 : `lsp rename` → `CountLines`, tests au vert | 5–10 s |
193+| reviewer | revue de ce renommage | exécution 1 : un appel d'outil qui se répétait, arrêté par mm ; exécution 2 : `references` + 2 `diagnostics`, un vrai constat (le message d'erreur du test dit encore `LineCount` : `rename` change les identifiants, pas les chaînes), `VERDICT: FAIL` | 200 s, 167 s |
194+
195+Le prompt du reviewer a aussi dû préciser de lancer `git diff` **dans** `playground/` : depuis le dossier au-dessus, git lit le dépôt de mm, où `playground/` est ignoré, et le diff est vide. Son `maxTurns` vaut maintenant 16 : 12 coupait une revue normale (13 appels) avant son verdict.
196+
197+### Quel coder ?
198+
199+Le ticket permet de remettre en question ses choix de modèles : le coder a donc été mesuré seul sur le brief du reformulator pour « les mots les plus fréquents », contre un test caché fait des cas d'acceptation du brief (copié après l'exécution), `go vet` et `gofmt` :
200+
201+| Coder | Exécutions justes | Temps par exécution | Formaté |
202+|---|---|---|---|
203+| Mellum2 12B-A2.5B Instruct (`agent.coder.yaml`) | 2 sur 3 | 129 s, 132 s, 639 s (l'échec) | non (avant le hook) |
204+| Ornith 1.5 9B, raisonnement activé (`agent.coder.ornith.yaml`) | 2 sur 2 | 783 s, 810 s | oui |
205+
206+Mellum2 reste le coder par défaut, comme le ticket l'a choisi : environ 6 fois plus rapide, et juste 2 fois sur 3. Son mode d'échec est celui vu plus haut : un appel d'outil qui se répète jusqu'à `max_tokens`. Ornith est le choix le plus régulier quand le temps compte moins : faites pointer `mmConfigs.coder.path` sur `agent.coder.ornith.yaml` (6,7 Go, donc rien ne change au pic mémoire).
207+
208+## Limites
209+
210+- **Le bash du reformulator n'est en lecture seule que par son prompt.** Il n'a pas d'outils de fichiers, mais `bash` pourrait écrire ; avec `yolo: true`, rien ne demande. Pour une garantie, ajoutez un hook `preToolCall` qui refuse les commandes qui écrivent (voir `demos/hooks`).
211+- **L'équipe peut écrire partout dans `workspace/`, pas seulement dans `playground/`.** Une fois, avant la séparation, un membre a écrit un `main.go` à côté de `agent.yaml`, ce qui cassait le `go vet ./...` de mm. Dans la sandbox, il ne peut plus toucher `team/` ; regardez `git status` après une exécution.
212+- **`-p` ne peut rien vous demander** : le reformulator choisit alors, et le dit.
213+- **Chaque rechargement coûte du temps** : décharger à chaque passage de main échange des secondes de chargement contre de la mémoire. Sur une machine avec plus de mémoire, retirez le hook `preAsk` pour garder le 4B chargé.
214+- Quand `ask_agent` est refusé (`yolo: false` et vous dites non), le coordinateur 4B a tendance à essayer un autre agent plutôt qu'à s'arrêter ; après un passage de main raté, il redemande à l'agent bien que son prompt le lui interdise (`maxTurns: 6` le borne).
215+- Un modèle déjà chargé avant que l'équipe démarre (par un autre agent, ou `llmman run`) reste chargé à côté de ceux de l'équipe : déchargez-le d'abord (`curl -s http://127.0.0.1:17434/api/generate -d '{"model":"…","keep_alive":0}'`).
216+
217+## Fichiers
218+
219+```
220+dev.team/
221+├── sbxenv.yaml la sandbox : workspace/ en lecture-écriture, team/ en lecture seule, AGENT_CONFIG
222+├── start-zed.sh, stop-zed.sh créer la sandbox et ouvrir workspace/ dans Zed ; la supprimer
223+├── team/ en lecture seule dans la sandbox
224+│ ├── agent.yaml coordinateur (🧭)
225+│ ├── agent.reformulator.yaml 📝
226+│ ├── agent.coder.yaml 🛠️
227+│ ├── agent.coder.ornith.yaml 🛠️ le coder sur Ornith 1.5 9B (plus régulier, plus lent)
228+│ ├── agent.reviewer.yaml 🔎
229+│ ├── agent.tutor.yaml 📘 (la configuration « prof » du ticket 152, adaptée)
230+│ └── hooks/ unload.js (un seul modèle en mémoire), gofmt.js (le formatage du coder)
231+├── workspace/ en lecture-écriture : vide dans le dépôt (.gitkeep) — votre projet va ici ;
232+│ .mm/ (sessions, traces) est créé à la première exécution
233+├── dev-team-flow.* diagramme : les trois routes (.drawio .svg .png)
234+├── dev-team-memory.* diagramme : les déchargements (.drawio .svg .png)
235+└── dev-team-sandbox.* diagramme : les montages de la sandbox (.drawio .svg .png)
236+```
237+
238+Les diagrammes sont générés par `.claude/skills/drawio-fancy-diagram/generator/examples/dev_team.py dev.team`.
added dev.team/dev-team-flow.drawio +37 -0
new file mode 100644
@@ -0,0 +1,37 @@
1+<mxfile host="app.diagrams.net">
2+ <diagram id="dev-team-flow" name="dev.team — a coordinator and four specialists">
3+ <mxGraphModel dx="1200" dy="720" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1200" pageHeight="720" background="#F5F7FC" math="0" shadow="0">
4+ <root>
5+ <mxCell id="0"/>
6+ <mxCell id="1" parent="0"/>
7+ <mxCell id="title" value="&lt;b style=&quot;font-size:25px;color:#1A237E&quot;&gt;dev.team — a coordinator and four specialists&lt;/b&gt;&lt;br&gt;&lt;i style=&quot;font-size:14px;color:#546E7A&quot;&gt;the coordinator picks a route and calls each agent with ask_agent, one at a time&lt;/i&gt;" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="150.0" y="12" width="900" height="56" as="geometry"/></mxCell>
8+ <mxCell id="c9" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="880" y="185" as="sourcePoint"/><mxPoint x="910" y="185" as="targetPoint"/></mxGeometry></mxCell>
9+ <mxCell id="c10" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="1025" y="240" as="sourcePoint"/><mxPoint x="930" y="290" as="targetPoint"/><Array as="points"><mxPoint x="1025" y="265"/><mxPoint x="930" y="265"/></Array></mxGeometry></mxCell>
10+ <mxCell id="c17" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=block;startFill=1;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="220" y="210" as="sourcePoint"/><mxPoint x="300" y="210" as="targetPoint"/></mxGeometry></mxCell>
11+ <mxCell id="c19" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="550" y="180" as="sourcePoint"/><mxPoint x="620" y="180" as="targetPoint"/></mxGeometry></mxCell>
12+ <mxCell id="c21" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="425" y="280" as="sourcePoint"/><mxPoint x="650" y="605" as="targetPoint"/><Array as="points"><mxPoint x="425" y="605"/></Array></mxGeometry></mxCell>
13+ <mxCell id="c23" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="550" y="250" as="sourcePoint"/><mxPoint x="1055" y="660" as="targetPoint"/><Array as="points"><mxPoint x="590" y="250"/><mxPoint x="590" y="690"/><mxPoint x="1055" y="690"/></Array></mxGeometry></mxCell>
14+ <mxCell id="c25" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;dashed=1;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="620" y="470" as="sourcePoint"/><mxPoint x="260" y="500" as="targetPoint"/></mxGeometry></mxCell>
15+ <mxCell id="c1" value="🧑&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;You&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;a request&lt;br&gt;Zed or mm -tui · workspace/&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E3F2FD;gradientColor=#BBDEFB;strokeColor=#1565C0;strokeWidth=2;shadow=1;fontSize=20;fontColor=#0D47A1;" vertex="1" parent="1"><mxGeometry x="40" y="150" width="180" height="120" as="geometry"/></mxCell>
16+ <mxCell id="c2" value="🔀&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;Coordinator&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Qwen3.5-4B · agent.yaml&lt;br&gt;only tool: ask_agent&lt;br&gt;picks route A, B or C&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#F3E5F5;gradientColor=#E1BEE7;strokeColor=#6A1B9A;strokeWidth=3;shadow=1;fontSize=20;fontColor=#4A148C;" vertex="1" parent="1"><mxGeometry x="300" y="140" width="250" height="140" as="geometry"/></mxCell>
17+ <mxCell id="c3" value="&lt;b&gt;★ routes&lt;/b&gt;" style="rounded=1;arcSize=50;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=11;" vertex="1" parent="1"><mxGeometry x="488.4" y="128" width="71.6" height="24" as="geometry"/></mxCell>
18+ <mxCell id="c4" value="&lt;i&gt;&lt;b&gt;route A — code to write or change (in order)&lt;/b&gt;&lt;/i&gt;" style="rounded=1;arcSize=3;html=1;dashed=1;dashPattern=7 5;fillColor=#FAFAFA;strokeColor=#90A4AE;strokeWidth=2;verticalAlign=top;fontColor=#607D8B;fontSize=13;" vertex="1" parent="1"><mxGeometry x="620" y="90" width="550" height="400" as="geometry"/></mxCell>
19+ <mxCell id="c5" value="💬&lt;br&gt;&lt;b style=&quot;font-size:15px&quot;&gt;1 · Reformulator&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Gemma 4 12B&lt;br&gt;task brief · ask_user&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="650" y="130" width="230" height="110" as="geometry"/></mxCell>
20+ <mxCell id="c6" value="🛠️&lt;br&gt;&lt;b style=&quot;font-size:15px&quot;&gt;2 · Coder&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Mellum2 12B&lt;br&gt;bash · files · tests&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="910" y="130" width="230" height="110" as="geometry"/></mxCell>
21+ <mxCell id="c7" value="🔎&lt;br&gt;&lt;b style=&quot;font-size:15px&quot;&gt;3 · Reviewer&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Gemma 4 12B&lt;br&gt;tests · VERDICT&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="780" y="290" width="230" height="110" as="geometry"/></mxCell>
22+ <mxCell id="c8" value="VERDICT: FAIL → the coder fixes ONCE → the reviewer again" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;fontSize=12;fontColor=#455A64;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="682.96" y="438" width="424.08" height="18" as="geometry"/></mxCell>
23+ <mxCell id="c11" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;brief + report&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="1005.0" y="253.0" width="110" height="24" as="geometry"/></mxCell>
24+ <mxCell id="c12" value="📘&lt;br&gt;&lt;b style=&quot;font-size:15px&quot;&gt;Tutor&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Mellum2 12B&lt;br&gt;mini tutorials · save_answer&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="650" y="550" width="230" height="110" as="geometry"/></mxCell>
25+ <mxCell id="c13" value="route B — a question, a lesson" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;fontSize=12.5;fontColor=#607D8B;fontStyle=3;" vertex="1" parent="1"><mxGeometry x="648.75" y="517.5" width="232.5" height="18.5" as="geometry"/></mxCell>
26+ <mxCell id="c14" value="🔎&lt;br&gt;&lt;b style=&quot;font-size:15px&quot;&gt;Reviewer&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;review existing code&lt;br&gt;no change&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="940" y="550" width="230" height="110" as="geometry"/></mxCell>
27+ <mxCell id="c15" value="route C — only a review" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;fontSize=12.5;fontColor=#607D8B;fontStyle=3;" vertex="1" parent="1"><mxGeometry x="965.875" y="517.5" width="178.25" height="18.5" as="geometry"/></mxCell>
28+ <mxCell id="c16" value="💻&lt;br&gt;&lt;b style=&quot;font-size:15px&quot;&gt;workspace/&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;your project&lt;br&gt;a git repo: git diff&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=20;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="40" y="440" width="220" height="120" as="geometry"/></mxCell>
29+ <mxCell id="c18" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;request&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:10.5px;color:#78909C&quot;&gt;← report&lt;/span&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="222.0" y="218.0" width="76" height="40" as="geometry"/></mxCell>
30+ <mxCell id="c20" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;A&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="570.0" y="168.0" width="30" height="24" as="geometry"/></mxCell>
31+ <mxCell id="c22" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;B&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="410.0" y="428.0" width="30" height="24" as="geometry"/></mxCell>
32+ <mxCell id="c24" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;C&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="815.0" y="678.0" width="30" height="24" as="geometry"/></mxCell>
33+ <mxCell id="c26" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;reads · changes · tests&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="255.0" y="482.0" width="150" height="24" as="geometry"/></mxCell>
34+ </root>
35+ </mxGraphModel>
36+ </diagram>
37+</mxfile>
new file mode 100644
@@ -0,0 +1,37 @@
1+<mxfile host="app.diagrams.net">
2+ <diagram id="dev-team-flow" name="dev.team — a coordinator and four specialists">
3+ <mxGraphModel dx="1200" dy="720" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1200" pageHeight="720" background="#F5F7FC" math="0" shadow="0">
4+ <root>
5+ <mxCell id="0"/>
6+ <mxCell id="1" parent="0"/>
7+ <mxCell id="title" value="&lt;b style=&quot;font-size:25px;color:#1A237E&quot;&gt;dev.team — a coordinator and four specialists&lt;/b&gt;&lt;br&gt;&lt;i style=&quot;font-size:14px;color:#546E7A&quot;&gt;the coordinator picks a route and calls each agent with ask_agent, one at a time&lt;/i&gt;" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="150.0" y="12" width="900" height="56" as="geometry"/></mxCell>
8+ <mxCell id="c9" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="880" y="185" as="sourcePoint"/><mxPoint x="910" y="185" as="targetPoint"/></mxGeometry></mxCell>
9+ <mxCell id="c10" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="1025" y="240" as="sourcePoint"/><mxPoint x="930" y="290" as="targetPoint"/><Array as="points"><mxPoint x="1025" y="265"/><mxPoint x="930" y="265"/></Array></mxGeometry></mxCell>
10+ <mxCell id="c17" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=block;startFill=1;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="220" y="210" as="sourcePoint"/><mxPoint x="300" y="210" as="targetPoint"/></mxGeometry></mxCell>
11+ <mxCell id="c19" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="550" y="180" as="sourcePoint"/><mxPoint x="620" y="180" as="targetPoint"/></mxGeometry></mxCell>
12+ <mxCell id="c21" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="425" y="280" as="sourcePoint"/><mxPoint x="650" y="605" as="targetPoint"/><Array as="points"><mxPoint x="425" y="605"/></Array></mxGeometry></mxCell>
13+ <mxCell id="c23" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="550" y="250" as="sourcePoint"/><mxPoint x="1055" y="660" as="targetPoint"/><Array as="points"><mxPoint x="590" y="250"/><mxPoint x="590" y="690"/><mxPoint x="1055" y="690"/></Array></mxGeometry></mxCell>
14+ <mxCell id="c25" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;dashed=1;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="620" y="470" as="sourcePoint"/><mxPoint x="260" y="500" as="targetPoint"/></mxGeometry></mxCell>
15+ <mxCell id="c1" value="🧑&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;You&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;a request&lt;br&gt;Zed or mm -tui · workspace/&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E3F2FD;gradientColor=#BBDEFB;strokeColor=#1565C0;strokeWidth=2;shadow=1;fontSize=20;fontColor=#0D47A1;" vertex="1" parent="1"><mxGeometry x="40" y="150" width="180" height="120" as="geometry"/></mxCell>
16+ <mxCell id="c2" value="🔀&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;Coordinator&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Qwen3.5-4B · agent.yaml&lt;br&gt;only tool: ask_agent&lt;br&gt;picks route A, B or C&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#F3E5F5;gradientColor=#E1BEE7;strokeColor=#6A1B9A;strokeWidth=3;shadow=1;fontSize=20;fontColor=#4A148C;" vertex="1" parent="1"><mxGeometry x="300" y="140" width="250" height="140" as="geometry"/></mxCell>
17+ <mxCell id="c3" value="&lt;b&gt;★ routes&lt;/b&gt;" style="rounded=1;arcSize=50;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=11;" vertex="1" parent="1"><mxGeometry x="488.4" y="128" width="71.6" height="24" as="geometry"/></mxCell>
18+ <mxCell id="c4" value="&lt;i&gt;&lt;b&gt;route A — code to write or change (in order)&lt;/b&gt;&lt;/i&gt;" style="rounded=1;arcSize=3;html=1;dashed=1;dashPattern=7 5;fillColor=#FAFAFA;strokeColor=#90A4AE;strokeWidth=2;verticalAlign=top;fontColor=#607D8B;fontSize=13;" vertex="1" parent="1"><mxGeometry x="620" y="90" width="550" height="400" as="geometry"/></mxCell>
19+ <mxCell id="c5" value="💬&lt;br&gt;&lt;b style=&quot;font-size:15px&quot;&gt;1 · Reformulator&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Gemma 4 12B&lt;br&gt;task brief · ask_user&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="650" y="130" width="230" height="110" as="geometry"/></mxCell>
20+ <mxCell id="c6" value="🛠️&lt;br&gt;&lt;b style=&quot;font-size:15px&quot;&gt;2 · Coder&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Mellum2 12B&lt;br&gt;bash · files · tests&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="910" y="130" width="230" height="110" as="geometry"/></mxCell>
21+ <mxCell id="c7" value="🔎&lt;br&gt;&lt;b style=&quot;font-size:15px&quot;&gt;3 · Reviewer&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Gemma 4 12B&lt;br&gt;tests · VERDICT&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="780" y="290" width="230" height="110" as="geometry"/></mxCell>
22+ <mxCell id="c8" value="VERDICT: FAIL → the coder fixes ONCE → the reviewer again" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;fontSize=12;fontColor=#455A64;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="682.96" y="438" width="424.08" height="18" as="geometry"/></mxCell>
23+ <mxCell id="c11" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;brief + report&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="1005.0" y="253.0" width="110" height="24" as="geometry"/></mxCell>
24+ <mxCell id="c12" value="📘&lt;br&gt;&lt;b style=&quot;font-size:15px&quot;&gt;Tutor&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Mellum2 12B&lt;br&gt;mini tutorials · save_answer&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="650" y="550" width="230" height="110" as="geometry"/></mxCell>
25+ <mxCell id="c13" value="route B — a question, a lesson" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;fontSize=12.5;fontColor=#607D8B;fontStyle=3;" vertex="1" parent="1"><mxGeometry x="648.75" y="517.5" width="232.5" height="18.5" as="geometry"/></mxCell>
26+ <mxCell id="c14" value="🔎&lt;br&gt;&lt;b style=&quot;font-size:15px&quot;&gt;Reviewer&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;review existing code&lt;br&gt;no change&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="940" y="550" width="230" height="110" as="geometry"/></mxCell>
27+ <mxCell id="c15" value="route C — only a review" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;fontSize=12.5;fontColor=#607D8B;fontStyle=3;" vertex="1" parent="1"><mxGeometry x="965.875" y="517.5" width="178.25" height="18.5" as="geometry"/></mxCell>
28+ <mxCell id="c16" value="💻&lt;br&gt;&lt;b style=&quot;font-size:15px&quot;&gt;workspace/&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;your project&lt;br&gt;a git repo: git diff&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=20;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="40" y="440" width="220" height="120" as="geometry"/></mxCell>
29+ <mxCell id="c18" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;request&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:10.5px;color:#78909C&quot;&gt;← report&lt;/span&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="222.0" y="218.0" width="76" height="40" as="geometry"/></mxCell>
30+ <mxCell id="c20" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;A&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="570.0" y="168.0" width="30" height="24" as="geometry"/></mxCell>
31+ <mxCell id="c22" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;B&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="410.0" y="428.0" width="30" height="24" as="geometry"/></mxCell>
32+ <mxCell id="c24" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;C&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="815.0" y="678.0" width="30" height="24" as="geometry"/></mxCell>
33+ <mxCell id="c26" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;reads · changes · tests&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="255.0" y="482.0" width="150" height="24" as="geometry"/></mxCell>
34+ </root>
35+ </mxGraphModel>
36+ </diagram>
37+</mxfile>
added dev.team/dev-team-flow.png +0 -0
new file mode 100644
Binary files /dev/null and b/dev.team/dev-team-flow.png differ
new file mode 100644
Binary files /dev/null and b/dev.team/dev-team-flow.png differBinary files /dev/null and b/dev.team/dev-team-flow.png differ
added dev.team/dev-team-flow.svg +83 -0
new file mode 100644
@@ -0,0 +1,83 @@
1+<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="720" viewBox="0 0 1200 720" font-family="DejaVu Sans, Verdana, sans-serif">
2+ <defs>
3+ <linearGradient id="gBg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#FFFFFF"/><stop offset="100%" stop-color="#EEF2FA"/></linearGradient>
4+ <linearGradient id="gBlue" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#E3F2FD"/><stop offset="100%" stop-color="#BBDEFB"/></linearGradient>
5+ <linearGradient id="gBlue2" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#E1F5FE"/><stop offset="100%" stop-color="#B3E5FC"/></linearGradient>
6+ <linearGradient id="gPurple" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#F3E5F5"/><stop offset="100%" stop-color="#E1BEE7"/></linearGradient>
7+ <linearGradient id="gYellow" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#FFFDE7"/><stop offset="100%" stop-color="#FFF9C4"/></linearGradient>
8+ <linearGradient id="gGrey" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#ECEFF1"/><stop offset="100%" stop-color="#CFD8DC"/></linearGradient>
9+ <filter id="shadow" x="-30%" y="-30%" width="160%" height="160%"><feDropShadow dx="2" dy="3" stdDeviation="4" flood-color="#000000" flood-opacity="0.25"/></filter>
10+ <marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#37474F"/></marker>
11+ </defs>
12+ <rect x="0" y="0" width="1200" height="720" fill="url(#gBg)"/>
13+ <text x="600.0" y="38" text-anchor="middle" font-size="25" font-weight="700" fill="#1A237E">dev.team — a coordinator and four specialists</text>
14+ <text x="600.0" y="61" text-anchor="middle" font-size="14" font-style="italic" fill="#546E7A">the coordinator picks a route and calls each agent with ask_agent, one at a time</text>
15+ <path d="M880,185 L910,185" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
16+ <path d="M1025,240 L1025,265 L930,265 L930,290" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
17+ <path d="M220,210 L300,210" fill="none" stroke="#37474F" stroke-width="2.2" marker-start="url(#arrow)" marker-end="url(#arrow)"/>
18+ <path d="M550,180 L620,180" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
19+ <path d="M425,280 L425,605 L650,605" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
20+ <path d="M550,250 L590,250 L590,690 L1055,690 L1055,660" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
21+ <path d="M620,470 L260,500" fill="none" stroke="#37474F" stroke-width="2.2" stroke-dasharray="6,4" marker-end="url(#arrow)"/>
22+ <g filter="url(#shadow)"><rect x="40" y="150" width="180" height="120" rx="16" fill="url(#gBlue)" stroke="#1565C0" stroke-width="2"/></g>
23+ <text x="130.0" y="190.0" text-anchor="middle" font-size="22">🧑</text>
24+ <text x="130.0" y="213.0" text-anchor="middle" font-size="16" font-weight="700" fill="#0D47A1">You</text>
25+ <text x="130.0" y="231.0" text-anchor="middle" font-size="11.5" fill="#0D47A1">a request</text>
26+ <text x="130.0" y="248.0" text-anchor="middle" font-size="11.5" fill="#0D47A1">Zed or mm -tui · workspace/</text>
27+ <g filter="url(#shadow)"><rect x="300" y="140" width="250" height="140" rx="16" fill="url(#gPurple)" stroke="#6A1B9A" stroke-width="3"/></g>
28+ <text x="425.0" y="181.5" text-anchor="middle" font-size="22">🔀</text>
29+ <text x="425.0" y="204.5" text-anchor="middle" font-size="16" font-weight="700" fill="#4A148C">Coordinator</text>
30+ <text x="425.0" y="222.5" text-anchor="middle" font-size="11.5" fill="#4A148C">Qwen3.5-4B · agent.yaml</text>
31+ <text x="425.0" y="239.5" text-anchor="middle" font-size="11.5" fill="#4A148C">only tool: ask_agent</text>
32+ <text x="425.0" y="256.5" text-anchor="middle" font-size="11.5" fill="#4A148C">picks route A, B or C</text>
33+ <rect x="488.4" y="128" width="71.6" height="24" rx="12" fill="#6A1B9A"/>
34+ <text x="524.1999999999999" y="144" text-anchor="middle" font-size="11.5" font-weight="700" fill="#FFFFFF">★ routes</text>
35+ <rect x="620" y="90" width="550" height="400" rx="10" fill="#FAFAFA" fill-opacity="0.7" stroke="#90A4AE" stroke-width="2" stroke-dasharray="7,5"/>
36+ <text x="895.0" y="112" text-anchor="middle" font-size="13" font-style="italic" font-weight="700" fill="#607D8B">route A — code to write or change (in order)</text>
37+ <g filter="url(#shadow)"><rect x="650" y="130" width="230" height="110" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>
38+ <text x="765.0" y="165.0" text-anchor="middle" font-size="22">💬</text>
39+ <text x="765.0" y="188.0" text-anchor="middle" font-size="15" font-weight="700" fill="#01579B">1 · Reformulator</text>
40+ <text x="765.0" y="206.0" text-anchor="middle" font-size="11.5" fill="#01579B">Gemma 4 12B</text>
41+ <text x="765.0" y="223.0" text-anchor="middle" font-size="11.5" fill="#01579B">task brief · ask_user</text>
42+ <g filter="url(#shadow)"><rect x="910" y="130" width="230" height="110" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>
43+ <text x="1025.0" y="165.0" text-anchor="middle" font-size="22">🛠️</text>
44+ <text x="1025.0" y="188.0" text-anchor="middle" font-size="15" font-weight="700" fill="#01579B">2 · Coder</text>
45+ <text x="1025.0" y="206.0" text-anchor="middle" font-size="11.5" fill="#01579B">Mellum2 12B</text>
46+ <text x="1025.0" y="223.0" text-anchor="middle" font-size="11.5" fill="#01579B">bash · files · tests</text>
47+ <g filter="url(#shadow)"><rect x="780" y="290" width="230" height="110" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>
48+ <text x="895.0" y="325.0" text-anchor="middle" font-size="22">🔎</text>
49+ <text x="895.0" y="348.0" text-anchor="middle" font-size="15" font-weight="700" fill="#01579B">3 · Reviewer</text>
50+ <text x="895.0" y="366.0" text-anchor="middle" font-size="11.5" fill="#01579B">Gemma 4 12B</text>
51+ <text x="895.0" y="383.0" text-anchor="middle" font-size="11.5" fill="#01579B">tests · VERDICT</text>
52+ <text x="895" y="450" text-anchor="middle" font-size="12" fill="#455A64" font-style="italic">VERDICT: FAIL → the coder fixes ONCE → the reviewer again</text>
53+ <rect x="1005.0" y="253.0" width="110" height="24" rx="5" fill="#FFFFFF"/>
54+ <text x="1060" y="269.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">brief + report</text>
55+ <g filter="url(#shadow)"><rect x="650" y="550" width="230" height="110" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>
56+ <text x="765.0" y="585.0" text-anchor="middle" font-size="22">📘</text>
57+ <text x="765.0" y="608.0" text-anchor="middle" font-size="15" font-weight="700" fill="#01579B">Tutor</text>
58+ <text x="765.0" y="626.0" text-anchor="middle" font-size="11.5" fill="#01579B">Mellum2 12B</text>
59+ <text x="765.0" y="643.0" text-anchor="middle" font-size="11.5" fill="#01579B">mini tutorials · save_answer</text>
60+ <text x="765" y="530" text-anchor="middle" font-size="12.5" fill="#607D8B" font-style="italic" font-weight="700">route B — a question, a lesson</text>
61+ <g filter="url(#shadow)"><rect x="940" y="550" width="230" height="110" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>
62+ <text x="1055.0" y="585.0" text-anchor="middle" font-size="22">🔎</text>
63+ <text x="1055.0" y="608.0" text-anchor="middle" font-size="15" font-weight="700" fill="#01579B">Reviewer</text>
64+ <text x="1055.0" y="626.0" text-anchor="middle" font-size="11.5" fill="#01579B">review existing code</text>
65+ <text x="1055.0" y="643.0" text-anchor="middle" font-size="11.5" fill="#01579B">no change</text>
66+ <text x="1055" y="530" text-anchor="middle" font-size="12.5" fill="#607D8B" font-style="italic" font-weight="700">route C — only a review</text>
67+ <g filter="url(#shadow)"><rect x="40" y="440" width="220" height="120" rx="16" fill="url(#gGrey)" stroke="#455A64" stroke-width="2"/></g>
68+ <text x="150.0" y="480.0" text-anchor="middle" font-size="22">💻</text>
69+ <text x="150.0" y="503.0" text-anchor="middle" font-size="15" font-weight="700" fill="#263238">workspace/</text>
70+ <text x="150.0" y="521.0" text-anchor="middle" font-size="11.5" fill="#263238">your project</text>
71+ <text x="150.0" y="538.0" text-anchor="middle" font-size="11.5" fill="#263238">a git repo: git diff</text>
72+ <rect x="222.0" y="218.0" width="76" height="40" rx="5" fill="#FFFFFF"/>
73+ <text x="260" y="235" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">request</text>
74+ <text x="260" y="251" text-anchor="middle" font-size="10.5" fill="#78909C">← report</text>
75+ <rect x="570.0" y="168.0" width="30" height="24" rx="5" fill="#FFFFFF"/>
76+ <text x="585" y="184.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">A</text>
77+ <rect x="410.0" y="428.0" width="30" height="24" rx="5" fill="#FFFFFF"/>
78+ <text x="425" y="444.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">B</text>
79+ <rect x="815.0" y="678.0" width="30" height="24" rx="5" fill="#FFFFFF"/>
80+ <text x="830" y="694.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">C</text>
81+ <rect x="255.0" y="482.0" width="150" height="24" rx="5" fill="#FFFFFF"/>
82+ <text x="330" y="498.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">reads · changes · tests</text>
83+</svg>
new file mode 100644
@@ -0,0 +1,83 @@
1+<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="720" viewBox="0 0 1200 720" font-family="DejaVu Sans, Verdana, sans-serif">
2+ <defs>
3+ <linearGradient id="gBg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#FFFFFF"/><stop offset="100%" stop-color="#EEF2FA"/></linearGradient>
4+ <linearGradient id="gBlue" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#E3F2FD"/><stop offset="100%" stop-color="#BBDEFB"/></linearGradient>
5+ <linearGradient id="gBlue2" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#E1F5FE"/><stop offset="100%" stop-color="#B3E5FC"/></linearGradient>
6+ <linearGradient id="gPurple" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#F3E5F5"/><stop offset="100%" stop-color="#E1BEE7"/></linearGradient>
7+ <linearGradient id="gYellow" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#FFFDE7"/><stop offset="100%" stop-color="#FFF9C4"/></linearGradient>
8+ <linearGradient id="gGrey" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#ECEFF1"/><stop offset="100%" stop-color="#CFD8DC"/></linearGradient>
9+ <filter id="shadow" x="-30%" y="-30%" width="160%" height="160%"><feDropShadow dx="2" dy="3" stdDeviation="4" flood-color="#000000" flood-opacity="0.25"/></filter>
10+ <marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#37474F"/></marker>
11+ </defs>
12+ <rect x="0" y="0" width="1200" height="720" fill="url(#gBg)"/>
13+ <text x="600.0" y="38" text-anchor="middle" font-size="25" font-weight="700" fill="#1A237E">dev.team — a coordinator and four specialists</text>
14+ <text x="600.0" y="61" text-anchor="middle" font-size="14" font-style="italic" fill="#546E7A">the coordinator picks a route and calls each agent with ask_agent, one at a time</text>
15+ <path d="M880,185 L910,185" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
16+ <path d="M1025,240 L1025,265 L930,265 L930,290" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
17+ <path d="M220,210 L300,210" fill="none" stroke="#37474F" stroke-width="2.2" marker-start="url(#arrow)" marker-end="url(#arrow)"/>
18+ <path d="M550,180 L620,180" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
19+ <path d="M425,280 L425,605 L650,605" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
20+ <path d="M550,250 L590,250 L590,690 L1055,690 L1055,660" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
21+ <path d="M620,470 L260,500" fill="none" stroke="#37474F" stroke-width="2.2" stroke-dasharray="6,4" marker-end="url(#arrow)"/>
22+ <g filter="url(#shadow)"><rect x="40" y="150" width="180" height="120" rx="16" fill="url(#gBlue)" stroke="#1565C0" stroke-width="2"/></g>
23+ <text x="130.0" y="190.0" text-anchor="middle" font-size="22">🧑</text>
24+ <text x="130.0" y="213.0" text-anchor="middle" font-size="16" font-weight="700" fill="#0D47A1">You</text>
25+ <text x="130.0" y="231.0" text-anchor="middle" font-size="11.5" fill="#0D47A1">a request</text>
26+ <text x="130.0" y="248.0" text-anchor="middle" font-size="11.5" fill="#0D47A1">Zed or mm -tui · workspace/</text>
27+ <g filter="url(#shadow)"><rect x="300" y="140" width="250" height="140" rx="16" fill="url(#gPurple)" stroke="#6A1B9A" stroke-width="3"/></g>
28+ <text x="425.0" y="181.5" text-anchor="middle" font-size="22">🔀</text>
29+ <text x="425.0" y="204.5" text-anchor="middle" font-size="16" font-weight="700" fill="#4A148C">Coordinator</text>
30+ <text x="425.0" y="222.5" text-anchor="middle" font-size="11.5" fill="#4A148C">Qwen3.5-4B · agent.yaml</text>
31+ <text x="425.0" y="239.5" text-anchor="middle" font-size="11.5" fill="#4A148C">only tool: ask_agent</text>
32+ <text x="425.0" y="256.5" text-anchor="middle" font-size="11.5" fill="#4A148C">picks route A, B or C</text>
33+ <rect x="488.4" y="128" width="71.6" height="24" rx="12" fill="#6A1B9A"/>
34+ <text x="524.1999999999999" y="144" text-anchor="middle" font-size="11.5" font-weight="700" fill="#FFFFFF">★ routes</text>
35+ <rect x="620" y="90" width="550" height="400" rx="10" fill="#FAFAFA" fill-opacity="0.7" stroke="#90A4AE" stroke-width="2" stroke-dasharray="7,5"/>
36+ <text x="895.0" y="112" text-anchor="middle" font-size="13" font-style="italic" font-weight="700" fill="#607D8B">route A — code to write or change (in order)</text>
37+ <g filter="url(#shadow)"><rect x="650" y="130" width="230" height="110" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>
38+ <text x="765.0" y="165.0" text-anchor="middle" font-size="22">💬</text>
39+ <text x="765.0" y="188.0" text-anchor="middle" font-size="15" font-weight="700" fill="#01579B">1 · Reformulator</text>
40+ <text x="765.0" y="206.0" text-anchor="middle" font-size="11.5" fill="#01579B">Gemma 4 12B</text>
41+ <text x="765.0" y="223.0" text-anchor="middle" font-size="11.5" fill="#01579B">task brief · ask_user</text>
42+ <g filter="url(#shadow)"><rect x="910" y="130" width="230" height="110" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>
43+ <text x="1025.0" y="165.0" text-anchor="middle" font-size="22">🛠️</text>
44+ <text x="1025.0" y="188.0" text-anchor="middle" font-size="15" font-weight="700" fill="#01579B">2 · Coder</text>
45+ <text x="1025.0" y="206.0" text-anchor="middle" font-size="11.5" fill="#01579B">Mellum2 12B</text>
46+ <text x="1025.0" y="223.0" text-anchor="middle" font-size="11.5" fill="#01579B">bash · files · tests</text>
47+ <g filter="url(#shadow)"><rect x="780" y="290" width="230" height="110" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>
48+ <text x="895.0" y="325.0" text-anchor="middle" font-size="22">🔎</text>
49+ <text x="895.0" y="348.0" text-anchor="middle" font-size="15" font-weight="700" fill="#01579B">3 · Reviewer</text>
50+ <text x="895.0" y="366.0" text-anchor="middle" font-size="11.5" fill="#01579B">Gemma 4 12B</text>
51+ <text x="895.0" y="383.0" text-anchor="middle" font-size="11.5" fill="#01579B">tests · VERDICT</text>
52+ <text x="895" y="450" text-anchor="middle" font-size="12" fill="#455A64" font-style="italic">VERDICT: FAIL → the coder fixes ONCE → the reviewer again</text>
53+ <rect x="1005.0" y="253.0" width="110" height="24" rx="5" fill="#FFFFFF"/>
54+ <text x="1060" y="269.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">brief + report</text>
55+ <g filter="url(#shadow)"><rect x="650" y="550" width="230" height="110" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>
56+ <text x="765.0" y="585.0" text-anchor="middle" font-size="22">📘</text>
57+ <text x="765.0" y="608.0" text-anchor="middle" font-size="15" font-weight="700" fill="#01579B">Tutor</text>
58+ <text x="765.0" y="626.0" text-anchor="middle" font-size="11.5" fill="#01579B">Mellum2 12B</text>
59+ <text x="765.0" y="643.0" text-anchor="middle" font-size="11.5" fill="#01579B">mini tutorials · save_answer</text>
60+ <text x="765" y="530" text-anchor="middle" font-size="12.5" fill="#607D8B" font-style="italic" font-weight="700">route B — a question, a lesson</text>
61+ <g filter="url(#shadow)"><rect x="940" y="550" width="230" height="110" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>
62+ <text x="1055.0" y="585.0" text-anchor="middle" font-size="22">🔎</text>
63+ <text x="1055.0" y="608.0" text-anchor="middle" font-size="15" font-weight="700" fill="#01579B">Reviewer</text>
64+ <text x="1055.0" y="626.0" text-anchor="middle" font-size="11.5" fill="#01579B">review existing code</text>
65+ <text x="1055.0" y="643.0" text-anchor="middle" font-size="11.5" fill="#01579B">no change</text>
66+ <text x="1055" y="530" text-anchor="middle" font-size="12.5" fill="#607D8B" font-style="italic" font-weight="700">route C — only a review</text>
67+ <g filter="url(#shadow)"><rect x="40" y="440" width="220" height="120" rx="16" fill="url(#gGrey)" stroke="#455A64" stroke-width="2"/></g>
68+ <text x="150.0" y="480.0" text-anchor="middle" font-size="22">💻</text>
69+ <text x="150.0" y="503.0" text-anchor="middle" font-size="15" font-weight="700" fill="#263238">workspace/</text>
70+ <text x="150.0" y="521.0" text-anchor="middle" font-size="11.5" fill="#263238">your project</text>
71+ <text x="150.0" y="538.0" text-anchor="middle" font-size="11.5" fill="#263238">a git repo: git diff</text>
72+ <rect x="222.0" y="218.0" width="76" height="40" rx="5" fill="#FFFFFF"/>
73+ <text x="260" y="235" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">request</text>
74+ <text x="260" y="251" text-anchor="middle" font-size="10.5" fill="#78909C">← report</text>
75+ <rect x="570.0" y="168.0" width="30" height="24" rx="5" fill="#FFFFFF"/>
76+ <text x="585" y="184.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">A</text>
77+ <rect x="410.0" y="428.0" width="30" height="24" rx="5" fill="#FFFFFF"/>
78+ <text x="425" y="444.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">B</text>
79+ <rect x="815.0" y="678.0" width="30" height="24" rx="5" fill="#FFFFFF"/>
80+ <text x="830" y="694.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">C</text>
81+ <rect x="255.0" y="482.0" width="150" height="24" rx="5" fill="#FFFFFF"/>
82+ <text x="330" y="498.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">reads · changes · tests</text>
83+</svg>
added dev.team/dev-team-memory.drawio +26 -0
new file mode 100644
@@ -0,0 +1,26 @@
1+<mxfile host="app.diagrams.net">
2+ <diagram id="dev-team-memory" name="dev.team — one model in memory at a time">
3+ <mxGraphModel dx="1200" dy="600" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1200" pageHeight="600" background="#F5F7FC" math="0" shadow="0">
4+ <root>
5+ <mxCell id="0"/>
6+ <mxCell id="1" parent="0"/>
7+ <mxCell id="title" value="&lt;b style=&quot;font-size:25px;color:#1A237E&quot;&gt;dev.team — one model in memory at a time&lt;/b&gt;&lt;br&gt;&lt;i style=&quot;font-size:14px;color:#546E7A&quot;&gt;two hooks of the coordinator unload a model at each hand-off; llmman reloads on the next request&lt;/i&gt;" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="150.0" y="12" width="900" height="56" as="geometry"/></mxCell>
8+ <mxCell id="c7" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="270" y="205" as="sourcePoint"/><mxPoint x="400" y="205" as="targetPoint"/></mxGeometry></mxCell>
9+ <mxCell id="c9" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="660" y="205" as="sourcePoint"/><mxPoint x="790" y="205" as="targetPoint"/></mxGeometry></mxCell>
10+ <mxCell id="c11" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="975" y="270" as="sourcePoint"/><mxPoint x="975" y="380" as="targetPoint"/></mxGeometry></mxCell>
11+ <mxCell id="c13" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="790" y="445" as="sourcePoint"/><mxPoint x="270" y="445" as="targetPoint"/></mxGeometry></mxCell>
12+ <mxCell id="c1" value="🔀&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;Coordinator&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Qwen3.5-4B · 3.4 GB&lt;br&gt;decides: ask_agent coder&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#F3E5F5;gradientColor=#E1BEE7;strokeColor=#6A1B9A;strokeWidth=3;shadow=1;fontSize=20;fontColor=#4A148C;" vertex="1" parent="1"><mxGeometry x="40" y="140" width="230" height="130" as="geometry"/></mxCell>
13+ <mxCell id="c2" value="&lt;b&gt;★ asks&lt;/b&gt;" style="rounded=1;arcSize=50;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=11;" vertex="1" parent="1"><mxGeometry x="222.8" y="128" width="57.2" height="24" as="geometry"/></mxCell>
14+ <mxCell id="c3" value="⚙️&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;preAsk hook&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;node hooks/unload.js self&lt;br&gt;unloads the 4B&lt;br&gt;before the agent starts&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#FFFDE7;gradientColor=#FFF9C4;strokeColor=#F9A825;strokeWidth=2;shadow=1;fontSize=20;fontColor=#E65100;" vertex="1" parent="1"><mxGeometry x="400" y="140" width="260" height="130" as="geometry"/></mxCell>
15+ <mxCell id="c4" value="🛠️&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;Team member&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Mellum2 12B · 8.1 GB or Gemma 4 12B · 7.3 GB&lt;br&gt;loaded alone · does the work&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="790" y="140" width="370" height="130" as="geometry"/></mxCell>
16+ <mxCell id="c5" value="⚙️&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;postAsk hook&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;node hooks/unload.js asked&lt;br&gt;unloads the 12B&lt;br&gt;before the coordinator reads&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#FFFDE7;gradientColor=#FFF9C4;strokeColor=#F9A825;strokeWidth=2;shadow=1;fontSize=20;fontColor=#E65100;" vertex="1" parent="1"><mxGeometry x="790" y="380" width="370" height="130" as="geometry"/></mxCell>
17+ <mxCell id="c6" value="🔀&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;Coordinator&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;the 4B reloads alone&lt;br&gt;reads the answer · next step&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#F3E5F5;gradientColor=#E1BEE7;strokeColor=#6A1B9A;strokeWidth=3;shadow=1;fontSize=20;fontColor=#4A148C;" vertex="1" parent="1"><mxGeometry x="40" y="380" width="230" height="130" as="geometry"/></mxCell>
18+ <mxCell id="c8" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;ask_agent&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="295.0" y="221.0" width="80" height="24" as="geometry"/></mxCell>
19+ <mxCell id="c10" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;keep_alive: 0&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:10.5px;color:#78909C&quot;&gt;POST /api/generate&lt;/span&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="670.0" y="185.0" width="110" height="40" as="geometry"/></mxCell>
20+ <mxCell id="c12" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;answer&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="975.0" y="313.0" width="70" height="24" as="geometry"/></mxCell>
21+ <mxCell id="c14" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;answer, then&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:10.5px;color:#78909C&quot;&gt;the 4B reloads&lt;/span&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="470.0" y="425.0" width="120" height="40" as="geometry"/></mxCell>
22+ <mxCell id="c15" value="Peak: one 12B model (~8 GB + KV cache at 32k) — reformulator &amp;amp; reviewer share Gemma, coder &amp;amp; tutor share Mellum2." style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;fontSize=12;fontColor=#455A64;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="179.64" y="548" width="840.72" height="18" as="geometry"/></mxCell>
23+ </root>
24+ </mxGraphModel>
25+ </diagram>
26+</mxfile>
new file mode 100644
@@ -0,0 +1,26 @@
1+<mxfile host="app.diagrams.net">
2+ <diagram id="dev-team-memory" name="dev.team — one model in memory at a time">
3+ <mxGraphModel dx="1200" dy="600" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1200" pageHeight="600" background="#F5F7FC" math="0" shadow="0">
4+ <root>
5+ <mxCell id="0"/>
6+ <mxCell id="1" parent="0"/>
7+ <mxCell id="title" value="&lt;b style=&quot;font-size:25px;color:#1A237E&quot;&gt;dev.team — one model in memory at a time&lt;/b&gt;&lt;br&gt;&lt;i style=&quot;font-size:14px;color:#546E7A&quot;&gt;two hooks of the coordinator unload a model at each hand-off; llmman reloads on the next request&lt;/i&gt;" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="150.0" y="12" width="900" height="56" as="geometry"/></mxCell>
8+ <mxCell id="c7" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="270" y="205" as="sourcePoint"/><mxPoint x="400" y="205" as="targetPoint"/></mxGeometry></mxCell>
9+ <mxCell id="c9" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="660" y="205" as="sourcePoint"/><mxPoint x="790" y="205" as="targetPoint"/></mxGeometry></mxCell>
10+ <mxCell id="c11" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="975" y="270" as="sourcePoint"/><mxPoint x="975" y="380" as="targetPoint"/></mxGeometry></mxCell>
11+ <mxCell id="c13" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="790" y="445" as="sourcePoint"/><mxPoint x="270" y="445" as="targetPoint"/></mxGeometry></mxCell>
12+ <mxCell id="c1" value="🔀&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;Coordinator&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Qwen3.5-4B · 3.4 GB&lt;br&gt;decides: ask_agent coder&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#F3E5F5;gradientColor=#E1BEE7;strokeColor=#6A1B9A;strokeWidth=3;shadow=1;fontSize=20;fontColor=#4A148C;" vertex="1" parent="1"><mxGeometry x="40" y="140" width="230" height="130" as="geometry"/></mxCell>
13+ <mxCell id="c2" value="&lt;b&gt;★ asks&lt;/b&gt;" style="rounded=1;arcSize=50;html=1;fillColor=#6A1B9A;strokeColor=none;fontColor=#FFFFFF;fontSize=11;" vertex="1" parent="1"><mxGeometry x="222.8" y="128" width="57.2" height="24" as="geometry"/></mxCell>
14+ <mxCell id="c3" value="⚙️&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;preAsk hook&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;node hooks/unload.js self&lt;br&gt;unloads the 4B&lt;br&gt;before the agent starts&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#FFFDE7;gradientColor=#FFF9C4;strokeColor=#F9A825;strokeWidth=2;shadow=1;fontSize=20;fontColor=#E65100;" vertex="1" parent="1"><mxGeometry x="400" y="140" width="260" height="130" as="geometry"/></mxCell>
15+ <mxCell id="c4" value="🛠️&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;Team member&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;Mellum2 12B · 8.1 GB or Gemma 4 12B · 7.3 GB&lt;br&gt;loaded alone · does the work&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="790" y="140" width="370" height="130" as="geometry"/></mxCell>
16+ <mxCell id="c5" value="⚙️&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;postAsk hook&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;node hooks/unload.js asked&lt;br&gt;unloads the 12B&lt;br&gt;before the coordinator reads&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#FFFDE7;gradientColor=#FFF9C4;strokeColor=#F9A825;strokeWidth=2;shadow=1;fontSize=20;fontColor=#E65100;" vertex="1" parent="1"><mxGeometry x="790" y="380" width="370" height="130" as="geometry"/></mxCell>
17+ <mxCell id="c6" value="🔀&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;Coordinator&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;the 4B reloads alone&lt;br&gt;reads the answer · next step&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#F3E5F5;gradientColor=#E1BEE7;strokeColor=#6A1B9A;strokeWidth=3;shadow=1;fontSize=20;fontColor=#4A148C;" vertex="1" parent="1"><mxGeometry x="40" y="380" width="230" height="130" as="geometry"/></mxCell>
18+ <mxCell id="c8" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;ask_agent&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="295.0" y="221.0" width="80" height="24" as="geometry"/></mxCell>
19+ <mxCell id="c10" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;keep_alive: 0&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:10.5px;color:#78909C&quot;&gt;POST /api/generate&lt;/span&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="670.0" y="185.0" width="110" height="40" as="geometry"/></mxCell>
20+ <mxCell id="c12" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;answer&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="975.0" y="313.0" width="70" height="24" as="geometry"/></mxCell>
21+ <mxCell id="c14" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;answer, then&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:10.5px;color:#78909C&quot;&gt;the 4B reloads&lt;/span&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="470.0" y="425.0" width="120" height="40" as="geometry"/></mxCell>
22+ <mxCell id="c15" value="Peak: one 12B model (~8 GB + KV cache at 32k) — reformulator &amp;amp; reviewer share Gemma, coder &amp;amp; tutor share Mellum2." style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;fontSize=12;fontColor=#455A64;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="179.64" y="548" width="840.72" height="18" as="geometry"/></mxCell>
23+ </root>
24+ </mxGraphModel>
25+ </diagram>
26+</mxfile>
added dev.team/dev-team-memory.png +0 -0
new file mode 100644
Binary files /dev/null and b/dev.team/dev-team-memory.png differ
new file mode 100644
Binary files /dev/null and b/dev.team/dev-team-memory.png differBinary files /dev/null and b/dev.team/dev-team-memory.png differ
added dev.team/dev-team-memory.svg +59 -0
new file mode 100644
@@ -0,0 +1,59 @@
1+<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="600" viewBox="0 0 1200 600" font-family="DejaVu Sans, Verdana, sans-serif">
2+ <defs>
3+ <linearGradient id="gBg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#FFFFFF"/><stop offset="100%" stop-color="#EEF2FA"/></linearGradient>
4+ <linearGradient id="gBlue" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#E3F2FD"/><stop offset="100%" stop-color="#BBDEFB"/></linearGradient>
5+ <linearGradient id="gBlue2" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#E1F5FE"/><stop offset="100%" stop-color="#B3E5FC"/></linearGradient>
6+ <linearGradient id="gPurple" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#F3E5F5"/><stop offset="100%" stop-color="#E1BEE7"/></linearGradient>
7+ <linearGradient id="gYellow" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#FFFDE7"/><stop offset="100%" stop-color="#FFF9C4"/></linearGradient>
8+ <linearGradient id="gGrey" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#ECEFF1"/><stop offset="100%" stop-color="#CFD8DC"/></linearGradient>
9+ <filter id="shadow" x="-30%" y="-30%" width="160%" height="160%"><feDropShadow dx="2" dy="3" stdDeviation="4" flood-color="#000000" flood-opacity="0.25"/></filter>
10+ <marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#37474F"/></marker>
11+ </defs>
12+ <rect x="0" y="0" width="1200" height="600" fill="url(#gBg)"/>
13+ <text x="600.0" y="38" text-anchor="middle" font-size="25" font-weight="700" fill="#1A237E">dev.team — one model in memory at a time</text>
14+ <text x="600.0" y="61" text-anchor="middle" font-size="14" font-style="italic" fill="#546E7A">two hooks of the coordinator unload a model at each hand-off; llmman reloads on the next request</text>
15+ <path d="M270,205 L400,205" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
16+ <path d="M660,205 L790,205" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
17+ <path d="M975,270 L975,380" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
18+ <path d="M790,445 L270,445" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
19+ <g filter="url(#shadow)"><rect x="40" y="140" width="230" height="130" rx="16" fill="url(#gPurple)" stroke="#6A1B9A" stroke-width="3"/></g>
20+ <text x="155.0" y="185.0" text-anchor="middle" font-size="22">🔀</text>
21+ <text x="155.0" y="208.0" text-anchor="middle" font-size="16" font-weight="700" fill="#4A148C">Coordinator</text>
22+ <text x="155.0" y="226.0" text-anchor="middle" font-size="11.5" fill="#4A148C">Qwen3.5-4B · 3.4 GB</text>
23+ <text x="155.0" y="243.0" text-anchor="middle" font-size="11.5" fill="#4A148C">decides: ask_agent coder</text>
24+ <rect x="222.8" y="128" width="57.2" height="24" rx="12" fill="#6A1B9A"/>
25+ <text x="251.4" y="144" text-anchor="middle" font-size="11.5" font-weight="700" fill="#FFFFFF">★ asks</text>
26+ <g filter="url(#shadow)"><rect x="400" y="140" width="260" height="130" rx="16" fill="url(#gYellow)" stroke="#F9A825" stroke-width="2"/></g>
27+ <text x="530.0" y="176.5" text-anchor="middle" font-size="22">⚙️</text>
28+ <text x="530.0" y="199.5" text-anchor="middle" font-size="16" font-weight="700" fill="#E65100">preAsk hook</text>
29+ <text x="530.0" y="217.5" text-anchor="middle" font-size="11.5" fill="#E65100">node hooks/unload.js self</text>
30+ <text x="530.0" y="234.5" text-anchor="middle" font-size="11.5" fill="#E65100">unloads the 4B</text>
31+ <text x="530.0" y="251.5" text-anchor="middle" font-size="11.5" fill="#E65100">before the agent starts</text>
32+ <g filter="url(#shadow)"><rect x="790" y="140" width="370" height="130" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>
33+ <text x="975.0" y="185.0" text-anchor="middle" font-size="22">🛠️</text>
34+ <text x="975.0" y="208.0" text-anchor="middle" font-size="16" font-weight="700" fill="#01579B">Team member</text>
35+ <text x="975.0" y="226.0" text-anchor="middle" font-size="11.5" fill="#01579B">Mellum2 12B · 8.1 GB or Gemma 4 12B · 7.3 GB</text>
36+ <text x="975.0" y="243.0" text-anchor="middle" font-size="11.5" fill="#01579B">loaded alone · does the work</text>
37+ <g filter="url(#shadow)"><rect x="790" y="380" width="370" height="130" rx="16" fill="url(#gYellow)" stroke="#F9A825" stroke-width="2"/></g>
38+ <text x="975.0" y="416.5" text-anchor="middle" font-size="22">⚙️</text>
39+ <text x="975.0" y="439.5" text-anchor="middle" font-size="16" font-weight="700" fill="#E65100">postAsk hook</text>
40+ <text x="975.0" y="457.5" text-anchor="middle" font-size="11.5" fill="#E65100">node hooks/unload.js asked</text>
41+ <text x="975.0" y="474.5" text-anchor="middle" font-size="11.5" fill="#E65100">unloads the 12B</text>
42+ <text x="975.0" y="491.5" text-anchor="middle" font-size="11.5" fill="#E65100">before the coordinator reads</text>
43+ <g filter="url(#shadow)"><rect x="40" y="380" width="230" height="130" rx="16" fill="url(#gPurple)" stroke="#6A1B9A" stroke-width="3"/></g>
44+ <text x="155.0" y="425.0" text-anchor="middle" font-size="22">🔀</text>
45+ <text x="155.0" y="448.0" text-anchor="middle" font-size="16" font-weight="700" fill="#4A148C">Coordinator</text>
46+ <text x="155.0" y="466.0" text-anchor="middle" font-size="11.5" fill="#4A148C">the 4B reloads alone</text>
47+ <text x="155.0" y="483.0" text-anchor="middle" font-size="11.5" fill="#4A148C">reads the answer · next step</text>
48+ <rect x="295.0" y="221.0" width="80" height="24" rx="5" fill="#FFFFFF"/>
49+ <text x="335" y="237.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">ask_agent</text>
50+ <rect x="670.0" y="185.0" width="110" height="40" rx="5" fill="#FFFFFF"/>
51+ <text x="725" y="202" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">keep_alive: 0</text>
52+ <text x="725" y="218" text-anchor="middle" font-size="10.5" fill="#78909C">POST /api/generate</text>
53+ <rect x="975.0" y="313.0" width="70" height="24" rx="5" fill="#FFFFFF"/>
54+ <text x="1010" y="329.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">answer</text>
55+ <rect x="470.0" y="425.0" width="120" height="40" rx="5" fill="#FFFFFF"/>
56+ <text x="530" y="442" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">answer, then</text>
57+ <text x="530" y="458" text-anchor="middle" font-size="10.5" fill="#78909C">the 4B reloads</text>
58+ <text x="600" y="560" text-anchor="middle" font-size="12" fill="#455A64" font-style="italic">Peak: one 12B model (~8 GB + KV cache at 32k) — reformulator &amp; reviewer share Gemma, coder &amp; tutor share Mellum2.</text>
59+</svg>
new file mode 100644
@@ -0,0 +1,59 @@
1+<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="600" viewBox="0 0 1200 600" font-family="DejaVu Sans, Verdana, sans-serif">
2+ <defs>
3+ <linearGradient id="gBg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#FFFFFF"/><stop offset="100%" stop-color="#EEF2FA"/></linearGradient>
4+ <linearGradient id="gBlue" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#E3F2FD"/><stop offset="100%" stop-color="#BBDEFB"/></linearGradient>
5+ <linearGradient id="gBlue2" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#E1F5FE"/><stop offset="100%" stop-color="#B3E5FC"/></linearGradient>
6+ <linearGradient id="gPurple" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#F3E5F5"/><stop offset="100%" stop-color="#E1BEE7"/></linearGradient>
7+ <linearGradient id="gYellow" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#FFFDE7"/><stop offset="100%" stop-color="#FFF9C4"/></linearGradient>
8+ <linearGradient id="gGrey" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#ECEFF1"/><stop offset="100%" stop-color="#CFD8DC"/></linearGradient>
9+ <filter id="shadow" x="-30%" y="-30%" width="160%" height="160%"><feDropShadow dx="2" dy="3" stdDeviation="4" flood-color="#000000" flood-opacity="0.25"/></filter>
10+ <marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#37474F"/></marker>
11+ </defs>
12+ <rect x="0" y="0" width="1200" height="600" fill="url(#gBg)"/>
13+ <text x="600.0" y="38" text-anchor="middle" font-size="25" font-weight="700" fill="#1A237E">dev.team — one model in memory at a time</text>
14+ <text x="600.0" y="61" text-anchor="middle" font-size="14" font-style="italic" fill="#546E7A">two hooks of the coordinator unload a model at each hand-off; llmman reloads on the next request</text>
15+ <path d="M270,205 L400,205" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
16+ <path d="M660,205 L790,205" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
17+ <path d="M975,270 L975,380" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
18+ <path d="M790,445 L270,445" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
19+ <g filter="url(#shadow)"><rect x="40" y="140" width="230" height="130" rx="16" fill="url(#gPurple)" stroke="#6A1B9A" stroke-width="3"/></g>
20+ <text x="155.0" y="185.0" text-anchor="middle" font-size="22">🔀</text>
21+ <text x="155.0" y="208.0" text-anchor="middle" font-size="16" font-weight="700" fill="#4A148C">Coordinator</text>
22+ <text x="155.0" y="226.0" text-anchor="middle" font-size="11.5" fill="#4A148C">Qwen3.5-4B · 3.4 GB</text>
23+ <text x="155.0" y="243.0" text-anchor="middle" font-size="11.5" fill="#4A148C">decides: ask_agent coder</text>
24+ <rect x="222.8" y="128" width="57.2" height="24" rx="12" fill="#6A1B9A"/>
25+ <text x="251.4" y="144" text-anchor="middle" font-size="11.5" font-weight="700" fill="#FFFFFF">★ asks</text>
26+ <g filter="url(#shadow)"><rect x="400" y="140" width="260" height="130" rx="16" fill="url(#gYellow)" stroke="#F9A825" stroke-width="2"/></g>
27+ <text x="530.0" y="176.5" text-anchor="middle" font-size="22">⚙️</text>
28+ <text x="530.0" y="199.5" text-anchor="middle" font-size="16" font-weight="700" fill="#E65100">preAsk hook</text>
29+ <text x="530.0" y="217.5" text-anchor="middle" font-size="11.5" fill="#E65100">node hooks/unload.js self</text>
30+ <text x="530.0" y="234.5" text-anchor="middle" font-size="11.5" fill="#E65100">unloads the 4B</text>
31+ <text x="530.0" y="251.5" text-anchor="middle" font-size="11.5" fill="#E65100">before the agent starts</text>
32+ <g filter="url(#shadow)"><rect x="790" y="140" width="370" height="130" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>
33+ <text x="975.0" y="185.0" text-anchor="middle" font-size="22">🛠️</text>
34+ <text x="975.0" y="208.0" text-anchor="middle" font-size="16" font-weight="700" fill="#01579B">Team member</text>
35+ <text x="975.0" y="226.0" text-anchor="middle" font-size="11.5" fill="#01579B">Mellum2 12B · 8.1 GB or Gemma 4 12B · 7.3 GB</text>
36+ <text x="975.0" y="243.0" text-anchor="middle" font-size="11.5" fill="#01579B">loaded alone · does the work</text>
37+ <g filter="url(#shadow)"><rect x="790" y="380" width="370" height="130" rx="16" fill="url(#gYellow)" stroke="#F9A825" stroke-width="2"/></g>
38+ <text x="975.0" y="416.5" text-anchor="middle" font-size="22">⚙️</text>
39+ <text x="975.0" y="439.5" text-anchor="middle" font-size="16" font-weight="700" fill="#E65100">postAsk hook</text>
40+ <text x="975.0" y="457.5" text-anchor="middle" font-size="11.5" fill="#E65100">node hooks/unload.js asked</text>
41+ <text x="975.0" y="474.5" text-anchor="middle" font-size="11.5" fill="#E65100">unloads the 12B</text>
42+ <text x="975.0" y="491.5" text-anchor="middle" font-size="11.5" fill="#E65100">before the coordinator reads</text>
43+ <g filter="url(#shadow)"><rect x="40" y="380" width="230" height="130" rx="16" fill="url(#gPurple)" stroke="#6A1B9A" stroke-width="3"/></g>
44+ <text x="155.0" y="425.0" text-anchor="middle" font-size="22">🔀</text>
45+ <text x="155.0" y="448.0" text-anchor="middle" font-size="16" font-weight="700" fill="#4A148C">Coordinator</text>
46+ <text x="155.0" y="466.0" text-anchor="middle" font-size="11.5" fill="#4A148C">the 4B reloads alone</text>
47+ <text x="155.0" y="483.0" text-anchor="middle" font-size="11.5" fill="#4A148C">reads the answer · next step</text>
48+ <rect x="295.0" y="221.0" width="80" height="24" rx="5" fill="#FFFFFF"/>
49+ <text x="335" y="237.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">ask_agent</text>
50+ <rect x="670.0" y="185.0" width="110" height="40" rx="5" fill="#FFFFFF"/>
51+ <text x="725" y="202" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">keep_alive: 0</text>
52+ <text x="725" y="218" text-anchor="middle" font-size="10.5" fill="#78909C">POST /api/generate</text>
53+ <rect x="975.0" y="313.0" width="70" height="24" rx="5" fill="#FFFFFF"/>
54+ <text x="1010" y="329.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">answer</text>
55+ <rect x="470.0" y="425.0" width="120" height="40" rx="5" fill="#FFFFFF"/>
56+ <text x="530" y="442" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">answer, then</text>
57+ <text x="530" y="458" text-anchor="middle" font-size="10.5" fill="#78909C">the 4B reloads</text>
58+ <text x="600" y="560" text-anchor="middle" font-size="12" fill="#455A64" font-style="italic">Peak: one 12B model (~8 GB + KV cache at 32k) — reformulator &amp; reviewer share Gemma, coder &amp; tutor share Mellum2.</text>
59+</svg>
added dev.team/dev-team-sandbox.drawio +24 -0
new file mode 100644
@@ -0,0 +1,24 @@
1+<mxfile host="app.diagrams.net">
2+ <diagram id="dev-team-sandbox" name="dev.team — the agents are read-only in the sandbox">
3+ <mxGraphModel dx="1200" dy="640" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1200" pageHeight="640" background="#F5F7FC" math="0" shadow="0">
4+ <root>
5+ <mxCell id="0"/>
6+ <mxCell id="1" parent="0"/>
7+ <mxCell id="title" value="&lt;b style=&quot;font-size:25px;color:#1A237E&quot;&gt;dev.team — the agents are read-only in the sandbox&lt;/b&gt;&lt;br&gt;&lt;i style=&quot;font-size:14px;color:#546E7A&quot;&gt;sbxenv.yaml mounts workspace/ read-write and team/ read-only; the env file itself is never mounted&lt;/i&gt;" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="150.0" y="12" width="900" height="56" as="geometry"/></mxCell>
8+ <mxCell id="c7" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="340" y="215" as="sourcePoint"/><mxPoint x="420" y="215" as="targetPoint"/></mxGeometry></mxCell>
9+ <mxCell id="c9" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="760" y="200" as="sourcePoint"/><mxPoint x="840" y="200" as="targetPoint"/></mxGeometry></mxCell>
10+ <mxCell id="c11" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="610" y="290" as="sourcePoint"/><mxPoint x="840" y="435" as="targetPoint"/><Array as="points"><mxPoint x="610" y="435"/></Array></mxGeometry></mxCell>
11+ <mxCell id="c1" value="💻&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;dev.team/ on the host&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;sbxenv.yaml · start-zed.sh · stop-zed.sh&lt;br&gt;README · diagrams&lt;br&gt;never mounted&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E3F2FD;gradientColor=#BBDEFB;strokeColor=#1565C0;strokeWidth=2;shadow=1;fontSize=20;fontColor=#0D47A1;" vertex="1" parent="1"><mxGeometry x="40" y="140" width="300" height="150" as="geometry"/></mxCell>
12+ <mxCell id="c2" value="&lt;i&gt;&lt;b&gt;sandbox zed-dev-team (sbx env create)&lt;/b&gt;&lt;/i&gt;" style="rounded=1;arcSize=3;html=1;dashed=1;dashPattern=7 5;fillColor=#FAFAFA;strokeColor=#90A4AE;strokeWidth=2;verticalAlign=top;fontColor=#607D8B;fontSize=13;" vertex="1" parent="1"><mxGeometry x="420" y="90" width="750" height="470" as="geometry"/></mxCell>
13+ <mxCell id="c3" value="🔀&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;mm -acp (Zed) / mm -tui&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;AGENT_CONFIG =&lt;br&gt;&amp;lt;dev.team&amp;gt;/team/agent.yaml&lt;br&gt;cwd = workspace/&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="460" y="140" width="300" height="150" as="geometry"/></mxCell>
14+ <mxCell id="c4" value="🛡️&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;team/ · read-only&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;agent*.yaml · hooks/&lt;br&gt;mm reads them&lt;br&gt;nothing can rewrite them&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#FFFDE7;gradientColor=#FFF9C4;strokeColor=#F9A825;strokeWidth=2;shadow=1;fontSize=20;fontColor=#E65100;" vertex="1" parent="1"><mxGeometry x="840" y="140" width="300" height="150" as="geometry"/></mxCell>
15+ <mxCell id="c5" value="&lt;b&gt;readOnly: true&lt;/b&gt;" style="rounded=1;arcSize=50;html=1;fillColor=#F9A825;strokeColor=none;fontColor=#FFFFFF;fontSize=11;" vertex="1" parent="1"><mxGeometry x="1035.2" y="128" width="114.8" height="24" as="geometry"/></mxCell>
16+ <mxCell id="c6" value="🧰&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;workspace/ · read-write&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;your project (a git repo)&lt;br&gt;.mm/ sessions and traces&lt;br&gt;Zed opens this folder&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=20;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="840" y="360" width="300" height="150" as="geometry"/></mxCell>
17+ <mxCell id="c8" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;creates&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="345.0" y="231.0" width="70" height="24" as="geometry"/></mxCell>
18+ <mxCell id="c10" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;reads&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="770.0" y="216.0" width="60" height="24" as="geometry"/></mxCell>
19+ <mxCell id="c12" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;reads · writes · runs&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="625.0" y="423.0" width="150" height="24" as="geometry"/></mxCell>
20+ <mxCell id="c13" value="start-zed.sh: sbx env create --env-arg DEV_TEAM_DIR=$PWD, then zed --new ssh://zed-dev-team.sbx$PWD/workspace" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;fontSize=12;fontColor=#455A64;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="199.51999999999998" y="588" width="810.96" height="18" as="geometry"/></mxCell>
21+ </root>
22+ </mxGraphModel>
23+ </diagram>
24+</mxfile>
new file mode 100644
@@ -0,0 +1,24 @@
1+<mxfile host="app.diagrams.net">
2+ <diagram id="dev-team-sandbox" name="dev.team — the agents are read-only in the sandbox">
3+ <mxGraphModel dx="1200" dy="640" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1200" pageHeight="640" background="#F5F7FC" math="0" shadow="0">
4+ <root>
5+ <mxCell id="0"/>
6+ <mxCell id="1" parent="0"/>
7+ <mxCell id="title" value="&lt;b style=&quot;font-size:25px;color:#1A237E&quot;&gt;dev.team — the agents are read-only in the sandbox&lt;/b&gt;&lt;br&gt;&lt;i style=&quot;font-size:14px;color:#546E7A&quot;&gt;sbxenv.yaml mounts workspace/ read-write and team/ read-only; the env file itself is never mounted&lt;/i&gt;" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;" vertex="1" parent="1"><mxGeometry x="150.0" y="12" width="900" height="56" as="geometry"/></mxCell>
8+ <mxCell id="c7" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="340" y="215" as="sourcePoint"/><mxPoint x="420" y="215" as="targetPoint"/></mxGeometry></mxCell>
9+ <mxCell id="c9" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="760" y="200" as="sourcePoint"/><mxPoint x="840" y="200" as="targetPoint"/></mxGeometry></mxCell>
10+ <mxCell id="c11" value="" style="edgeStyle=none;rounded=0;html=1;strokeColor=#37474F;strokeWidth=2;endArrow=block;endFill=1;startArrow=none;" edge="1" parent="1"><mxGeometry relative="1" as="geometry"><mxPoint x="610" y="290" as="sourcePoint"/><mxPoint x="840" y="435" as="targetPoint"/><Array as="points"><mxPoint x="610" y="435"/></Array></mxGeometry></mxCell>
11+ <mxCell id="c1" value="💻&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;dev.team/ on the host&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;sbxenv.yaml · start-zed.sh · stop-zed.sh&lt;br&gt;README · diagrams&lt;br&gt;never mounted&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E3F2FD;gradientColor=#BBDEFB;strokeColor=#1565C0;strokeWidth=2;shadow=1;fontSize=20;fontColor=#0D47A1;" vertex="1" parent="1"><mxGeometry x="40" y="140" width="300" height="150" as="geometry"/></mxCell>
12+ <mxCell id="c2" value="&lt;i&gt;&lt;b&gt;sandbox zed-dev-team (sbx env create)&lt;/b&gt;&lt;/i&gt;" style="rounded=1;arcSize=3;html=1;dashed=1;dashPattern=7 5;fillColor=#FAFAFA;strokeColor=#90A4AE;strokeWidth=2;verticalAlign=top;fontColor=#607D8B;fontSize=13;" vertex="1" parent="1"><mxGeometry x="420" y="90" width="750" height="470" as="geometry"/></mxCell>
13+ <mxCell id="c3" value="🔀&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;mm -acp (Zed) / mm -tui&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;AGENT_CONFIG =&lt;br&gt;&amp;lt;dev.team&amp;gt;/team/agent.yaml&lt;br&gt;cwd = workspace/&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#E1F5FE;gradientColor=#B3E5FC;strokeColor=#0277BD;strokeWidth=2;shadow=1;fontSize=20;fontColor=#01579B;" vertex="1" parent="1"><mxGeometry x="460" y="140" width="300" height="150" as="geometry"/></mxCell>
14+ <mxCell id="c4" value="🛡️&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;team/ · read-only&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;agent*.yaml · hooks/&lt;br&gt;mm reads them&lt;br&gt;nothing can rewrite them&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#FFFDE7;gradientColor=#FFF9C4;strokeColor=#F9A825;strokeWidth=2;shadow=1;fontSize=20;fontColor=#E65100;" vertex="1" parent="1"><mxGeometry x="840" y="140" width="300" height="150" as="geometry"/></mxCell>
15+ <mxCell id="c5" value="&lt;b&gt;readOnly: true&lt;/b&gt;" style="rounded=1;arcSize=50;html=1;fillColor=#F9A825;strokeColor=none;fontColor=#FFFFFF;fontSize=11;" vertex="1" parent="1"><mxGeometry x="1035.2" y="128" width="114.8" height="24" as="geometry"/></mxCell>
16+ <mxCell id="c6" value="🧰&lt;br&gt;&lt;b style=&quot;font-size:16px&quot;&gt;workspace/ · read-write&lt;/b&gt;&lt;br&gt;&lt;span style=&quot;font-size:11.5px;font-weight:normal&quot;&gt;your project (a git repo)&lt;br&gt;.mm/ sessions and traces&lt;br&gt;Zed opens this folder&lt;/span&gt;" style="rounded=1;arcSize=14;whiteSpace=wrap;html=1;fillColor=#ECEFF1;gradientColor=#CFD8DC;strokeColor=#455A64;strokeWidth=2;shadow=1;fontSize=20;fontColor=#263238;" vertex="1" parent="1"><mxGeometry x="840" y="360" width="300" height="150" as="geometry"/></mxCell>
17+ <mxCell id="c8" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;creates&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="345.0" y="231.0" width="70" height="24" as="geometry"/></mxCell>
18+ <mxCell id="c10" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;reads&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="770.0" y="216.0" width="60" height="24" as="geometry"/></mxCell>
19+ <mxCell id="c12" value="&lt;b style=&quot;font-size:12.5px;color:#263238&quot;&gt;reads · writes · runs&lt;/b&gt;" style="text;html=1;align=center;verticalAlign=middle;fillColor=#FFFFFF;strokeColor=none;rounded=1;" vertex="1" parent="1"><mxGeometry x="625.0" y="423.0" width="150" height="24" as="geometry"/></mxCell>
20+ <mxCell id="c13" value="start-zed.sh: sbx env create --env-arg DEV_TEAM_DIR=$PWD, then zed --new ssh://zed-dev-team.sbx$PWD/workspace" style="text;html=1;align=center;verticalAlign=middle;strokeColor=none;fillColor=none;fontSize=12;fontColor=#455A64;fontStyle=2;" vertex="1" parent="1"><mxGeometry x="199.51999999999998" y="588" width="810.96" height="18" as="geometry"/></mxCell>
21+ </root>
22+ </mxGraphModel>
23+ </diagram>
24+</mxfile>
added dev.team/dev-team-sandbox.png +0 -0
new file mode 100644
Binary files /dev/null and b/dev.team/dev-team-sandbox.png differ
new file mode 100644
Binary files /dev/null and b/dev.team/dev-team-sandbox.png differBinary files /dev/null and b/dev.team/dev-team-sandbox.png differ
added dev.team/dev-team-sandbox.svg +53 -0
new file mode 100644
@@ -0,0 +1,53 @@
1+<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="640" viewBox="0 0 1200 640" font-family="DejaVu Sans, Verdana, sans-serif">
2+ <defs>
3+ <linearGradient id="gBg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#FFFFFF"/><stop offset="100%" stop-color="#EEF2FA"/></linearGradient>
4+ <linearGradient id="gBlue" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#E3F2FD"/><stop offset="100%" stop-color="#BBDEFB"/></linearGradient>
5+ <linearGradient id="gBlue2" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#E1F5FE"/><stop offset="100%" stop-color="#B3E5FC"/></linearGradient>
6+ <linearGradient id="gPurple" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#F3E5F5"/><stop offset="100%" stop-color="#E1BEE7"/></linearGradient>
7+ <linearGradient id="gYellow" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#FFFDE7"/><stop offset="100%" stop-color="#FFF9C4"/></linearGradient>
8+ <linearGradient id="gGrey" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#ECEFF1"/><stop offset="100%" stop-color="#CFD8DC"/></linearGradient>
9+ <filter id="shadow" x="-30%" y="-30%" width="160%" height="160%"><feDropShadow dx="2" dy="3" stdDeviation="4" flood-color="#000000" flood-opacity="0.25"/></filter>
10+ <marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#37474F"/></marker>
11+ </defs>
12+ <rect x="0" y="0" width="1200" height="640" fill="url(#gBg)"/>
13+ <text x="600.0" y="38" text-anchor="middle" font-size="25" font-weight="700" fill="#1A237E">dev.team — the agents are read-only in the sandbox</text>
14+ <text x="600.0" y="61" text-anchor="middle" font-size="14" font-style="italic" fill="#546E7A">sbxenv.yaml mounts workspace/ read-write and team/ read-only; the env file itself is never mounted</text>
15+ <path d="M340,215 L420,215" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
16+ <path d="M760,200 L840,200" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
17+ <path d="M610,290 L610,435 L840,435" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
18+ <g filter="url(#shadow)"><rect x="40" y="140" width="300" height="150" rx="16" fill="url(#gBlue)" stroke="#1565C0" stroke-width="2"/></g>
19+ <text x="190.0" y="186.5" text-anchor="middle" font-size="22">💻</text>
20+ <text x="190.0" y="209.5" text-anchor="middle" font-size="16" font-weight="700" fill="#0D47A1">dev.team/ on the host</text>
21+ <text x="190.0" y="227.5" text-anchor="middle" font-size="11.5" fill="#0D47A1">sbxenv.yaml · start-zed.sh · stop-zed.sh</text>
22+ <text x="190.0" y="244.5" text-anchor="middle" font-size="11.5" fill="#0D47A1">README · diagrams</text>
23+ <text x="190.0" y="261.5" text-anchor="middle" font-size="11.5" fill="#0D47A1">never mounted</text>
24+ <rect x="420" y="90" width="750" height="470" rx="10" fill="#FAFAFA" fill-opacity="0.7" stroke="#90A4AE" stroke-width="2" stroke-dasharray="7,5"/>
25+ <text x="795.0" y="112" text-anchor="middle" font-size="13" font-style="italic" font-weight="700" fill="#607D8B">sandbox zed-dev-team (sbx env create)</text>
26+ <g filter="url(#shadow)"><rect x="460" y="140" width="300" height="150" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>
27+ <text x="610.0" y="186.5" text-anchor="middle" font-size="22">🔀</text>
28+ <text x="610.0" y="209.5" text-anchor="middle" font-size="16" font-weight="700" fill="#01579B">mm -acp (Zed) / mm -tui</text>
29+ <text x="610.0" y="227.5" text-anchor="middle" font-size="11.5" fill="#01579B">AGENT_CONFIG =</text>
30+ <text x="610.0" y="244.5" text-anchor="middle" font-size="11.5" fill="#01579B">&lt;dev.team&gt;/team/agent.yaml</text>
31+ <text x="610.0" y="261.5" text-anchor="middle" font-size="11.5" fill="#01579B">cwd = workspace/</text>
32+ <g filter="url(#shadow)"><rect x="840" y="140" width="300" height="150" rx="16" fill="url(#gYellow)" stroke="#F9A825" stroke-width="2"/></g>
33+ <text x="990.0" y="186.5" text-anchor="middle" font-size="22">🛡️</text>
34+ <text x="990.0" y="209.5" text-anchor="middle" font-size="16" font-weight="700" fill="#E65100">team/ · read-only</text>
35+ <text x="990.0" y="227.5" text-anchor="middle" font-size="11.5" fill="#E65100">agent*.yaml · hooks/</text>
36+ <text x="990.0" y="244.5" text-anchor="middle" font-size="11.5" fill="#E65100">mm reads them</text>
37+ <text x="990.0" y="261.5" text-anchor="middle" font-size="11.5" fill="#E65100">nothing can rewrite them</text>
38+ <rect x="1035.2" y="128" width="114.8" height="24" rx="12" fill="#F9A825"/>
39+ <text x="1092.6000000000001" y="144" text-anchor="middle" font-size="11.5" font-weight="700" fill="#FFFFFF">readOnly: true</text>
40+ <g filter="url(#shadow)"><rect x="840" y="360" width="300" height="150" rx="16" fill="url(#gGrey)" stroke="#455A64" stroke-width="2"/></g>
41+ <text x="990.0" y="406.5" text-anchor="middle" font-size="22">🧰</text>
42+ <text x="990.0" y="429.5" text-anchor="middle" font-size="16" font-weight="700" fill="#263238">workspace/ · read-write</text>
43+ <text x="990.0" y="447.5" text-anchor="middle" font-size="11.5" fill="#263238">your project (a git repo)</text>
44+ <text x="990.0" y="464.5" text-anchor="middle" font-size="11.5" fill="#263238">.mm/ sessions and traces</text>
45+ <text x="990.0" y="481.5" text-anchor="middle" font-size="11.5" fill="#263238">Zed opens this folder</text>
46+ <rect x="345.0" y="231.0" width="70" height="24" rx="5" fill="#FFFFFF"/>
47+ <text x="380" y="247.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">creates</text>
48+ <rect x="770.0" y="216.0" width="60" height="24" rx="5" fill="#FFFFFF"/>
49+ <text x="800" y="232.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">reads</text>
50+ <rect x="625.0" y="423.0" width="150" height="24" rx="5" fill="#FFFFFF"/>
51+ <text x="700" y="439.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">reads · writes · runs</text>
52+ <text x="605" y="600" text-anchor="middle" font-size="12" fill="#455A64" font-style="italic">start-zed.sh: sbx env create --env-arg DEV_TEAM_DIR=$PWD, then zed --new ssh://zed-dev-team.sbx$PWD/workspace</text>
53+</svg>
new file mode 100644
@@ -0,0 +1,53 @@
1+<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="640" viewBox="0 0 1200 640" font-family="DejaVu Sans, Verdana, sans-serif">
2+ <defs>
3+ <linearGradient id="gBg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#FFFFFF"/><stop offset="100%" stop-color="#EEF2FA"/></linearGradient>
4+ <linearGradient id="gBlue" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#E3F2FD"/><stop offset="100%" stop-color="#BBDEFB"/></linearGradient>
5+ <linearGradient id="gBlue2" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#E1F5FE"/><stop offset="100%" stop-color="#B3E5FC"/></linearGradient>
6+ <linearGradient id="gPurple" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#F3E5F5"/><stop offset="100%" stop-color="#E1BEE7"/></linearGradient>
7+ <linearGradient id="gYellow" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#FFFDE7"/><stop offset="100%" stop-color="#FFF9C4"/></linearGradient>
8+ <linearGradient id="gGrey" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#ECEFF1"/><stop offset="100%" stop-color="#CFD8DC"/></linearGradient>
9+ <filter id="shadow" x="-30%" y="-30%" width="160%" height="160%"><feDropShadow dx="2" dy="3" stdDeviation="4" flood-color="#000000" flood-opacity="0.25"/></filter>
10+ <marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" fill="#37474F"/></marker>
11+ </defs>
12+ <rect x="0" y="0" width="1200" height="640" fill="url(#gBg)"/>
13+ <text x="600.0" y="38" text-anchor="middle" font-size="25" font-weight="700" fill="#1A237E">dev.team — the agents are read-only in the sandbox</text>
14+ <text x="600.0" y="61" text-anchor="middle" font-size="14" font-style="italic" fill="#546E7A">sbxenv.yaml mounts workspace/ read-write and team/ read-only; the env file itself is never mounted</text>
15+ <path d="M340,215 L420,215" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
16+ <path d="M760,200 L840,200" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
17+ <path d="M610,290 L610,435 L840,435" fill="none" stroke="#37474F" stroke-width="2.2" marker-end="url(#arrow)"/>
18+ <g filter="url(#shadow)"><rect x="40" y="140" width="300" height="150" rx="16" fill="url(#gBlue)" stroke="#1565C0" stroke-width="2"/></g>
19+ <text x="190.0" y="186.5" text-anchor="middle" font-size="22">💻</text>
20+ <text x="190.0" y="209.5" text-anchor="middle" font-size="16" font-weight="700" fill="#0D47A1">dev.team/ on the host</text>
21+ <text x="190.0" y="227.5" text-anchor="middle" font-size="11.5" fill="#0D47A1">sbxenv.yaml · start-zed.sh · stop-zed.sh</text>
22+ <text x="190.0" y="244.5" text-anchor="middle" font-size="11.5" fill="#0D47A1">README · diagrams</text>
23+ <text x="190.0" y="261.5" text-anchor="middle" font-size="11.5" fill="#0D47A1">never mounted</text>
24+ <rect x="420" y="90" width="750" height="470" rx="10" fill="#FAFAFA" fill-opacity="0.7" stroke="#90A4AE" stroke-width="2" stroke-dasharray="7,5"/>
25+ <text x="795.0" y="112" text-anchor="middle" font-size="13" font-style="italic" font-weight="700" fill="#607D8B">sandbox zed-dev-team (sbx env create)</text>
26+ <g filter="url(#shadow)"><rect x="460" y="140" width="300" height="150" rx="16" fill="url(#gBlue2)" stroke="#0277BD" stroke-width="2"/></g>
27+ <text x="610.0" y="186.5" text-anchor="middle" font-size="22">🔀</text>
28+ <text x="610.0" y="209.5" text-anchor="middle" font-size="16" font-weight="700" fill="#01579B">mm -acp (Zed) / mm -tui</text>
29+ <text x="610.0" y="227.5" text-anchor="middle" font-size="11.5" fill="#01579B">AGENT_CONFIG =</text>
30+ <text x="610.0" y="244.5" text-anchor="middle" font-size="11.5" fill="#01579B">&lt;dev.team&gt;/team/agent.yaml</text>
31+ <text x="610.0" y="261.5" text-anchor="middle" font-size="11.5" fill="#01579B">cwd = workspace/</text>
32+ <g filter="url(#shadow)"><rect x="840" y="140" width="300" height="150" rx="16" fill="url(#gYellow)" stroke="#F9A825" stroke-width="2"/></g>
33+ <text x="990.0" y="186.5" text-anchor="middle" font-size="22">🛡️</text>
34+ <text x="990.0" y="209.5" text-anchor="middle" font-size="16" font-weight="700" fill="#E65100">team/ · read-only</text>
35+ <text x="990.0" y="227.5" text-anchor="middle" font-size="11.5" fill="#E65100">agent*.yaml · hooks/</text>
36+ <text x="990.0" y="244.5" text-anchor="middle" font-size="11.5" fill="#E65100">mm reads them</text>
37+ <text x="990.0" y="261.5" text-anchor="middle" font-size="11.5" fill="#E65100">nothing can rewrite them</text>
38+ <rect x="1035.2" y="128" width="114.8" height="24" rx="12" fill="#F9A825"/>
39+ <text x="1092.6000000000001" y="144" text-anchor="middle" font-size="11.5" font-weight="700" fill="#FFFFFF">readOnly: true</text>
40+ <g filter="url(#shadow)"><rect x="840" y="360" width="300" height="150" rx="16" fill="url(#gGrey)" stroke="#455A64" stroke-width="2"/></g>
41+ <text x="990.0" y="406.5" text-anchor="middle" font-size="22">🧰</text>
42+ <text x="990.0" y="429.5" text-anchor="middle" font-size="16" font-weight="700" fill="#263238">workspace/ · read-write</text>
43+ <text x="990.0" y="447.5" text-anchor="middle" font-size="11.5" fill="#263238">your project (a git repo)</text>
44+ <text x="990.0" y="464.5" text-anchor="middle" font-size="11.5" fill="#263238">.mm/ sessions and traces</text>
45+ <text x="990.0" y="481.5" text-anchor="middle" font-size="11.5" fill="#263238">Zed opens this folder</text>
46+ <rect x="345.0" y="231.0" width="70" height="24" rx="5" fill="#FFFFFF"/>
47+ <text x="380" y="247.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">creates</text>
48+ <rect x="770.0" y="216.0" width="60" height="24" rx="5" fill="#FFFFFF"/>
49+ <text x="800" y="232.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">reads</text>
50+ <rect x="625.0" y="423.0" width="150" height="24" rx="5" fill="#FFFFFF"/>
51+ <text x="700" y="439.5" text-anchor="middle" font-size="12.5" font-weight="700" fill="#263238">reads · writes · runs</text>
52+ <text x="605" y="600" text-anchor="middle" font-size="12" fill="#455A64" font-style="italic">start-zed.sh: sbx env create --env-arg DEV_TEAM_DIR=$PWD, then zed --new ssh://zed-dev-team.sbx$PWD/workspace</text>
53+</svg>
added dev.team/sbxenv.yaml +60 -0
new file mode 100644
@@ -0,0 +1,60 @@
1+schemaVersion: "1"
2+
3+# Sandbox name = SSH host (<name>.sbx): letters, digits and hyphens only.
4+name: zed-dev-team
5+
6+agent: zed-ssh-kit
7+
8+kits:
9+ - docker.io/k33g/zed-ssh-kit:0.0.8
10+ - docker.io/k33g/mini-me-agent-kit:0.0.8
11+ - docker.io/k33g/nushell-kit:0.0.8
12+ - docker.io/k33g/go-toolchain-kit:0.0.8
13+ - docker.io/k33g/rust-toolchain-kit:0.0.8
14+
15+# This file and the start / stop scripts stay OUTSIDE every mounted
16+# directory: Docker recommends it, so that an agent cannot rewrite the
17+# commands your host runs (writableEnvFiles is left off).
18+#
19+# dev.team/
20+# ├── sbxenv.yaml, start-zed.sh, stop-zed.sh, README.* not mounted
21+# ├── team/ the agents (agent*.yaml) and their hooks READ-ONLY
22+# └── workspace/ the code the team works on read-write
23+
24+# The code: playground/, demo/, sessions (.mm/). Zed opens this folder.
25+workspace: ./workspace
26+
27+# The agents: mm reads them, nothing in the sandbox can change them.
28+additionalWorkspaces:
29+ - path: ./team
30+ readOnly: true
31+
32+# The folder of this file, as the sandbox sees it — start-zed.sh passes it:
33+# sbx env create --force --env-arg DEV_TEAM_DIR="$PWD"
34+args:
35+ DEV_TEAM_DIR:
36+ required: true
37+
38+env:
39+ # mm -acp (Zed) and mm without -config read the coordinator from team/,
40+ # not ./agent.yaml: the workspace holds no agent file to rewrite.
41+ AGENT_CONFIG: ${{ env.args.DEV_TEAM_DIR }}/team/agent.yaml
42+
43+# Published when the sandbox is created, each to the same port on the host
44+# (loopback, tcp4 by default). A port already taken on the host makes the
45+# creation fail. A change here applies at the next ./start-zed.sh.
46+# Inside the sandbox, a server must listen on 0.0.0.0, not 127.0.0.1.
47+ports:
48+ - sandbox: 8080
49+ host: 8080
50+ - sandbox: 6060
51+ host: 6060
52+ - sandbox: 5050
53+ host: 5050
54+
55+sandboxOptions:
56+ pullPolicy: always
57+
58+# sbx setup ssh
59+# ./start-zed.sh (sbx env create … then zed --new ssh://zed-dev-team.sbx<dev.team>/workspace)
60+# ./stop-zed.sh (sbx env rm --force --env-arg DEV_TEAM_DIR="$PWD")
new file mode 100644
@@ -0,0 +1,60 @@
1+schemaVersion: "1"
2+
3+# Sandbox name = SSH host (<name>.sbx): letters, digits and hyphens only.
4+name: zed-dev-team
5+
6+agent: zed-ssh-kit
7+
8+kits:
9+ - docker.io/k33g/zed-ssh-kit:0.0.8
10+ - docker.io/k33g/mini-me-agent-kit:0.0.8
11+ - docker.io/k33g/nushell-kit:0.0.8
12+ - docker.io/k33g/go-toolchain-kit:0.0.8
13+ - docker.io/k33g/rust-toolchain-kit:0.0.8
14+
15+# This file and the start / stop scripts stay OUTSIDE every mounted
16+# directory: Docker recommends it, so that an agent cannot rewrite the
17+# commands your host runs (writableEnvFiles is left off).
18+#
19+# dev.team/
20+# ├── sbxenv.yaml, start-zed.sh, stop-zed.sh, README.* not mounted
21+# ├── team/ the agents (agent*.yaml) and their hooks READ-ONLY
22+# └── workspace/ the code the team works on read-write
23+
24+# The code: playground/, demo/, sessions (.mm/). Zed opens this folder.
25+workspace: ./workspace
26+
27+# The agents: mm reads them, nothing in the sandbox can change them.
28+additionalWorkspaces:
29+ - path: ./team
30+ readOnly: true
31+
32+# The folder of this file, as the sandbox sees it — start-zed.sh passes it:
33+# sbx env create --force --env-arg DEV_TEAM_DIR="$PWD"
34+args:
35+ DEV_TEAM_DIR:
36+ required: true
37+
38+env:
39+ # mm -acp (Zed) and mm without -config read the coordinator from team/,
40+ # not ./agent.yaml: the workspace holds no agent file to rewrite.
41+ AGENT_CONFIG: ${{ env.args.DEV_TEAM_DIR }}/team/agent.yaml
42+
43+# Published when the sandbox is created, each to the same port on the host
44+# (loopback, tcp4 by default). A port already taken on the host makes the
45+# creation fail. A change here applies at the next ./start-zed.sh.
46+# Inside the sandbox, a server must listen on 0.0.0.0, not 127.0.0.1.
47+ports:
48+ - sandbox: 8080
49+ host: 8080
50+ - sandbox: 6060
51+ host: 6060
52+ - sandbox: 5050
53+ host: 5050
54+
55+sandboxOptions:
56+ pullPolicy: always
57+
58+# sbx setup ssh
59+# ./start-zed.sh (sbx env create … then zed --new ssh://zed-dev-team.sbx<dev.team>/workspace)
60+# ./stop-zed.sh (sbx env rm --force --env-arg DEV_TEAM_DIR="$PWD")
added dev.team/start-zed.sh +8 -0
new file mode 100755
@@ -0,0 +1,8 @@
1+#!/usr/bin/env bash
2+# Creates the sandbox from sbxenv.yaml and opens workspace/ in Zed over SSH.
3+# Run it from dev.team/ (the folder of sbxenv.yaml). Once: sbx setup ssh
4+set -euo pipefail
5+cd "$(dirname "$0")"
6+# DEV_TEAM_DIR tells the sandbox where team/agent.yaml is (AGENT_CONFIG).
7+sbx env create --force --env-arg DEV_TEAM_DIR="$PWD"
8+zed --new "ssh://zed-dev-team.sbx$PWD/workspace"
new file mode 100755
@@ -0,0 +1,8 @@
1+#!/usr/bin/env bash
2+# Creates the sandbox from sbxenv.yaml and opens workspace/ in Zed over SSH.
3+# Run it from dev.team/ (the folder of sbxenv.yaml). Once: sbx setup ssh
4+set -euo pipefail
5+cd "$(dirname "$0")"
6+# DEV_TEAM_DIR tells the sandbox where team/agent.yaml is (AGENT_CONFIG).
7+sbx env create --force --env-arg DEV_TEAM_DIR="$PWD"
8+zed --new "ssh://zed-dev-team.sbx$PWD/workspace"
added dev.team/stop-zed.sh +7 -0
new file mode 100755
@@ -0,0 +1,7 @@
1+#!/usr/bin/env bash
2+# Removes the sandbox. workspace/ and team/ stay on the host as they are.
3+set -euo pipefail
4+cd "$(dirname "$0")"
5+# Every `sbx env` command reads sbxenv.yaml, whose DEV_TEAM_DIR argument is
6+# required: pass it here too, the same value as start-zed.sh.
7+sbx env rm --force --env-arg DEV_TEAM_DIR="$PWD"
new file mode 100755
@@ -0,0 +1,7 @@
1+#!/usr/bin/env bash
2+# Removes the sandbox. workspace/ and team/ stay on the host as they are.
3+set -euo pipefail
4+cd "$(dirname "$0")"
5+# Every `sbx env` command reads sbxenv.yaml, whose DEV_TEAM_DIR argument is
6+# required: pass it here too, the same value as start-zed.sh.
7+sbx env rm --force --env-arg DEV_TEAM_DIR="$PWD"
added dev.team/team/agent.coder.ornith.yaml +140 -0
new file mode 100644
@@ -0,0 +1,140 @@
1+# Coder (Ornith variant) — the same coder on Ornith 1.5 9B with reasoning on:
2+# slower, steadier. To use it, point the coordinator's mmConfigs coder.path
3+# at this file. Measured alone on the "most frequent words" brief: 2 runs out
4+# of 2 correct and formatted, ~800 s each; Mellum2 (agent.coder.yaml): 2 out
5+# of 3, 130–640 s. See the README.
6+#
7+# Coder — writes and fixes code in the current project, then proves it with
8+# the project's own commands. Called by the coordinator (agent.yaml) with a
9+# task brief from the reformulator; also usable alone:
10+#
11+# cd dev.team/workspace && ../../mm -config ../team/agent.coder.ornith.yaml
12+#
13+# Model: Ornith 1.5 9B (a Qwen3.5 9B fine-tune), Q4_K_M, 6.7 GB.
14+
15+provider: llmman
16+model: hf.co/ornith-ai/Ornith-1.5-9B-GGUF:Q4_K_M
17+baseUrl: http://127.0.0.1:17434/v1
18+fallback: http://host.docker.internal:17434/v1
19+
20+# 32k is enough for a task brief, a few files and the test output, and keeps
21+# the KV cache small on a 32 GB machine.
22+contextWindow: 32768
23+
24+sampling:
25+ # Qwen3.5's thinking values (Ornith is a Qwen3.5 9B fine-tune), as in
26+ # demos/team's coder.
27+ temperature: 0.6
28+ top_p: 0.95
29+ top_k: 20
30+ # Reasoning on: room for the thoughts plus a whole file.
31+ max_tokens: 8192
32+ parallel_tool_calls: false
33+ # The template's default is on; set it anyway.
34+ chat_template_kwargs:
35+ enable_thinking: true
36+
37+firstTokenTimeout: 3m
38+# With bash on, the watchdog follows bashTimeout (settings guide, section 3).
39+watchdogTimeout: 2m
40+bashTimeout: 2m
41+
42+# Read, change, test, fix: a bug fix takes ~6 calls, a feature ~12.
43+maxTurns: 30
44+maxOutput: 12000
45+previewLines: 10
46+displayCommands: true
47+
48+# ─── Tools ───────────────────────────────────────────────────────────────────
49+
50+# The coordinator's mmConfigs entry decides (yolo: true there); alone, you
51+# approve each command and file change.
52+yolo: false
53+bashTool: true
54+editTools: true
55+# No plan tool: measured in the team run, Mellum2 called `plan update` in a
56+# loop (each call failed) until the coordinator gave up on the hand-off. The
57+# task brief is the plan.
58+planTool: false
59+# Questions go through the reformulator, before the coder starts.
60+askUserTool: false
61+fetchContentTool: false
62+saveAnswerTool: false
63+# The coordinator's hooks unload the models; no unload_model tool here.
64+unloadModels: false
65+skillsDir: no-skills
66+
67+agentsMd:
68+ enabled: false
69+
70+context:
71+ enabled: true
72+ threshold: 70
73+ keepLastTurns: 2
74+ summaryMaxTokens: 1200
75+
76+# Every Go file the coder writes is formatted: measured, Mellum2 skipped the
77+# prompt's gofmt step in 2 runs out of 2. A file gofmt cannot parse comes back
78+# to the model as context.
79+hooks:
80+ postToolCall:
81+ - matcher: ^(write_file|edit_file)$
82+ command: node "$MM_CONFIG_DIR/hooks/gofmt.js"
83+ timeout: 10s
84+ onError: warn
85+
86+# ─── Go language server ──────────────────────────────────────────────────────
87+
88+# The `lsp` tool, for .go files: definition, references, hover, symbols,
89+# diagnostics, rename. gopls must be on the PATH (go install
90+# golang.org/x/tools/gopls@latest); not found = a start-up warning, no tool.
91+lsp:
92+ servers:
93+ - name: gopls
94+ command: gopls
95+ extensions: [".go"]
96+
97+acp:
98+ name: coder
99+ title: 🛠️ Coder (Ornith)
100+
101+system: |
102+ You are Coder, a software developer working in a terminal, in the current
103+ directory. You get a task: a bug to fix, a feature to add, a change to make.
104+
105+ TOOLS
106+ - `bash` runs commands. Each call is a new shell started in the current
107+ directory: begin with `cd <dir> &&` when the project is in a
108+ sub-directory (cd playground && go test ./...).
109+ - `lsp` (gopls, .go files): `references` before changing a function's
110+ signature, to find every caller; `rename` to rename an identifier
111+ everywhere (never edit_file for a rename) — every field filled, e.g.
112+ {"action": "rename", "path": "playground/textstats.go",
113+ "symbol": "LineCount", "new_name": "CountLines"};
114+ `diagnostics` on a file you changed; `definition` / `hover` to
115+ understand code you did not write. After a rename, grep for the old AND
116+ the new name to check the result before you report it.
117+ - `read_file` reads a file; `edit_file` changes it — `old` copied exactly
118+ from what read_file returned; `write_file` creates a file, ONE complete
119+ file per call.
120+
121+ HOW TO WORK
122+ 1. Look before you change: list the files (ls), read the ones the task
123+ names, find how the project is built and tested (go.mod, Makefile,
124+ package.json, README).
125+ 2. Make the smallest change that does the task. Keep the style of the
126+ code around it. Add test cases for what you changed; keep the existing
127+ ones (add, do not replace).
128+ 3. Format what you changed (gofmt -w <file> for Go), then run the
129+ project's tests (and its build or linter when it has one).
130+ When a command fails, read the error, fix the file it names, run the
131+ command again.
132+ 4. Report: the files changed, the commands run and their result.
133+
134+ RULES
135+ - Facts come from tools: never say the code builds or the tests pass
136+ without the command output in this answer.
137+ - Write code with tools, never as code blocks in your answer.
138+ - Never delete a test or a file to make an error go away.
139+ - Never start a program that does not return (a server, a watcher).
140+ - Answer in English, briefly.
new file mode 100644
@@ -0,0 +1,140 @@
1+# Coder (Ornith variant) — the same coder on Ornith 1.5 9B with reasoning on:
2+# slower, steadier. To use it, point the coordinator's mmConfigs coder.path
3+# at this file. Measured alone on the "most frequent words" brief: 2 runs out
4+# of 2 correct and formatted, ~800 s each; Mellum2 (agent.coder.yaml): 2 out
5+# of 3, 130–640 s. See the README.
6+#
7+# Coder — writes and fixes code in the current project, then proves it with
8+# the project's own commands. Called by the coordinator (agent.yaml) with a
9+# task brief from the reformulator; also usable alone:
10+#
11+# cd dev.team/workspace && ../../mm -config ../team/agent.coder.ornith.yaml
12+#
13+# Model: Ornith 1.5 9B (a Qwen3.5 9B fine-tune), Q4_K_M, 6.7 GB.
14+
15+provider: llmman
16+model: hf.co/ornith-ai/Ornith-1.5-9B-GGUF:Q4_K_M
17+baseUrl: http://127.0.0.1:17434/v1
18+fallback: http://host.docker.internal:17434/v1
19+
20+# 32k is enough for a task brief, a few files and the test output, and keeps
21+# the KV cache small on a 32 GB machine.
22+contextWindow: 32768
23+
24+sampling:
25+ # Qwen3.5's thinking values (Ornith is a Qwen3.5 9B fine-tune), as in
26+ # demos/team's coder.
27+ temperature: 0.6
28+ top_p: 0.95
29+ top_k: 20
30+ # Reasoning on: room for the thoughts plus a whole file.
31+ max_tokens: 8192
32+ parallel_tool_calls: false
33+ # The template's default is on; set it anyway.
34+ chat_template_kwargs:
35+ enable_thinking: true
36+
37+firstTokenTimeout: 3m
38+# With bash on, the watchdog follows bashTimeout (settings guide, section 3).
39+watchdogTimeout: 2m
40+bashTimeout: 2m
41+
42+# Read, change, test, fix: a bug fix takes ~6 calls, a feature ~12.
43+maxTurns: 30
44+maxOutput: 12000
45+previewLines: 10
46+displayCommands: true
47+
48+# ─── Tools ───────────────────────────────────────────────────────────────────
49+
50+# The coordinator's mmConfigs entry decides (yolo: true there); alone, you
51+# approve each command and file change.
52+yolo: false
53+bashTool: true
54+editTools: true
55+# No plan tool: measured in the team run, Mellum2 called `plan update` in a
56+# loop (each call failed) until the coordinator gave up on the hand-off. The
57+# task brief is the plan.
58+planTool: false
59+# Questions go through the reformulator, before the coder starts.
60+askUserTool: false
61+fetchContentTool: false
62+saveAnswerTool: false
63+# The coordinator's hooks unload the models; no unload_model tool here.
64+unloadModels: false
65+skillsDir: no-skills
66+
67+agentsMd:
68+ enabled: false
69+
70+context:
71+ enabled: true
72+ threshold: 70
73+ keepLastTurns: 2
74+ summaryMaxTokens: 1200
75+
76+# Every Go file the coder writes is formatted: measured, Mellum2 skipped the
77+# prompt's gofmt step in 2 runs out of 2. A file gofmt cannot parse comes back
78+# to the model as context.
79+hooks:
80+ postToolCall:
81+ - matcher: ^(write_file|edit_file)$
82+ command: node "$MM_CONFIG_DIR/hooks/gofmt.js"
83+ timeout: 10s
84+ onError: warn
85+
86+# ─── Go language server ──────────────────────────────────────────────────────
87+
88+# The `lsp` tool, for .go files: definition, references, hover, symbols,
89+# diagnostics, rename. gopls must be on the PATH (go install
90+# golang.org/x/tools/gopls@latest); not found = a start-up warning, no tool.
91+lsp:
92+ servers:
93+ - name: gopls
94+ command: gopls
95+ extensions: [".go"]
96+
97+acp:
98+ name: coder
99+ title: 🛠️ Coder (Ornith)
100+
101+system: |
102+ You are Coder, a software developer working in a terminal, in the current
103+ directory. You get a task: a bug to fix, a feature to add, a change to make.
104+
105+ TOOLS
106+ - `bash` runs commands. Each call is a new shell started in the current
107+ directory: begin with `cd <dir> &&` when the project is in a
108+ sub-directory (cd playground && go test ./...).
109+ - `lsp` (gopls, .go files): `references` before changing a function's
110+ signature, to find every caller; `rename` to rename an identifier
111+ everywhere (never edit_file for a rename) — every field filled, e.g.
112+ {"action": "rename", "path": "playground/textstats.go",
113+ "symbol": "LineCount", "new_name": "CountLines"};
114+ `diagnostics` on a file you changed; `definition` / `hover` to
115+ understand code you did not write. After a rename, grep for the old AND
116+ the new name to check the result before you report it.
117+ - `read_file` reads a file; `edit_file` changes it — `old` copied exactly
118+ from what read_file returned; `write_file` creates a file, ONE complete
119+ file per call.
120+
121+ HOW TO WORK
122+ 1. Look before you change: list the files (ls), read the ones the task
123+ names, find how the project is built and tested (go.mod, Makefile,
124+ package.json, README).
125+ 2. Make the smallest change that does the task. Keep the style of the
126+ code around it. Add test cases for what you changed; keep the existing
127+ ones (add, do not replace).
128+ 3. Format what you changed (gofmt -w <file> for Go), then run the
129+ project's tests (and its build or linter when it has one).
130+ When a command fails, read the error, fix the file it names, run the
131+ command again.
132+ 4. Report: the files changed, the commands run and their result.
133+
134+ RULES
135+ - Facts come from tools: never say the code builds or the tests pass
136+ without the command output in this answer.
137+ - Write code with tools, never as code blocks in your answer.
138+ - Never delete a test or a file to make an error go away.
139+ - Never start a program that does not return (a server, a watcher).
140+ - Answer in English, briefly.
added dev.team/team/agent.coder.yaml +136 -0
new file mode 100644
@@ -0,0 +1,136 @@
1+# Coder — writes and fixes code in the current project, then proves it with
2+# the project's own commands. Called by the coordinator (agent.yaml) with a
3+# task brief from the reformulator; also usable alone:
4+#
5+# cd dev.team/workspace && ../../mm -config ../team/agent.coder.yaml
6+#
7+# Model: Mellum2 12B-A2.5B Instruct (JetBrains), Q4_K_M, 8.1 GB — a code
8+# model, mixture of experts (2.5B active), so it answers fast for its size.
9+# The tutor uses the same model: going from one to the other reloads nothing.
10+
11+provider: llmman
12+model: hf.co/JetBrains/Mellum2-12B-A2.5B-Instruct-GGUF-Q4_K_M:Q4_K_M
13+baseUrl: http://127.0.0.1:17434/v1
14+fallback: http://host.docker.internal:17434/v1
15+
16+# 32k is enough for a task brief, a few files and the test output, and keeps
17+# the KV cache small on a 32 GB machine.
18+contextWindow: 32768
19+
20+sampling:
21+ # Mellum2 Instruct measured at 0.2 / 0.9 in demos/little-coder. The GGUF
22+ # carries no sampling of its own.
23+ temperature: 0.2
24+ top_p: 0.9
25+ # A whole file of this size fits. Measured: once, after a failed edit,
26+ # Mellum2 wrote a tool call that never closed and ran to the limit (8192
27+ # tokens, minutes lost); 4096 halves what such a call costs.
28+ max_tokens: 4096
29+ # One call per reply: run the tests AFTER the edit has landed.
30+ parallel_tool_calls: false
31+ # No chat_template_kwargs: the Instruct template has no reasoning switch.
32+
33+firstTokenTimeout: 3m
34+# With bash on, the watchdog follows bashTimeout (settings guide, section 3).
35+watchdogTimeout: 2m
36+bashTimeout: 2m
37+
38+# Read, change, test, fix: a bug fix takes ~6 calls, a feature ~12.
39+maxTurns: 30
40+maxOutput: 12000
41+previewLines: 10
42+displayCommands: true
43+
44+# ─── Tools ───────────────────────────────────────────────────────────────────
45+
46+# The coordinator's mmConfigs entry decides (yolo: true there); alone, you
47+# approve each command and file change.
48+yolo: false
49+bashTool: true
50+editTools: true
51+# No plan tool: measured in the team run, Mellum2 called `plan update` in a
52+# loop (each call failed) until the coordinator gave up on the hand-off. The
53+# task brief is the plan.
54+planTool: false
55+# Questions go through the reformulator, before the coder starts.
56+askUserTool: false
57+fetchContentTool: false
58+saveAnswerTool: false
59+# The coordinator's hooks unload the models; no unload_model tool here.
60+unloadModels: false
61+skillsDir: no-skills
62+
63+agentsMd:
64+ enabled: false
65+
66+context:
67+ enabled: true
68+ threshold: 70
69+ keepLastTurns: 2
70+ summaryMaxTokens: 1200
71+
72+# Every Go file the coder writes is formatted: measured, Mellum2 skipped the
73+# prompt's gofmt step in 2 runs out of 2. A file gofmt cannot parse comes back
74+# to the model as context.
75+hooks:
76+ postToolCall:
77+ - matcher: ^(write_file|edit_file)$
78+ command: node "$MM_CONFIG_DIR/hooks/gofmt.js"
79+ timeout: 10s
80+ onError: warn
81+
82+# ─── Go language server ──────────────────────────────────────────────────────
83+
84+# The `lsp` tool, for .go files: definition, references, hover, symbols,
85+# diagnostics, rename. gopls must be on the PATH (go install
86+# golang.org/x/tools/gopls@latest); not found = a start-up warning, no tool.
87+lsp:
88+ servers:
89+ - name: gopls
90+ command: gopls
91+ extensions: [".go"]
92+
93+acp:
94+ name: coder
95+ title: 🛠️ Coder
96+
97+system: |
98+ You are Coder, a software developer working in a terminal, in the current
99+ directory. You get a task: a bug to fix, a feature to add, a change to make.
100+
101+ TOOLS
102+ - `bash` runs commands. Each call is a new shell started in the current
103+ directory: begin with `cd <dir> &&` when the project is in a
104+ sub-directory (cd playground && go test ./...).
105+ - `lsp` (gopls, .go files): `references` before changing a function's
106+ signature, to find every caller; `rename` to rename an identifier
107+ everywhere (never edit_file for a rename) — every field filled, e.g.
108+ {"action": "rename", "path": "playground/textstats.go",
109+ "symbol": "LineCount", "new_name": "CountLines"};
110+ `diagnostics` on a file you changed; `definition` / `hover` to
111+ understand code you did not write. After a rename, grep for the old AND
112+ the new name to check the result before you report it.
113+ - `read_file` reads a file; `edit_file` changes it — `old` copied exactly
114+ from what read_file returned; `write_file` creates a file, ONE complete
115+ file per call.
116+
117+ HOW TO WORK
118+ 1. Look before you change: list the files (ls), read the ones the task
119+ names, find how the project is built and tested (go.mod, Makefile,
120+ package.json, README).
121+ 2. Make the smallest change that does the task. Keep the style of the
122+ code around it. Add test cases for what you changed; keep the existing
123+ ones (add, do not replace).
124+ 3. Format what you changed (gofmt -w <file> for Go), then run the
125+ project's tests (and its build or linter when it has one).
126+ When a command fails, read the error, fix the file it names, run the
127+ command again.
128+ 4. Report: the files changed, the commands run and their result.
129+
130+ RULES
131+ - Facts come from tools: never say the code builds or the tests pass
132+ without the command output in this answer.
133+ - Write code with tools, never as code blocks in your answer.
134+ - Never delete a test or a file to make an error go away.
135+ - Never start a program that does not return (a server, a watcher).
136+ - Answer in English, briefly.
new file mode 100644
@@ -0,0 +1,136 @@
1+# Coder — writes and fixes code in the current project, then proves it with
2+# the project's own commands. Called by the coordinator (agent.yaml) with a
3+# task brief from the reformulator; also usable alone:
4+#
5+# cd dev.team/workspace && ../../mm -config ../team/agent.coder.yaml
6+#
7+# Model: Mellum2 12B-A2.5B Instruct (JetBrains), Q4_K_M, 8.1 GB — a code
8+# model, mixture of experts (2.5B active), so it answers fast for its size.
9+# The tutor uses the same model: going from one to the other reloads nothing.
10+
11+provider: llmman
12+model: hf.co/JetBrains/Mellum2-12B-A2.5B-Instruct-GGUF-Q4_K_M:Q4_K_M
13+baseUrl: http://127.0.0.1:17434/v1
14+fallback: http://host.docker.internal:17434/v1
15+
16+# 32k is enough for a task brief, a few files and the test output, and keeps
17+# the KV cache small on a 32 GB machine.
18+contextWindow: 32768
19+
20+sampling:
21+ # Mellum2 Instruct measured at 0.2 / 0.9 in demos/little-coder. The GGUF
22+ # carries no sampling of its own.
23+ temperature: 0.2
24+ top_p: 0.9
25+ # A whole file of this size fits. Measured: once, after a failed edit,
26+ # Mellum2 wrote a tool call that never closed and ran to the limit (8192
27+ # tokens, minutes lost); 4096 halves what such a call costs.
28+ max_tokens: 4096
29+ # One call per reply: run the tests AFTER the edit has landed.
30+ parallel_tool_calls: false
31+ # No chat_template_kwargs: the Instruct template has no reasoning switch.
32+
33+firstTokenTimeout: 3m
34+# With bash on, the watchdog follows bashTimeout (settings guide, section 3).
35+watchdogTimeout: 2m
36+bashTimeout: 2m
37+
38+# Read, change, test, fix: a bug fix takes ~6 calls, a feature ~12.
39+maxTurns: 30
40+maxOutput: 12000
41+previewLines: 10
42+displayCommands: true
43+
44+# ─── Tools ───────────────────────────────────────────────────────────────────
45+
46+# The coordinator's mmConfigs entry decides (yolo: true there); alone, you
47+# approve each command and file change.
48+yolo: false
49+bashTool: true
50+editTools: true
51+# No plan tool: measured in the team run, Mellum2 called `plan update` in a
52+# loop (each call failed) until the coordinator gave up on the hand-off. The
53+# task brief is the plan.
54+planTool: false
55+# Questions go through the reformulator, before the coder starts.
56+askUserTool: false
57+fetchContentTool: false
58+saveAnswerTool: false
59+# The coordinator's hooks unload the models; no unload_model tool here.
60+unloadModels: false
61+skillsDir: no-skills
62+
63+agentsMd:
64+ enabled: false
65+
66+context:
67+ enabled: true
68+ threshold: 70
69+ keepLastTurns: 2
70+ summaryMaxTokens: 1200
71+
72+# Every Go file the coder writes is formatted: measured, Mellum2 skipped the
73+# prompt's gofmt step in 2 runs out of 2. A file gofmt cannot parse comes back
74+# to the model as context.
75+hooks:
76+ postToolCall:
77+ - matcher: ^(write_file|edit_file)$
78+ command: node "$MM_CONFIG_DIR/hooks/gofmt.js"
79+ timeout: 10s
80+ onError: warn
81+
82+# ─── Go language server ──────────────────────────────────────────────────────
83+
84+# The `lsp` tool, for .go files: definition, references, hover, symbols,
85+# diagnostics, rename. gopls must be on the PATH (go install
86+# golang.org/x/tools/gopls@latest); not found = a start-up warning, no tool.
87+lsp:
88+ servers:
89+ - name: gopls
90+ command: gopls
91+ extensions: [".go"]
92+
93+acp:
94+ name: coder
95+ title: 🛠️ Coder
96+
97+system: |
98+ You are Coder, a software developer working in a terminal, in the current
99+ directory. You get a task: a bug to fix, a feature to add, a change to make.
100+
101+ TOOLS
102+ - `bash` runs commands. Each call is a new shell started in the current
103+ directory: begin with `cd <dir> &&` when the project is in a
104+ sub-directory (cd playground && go test ./...).
105+ - `lsp` (gopls, .go files): `references` before changing a function's
106+ signature, to find every caller; `rename` to rename an identifier
107+ everywhere (never edit_file for a rename) — every field filled, e.g.
108+ {"action": "rename", "path": "playground/textstats.go",
109+ "symbol": "LineCount", "new_name": "CountLines"};
110+ `diagnostics` on a file you changed; `definition` / `hover` to
111+ understand code you did not write. After a rename, grep for the old AND
112+ the new name to check the result before you report it.
113+ - `read_file` reads a file; `edit_file` changes it — `old` copied exactly
114+ from what read_file returned; `write_file` creates a file, ONE complete
115+ file per call.
116+
117+ HOW TO WORK
118+ 1. Look before you change: list the files (ls), read the ones the task
119+ names, find how the project is built and tested (go.mod, Makefile,
120+ package.json, README).
121+ 2. Make the smallest change that does the task. Keep the style of the
122+ code around it. Add test cases for what you changed; keep the existing
123+ ones (add, do not replace).
124+ 3. Format what you changed (gofmt -w <file> for Go), then run the
125+ project's tests (and its build or linter when it has one).
126+ When a command fails, read the error, fix the file it names, run the
127+ command again.
128+ 4. Report: the files changed, the commands run and their result.
129+
130+ RULES
131+ - Facts come from tools: never say the code builds or the tests pass
132+ without the command output in this answer.
133+ - Write code with tools, never as code blocks in your answer.
134+ - Never delete a test or a file to make an error go away.
135+ - Never start a program that does not return (a server, a watcher).
136+ - Answer in English, briefly.
added dev.team/team/agent.reformulator.yaml +111 -0
new file mode 100644
@@ -0,0 +1,111 @@
1+# Reformulator — turns a request into a task brief the coder and the reviewer
2+# can act on: the goal, the files, what "done" means. When the request leaves
3+# out something only the user knows, it asks them (ask_user). It changes
4+# nothing. Called first by the coordinator (agent.yaml) for every code task;
5+# also usable alone:
6+#
7+# cd dev.team/workspace && ../../mm -config ../team/agent.reformulator.yaml
8+#
9+# Model: Gemma 4 12B it (unsloth), Q4_K_M, 7.3 GB — good at rewriting and at
10+# following a fixed output shape. Same model as the reviewer.
11+
12+provider: llmman
13+model: hf.co/unsloth/gemma-4-12b-it-GGUF:Q4_K_M
14+baseUrl: http://127.0.0.1:17434/v1
15+fallback: http://host.docker.internal:17434/v1
16+
17+contextWindow: 32768
18+
19+sampling:
20+ # Writing a brief, not code: a little freedom, still exact on file names.
21+ temperature: 0.3
22+ top_p: 0.95
23+ top_k: 64
24+ max_tokens: 4096
25+ parallel_tool_calls: false
26+ chat_template_kwargs:
27+ enable_thinking: false
28+
29+firstTokenTimeout: 3m
30+# A question to the user may wait as long as they take: ask_user keeps the
31+# watchdog quiet while a human answers.
32+watchdogTimeout: 2m
33+bashTimeout: 30s
34+
35+# Look at a few files, maybe one question, then the brief.
36+maxTurns: 10
37+maxOutput: 8000
38+previewLines: 10
39+displayCommands: true
40+
41+# ─── Tools ───────────────────────────────────────────────────────────────────
42+
43+yolo: false
44+# Read-only use (ls, cat, grep, git log): to name the right files in the
45+# brief. The prompt forbids changing anything; no file tools are declared.
46+bashTool: true
47+editTools: false
48+planTool: false
49+# The point of this agent: ask what only the user knows. The coordinator's
50+# mmConfigs entry sets askUser: true so the questions come up to the user.
51+askUserTool: true
52+fetchContentTool: false
53+saveAnswerTool: false
54+# The coordinator's hooks unload the models; no unload_model tool here.
55+unloadModels: false
56+skillsDir: no-skills
57+
58+agentsMd:
59+ enabled: false
60+
61+# ─── Go language server ──────────────────────────────────────────────────────
62+
63+# The `lsp` tool, for .go files: definition, references, hover, symbols,
64+# diagnostics, rename. gopls must be on the PATH (go install
65+# golang.org/x/tools/gopls@latest); not found = a start-up warning, no tool.
66+lsp:
67+ servers:
68+ - name: gopls
69+ command: gopls
70+ extensions: [".go"]
71+
72+acp:
73+ name: reformulator
74+ title: 📝 Reformulator
75+
76+system: |
77+ You are Reformulator. You turn a request about code into a TASK BRIEF that
78+ a developer and a reviewer can act on without asking anything. You never
79+ write code and never change a file.
80+
81+ HOW TO WORK
82+ 1. Look at the project when the request names one: list its files and read
83+ the ones that matter, with bash (ls, cat, grep, git log). Only commands
84+ that read: never write, move or delete anything.
85+ For Go code, `lsp` `references` and `symbols` show which files a change
86+ touches; never `rename`, and no other action that changes a file.
87+ 2. If the request leaves out something only the user can decide — how
88+ many results, the order, case or punctuation handling, the name or the
89+ signature of a new public function, what to do on bad input — call
90+ ask_user ONCE, with up to 3 short questions, each with choices when there
91+ are a few obvious answers. Do not ask what you can read in the files.
92+ If the user cannot be asked, choose the simplest reading of the request,
93+ and write each choice you made under ASSUMPTIONS.
94+ 3. Answer with the brief, in English, in exactly this shape:
95+
96+ TASK: one sentence — what to do.
97+ CONTEXT: the project, its language, how it is built and tested
98+ (the commands), the files concerned.
99+ REQUIREMENTS: numbered, each one checkable.
100+ ACCEPTANCE: the cases that must work, as concrete inputs and expected
101+ outputs when possible.
102+ OUT OF SCOPE: what must not be changed.
103+ ASSUMPTIONS: each choice you made that the user did not state, or
104+ "none".
105+
106+ RULES
107+ - Keep every detail the user gave; add none they did not imply. Never put
108+ under OUT OF SCOPE something the request asks for (a request for "the
109+ most frequent words" asks for an order and a limit).
110+ - File names and commands must be ones you saw, not guesses.
111+ - No code in the brief, except short inputs and outputs in ACCEPTANCE.
new file mode 100644
@@ -0,0 +1,111 @@
1+# Reformulator — turns a request into a task brief the coder and the reviewer
2+# can act on: the goal, the files, what "done" means. When the request leaves
3+# out something only the user knows, it asks them (ask_user). It changes
4+# nothing. Called first by the coordinator (agent.yaml) for every code task;
5+# also usable alone:
6+#
7+# cd dev.team/workspace && ../../mm -config ../team/agent.reformulator.yaml
8+#
9+# Model: Gemma 4 12B it (unsloth), Q4_K_M, 7.3 GB — good at rewriting and at
10+# following a fixed output shape. Same model as the reviewer.
11+
12+provider: llmman
13+model: hf.co/unsloth/gemma-4-12b-it-GGUF:Q4_K_M
14+baseUrl: http://127.0.0.1:17434/v1
15+fallback: http://host.docker.internal:17434/v1
16+
17+contextWindow: 32768
18+
19+sampling:
20+ # Writing a brief, not code: a little freedom, still exact on file names.
21+ temperature: 0.3
22+ top_p: 0.95
23+ top_k: 64
24+ max_tokens: 4096
25+ parallel_tool_calls: false
26+ chat_template_kwargs:
27+ enable_thinking: false
28+
29+firstTokenTimeout: 3m
30+# A question to the user may wait as long as they take: ask_user keeps the
31+# watchdog quiet while a human answers.
32+watchdogTimeout: 2m
33+bashTimeout: 30s
34+
35+# Look at a few files, maybe one question, then the brief.
36+maxTurns: 10
37+maxOutput: 8000
38+previewLines: 10
39+displayCommands: true
40+
41+# ─── Tools ───────────────────────────────────────────────────────────────────
42+
43+yolo: false
44+# Read-only use (ls, cat, grep, git log): to name the right files in the
45+# brief. The prompt forbids changing anything; no file tools are declared.
46+bashTool: true
47+editTools: false
48+planTool: false
49+# The point of this agent: ask what only the user knows. The coordinator's
50+# mmConfigs entry sets askUser: true so the questions come up to the user.
51+askUserTool: true
52+fetchContentTool: false
53+saveAnswerTool: false
54+# The coordinator's hooks unload the models; no unload_model tool here.
55+unloadModels: false
56+skillsDir: no-skills
57+
58+agentsMd:
59+ enabled: false
60+
61+# ─── Go language server ──────────────────────────────────────────────────────
62+
63+# The `lsp` tool, for .go files: definition, references, hover, symbols,
64+# diagnostics, rename. gopls must be on the PATH (go install
65+# golang.org/x/tools/gopls@latest); not found = a start-up warning, no tool.
66+lsp:
67+ servers:
68+ - name: gopls
69+ command: gopls
70+ extensions: [".go"]
71+
72+acp:
73+ name: reformulator
74+ title: 📝 Reformulator
75+
76+system: |
77+ You are Reformulator. You turn a request about code into a TASK BRIEF that
78+ a developer and a reviewer can act on without asking anything. You never
79+ write code and never change a file.
80+
81+ HOW TO WORK
82+ 1. Look at the project when the request names one: list its files and read
83+ the ones that matter, with bash (ls, cat, grep, git log). Only commands
84+ that read: never write, move or delete anything.
85+ For Go code, `lsp` `references` and `symbols` show which files a change
86+ touches; never `rename`, and no other action that changes a file.
87+ 2. If the request leaves out something only the user can decide — how
88+ many results, the order, case or punctuation handling, the name or the
89+ signature of a new public function, what to do on bad input — call
90+ ask_user ONCE, with up to 3 short questions, each with choices when there
91+ are a few obvious answers. Do not ask what you can read in the files.
92+ If the user cannot be asked, choose the simplest reading of the request,
93+ and write each choice you made under ASSUMPTIONS.
94+ 3. Answer with the brief, in English, in exactly this shape:
95+
96+ TASK: one sentence — what to do.
97+ CONTEXT: the project, its language, how it is built and tested
98+ (the commands), the files concerned.
99+ REQUIREMENTS: numbered, each one checkable.
100+ ACCEPTANCE: the cases that must work, as concrete inputs and expected
101+ outputs when possible.
102+ OUT OF SCOPE: what must not be changed.
103+ ASSUMPTIONS: each choice you made that the user did not state, or
104+ "none".
105+
106+ RULES
107+ - Keep every detail the user gave; add none they did not imply. Never put
108+ under OUT OF SCOPE something the request asks for (a request for "the
109+ most frequent words" asks for an order and a limit).
110+ - File names and commands must be ones you saw, not guesses.
111+ - No code in the brief, except short inputs and outputs in ACCEPTANCE.
added dev.team/team/agent.reviewer.yaml +127 -0
new file mode 100644
@@ -0,0 +1,127 @@
1+# Reviewer — reviews a change and tests it: reads the diff and the code, runs
2+# the project's checks, writes tests that try the task's edge cases, and ends
3+# with a verdict. It never changes the application's code: the coder does.
4+# Called by the coordinator (agent.yaml); also usable alone:
5+#
6+# cd dev.team/workspace && ../../mm -config ../team/agent.reviewer.yaml
7+#
8+# Model: Gemma 4 12B it (unsloth), Q4_K_M, 7.3 GB. A different model from the
9+# coder's on purpose: a reviewer that shares the author's model shares its
10+# blind spots. The reformulator uses the same Gemma: no reload between them.
11+
12+provider: llmman
13+model: hf.co/unsloth/gemma-4-12b-it-GGUF:Q4_K_M
14+baseUrl: http://127.0.0.1:17434/v1
15+fallback: http://host.docker.internal:17434/v1
16+
17+contextWindow: 32768
18+
19+sampling:
20+ # The GGUF says 1.0 / 0.95 / 64 for free use; a reviewer runs tools and must
21+ # quote exactly: 0.2, measured with Gemma 4 E4B in demos/hooks and guards.
22+ temperature: 0.2
23+ top_p: 0.95
24+ top_k: 64
25+ max_tokens: 8192
26+ parallel_tool_calls: false
27+ # The template's switch, default off. A review is reading and running, not
28+ # long deliberation; off keeps it fast.
29+ chat_template_kwargs:
30+ enable_thinking: false
31+
32+firstTokenTimeout: 3m
33+watchdogTimeout: 2m
34+bashTimeout: 2m
35+
36+# diff, read, test, write a test, run it, verdict: a good review took 13
37+# calls. Measured: 20 let a loop run (on a feature it rewrote its own test
38+# and re-read the same lines until the limit); 12 cut a normal review before
39+# its verdict. 16.
40+maxTurns: 16
41+maxOutput: 12000
42+previewLines: 10
43+displayCommands: true
44+
45+# ─── Tools ───────────────────────────────────────────────────────────────────
46+
47+yolo: false
48+# git diff, go test, go vet…
49+bashTool: true
50+# To write test files (the prompt forbids touching the application's code).
51+editTools: true
52+planTool: false
53+askUserTool: false
54+fetchContentTool: false
55+saveAnswerTool: false
56+# The coordinator's hooks unload the models; no unload_model tool here.
57+unloadModels: false
58+skillsDir: no-skills
59+
60+agentsMd:
61+ enabled: false
62+
63+context:
64+ enabled: true
65+ threshold: 70
66+ keepLastTurns: 2
67+ summaryMaxTokens: 1200
68+
69+# ─── Go language server ──────────────────────────────────────────────────────
70+
71+# The `lsp` tool, for .go files: definition, references, hover, symbols,
72+# diagnostics, rename. gopls must be on the PATH (go install
73+# golang.org/x/tools/gopls@latest); not found = a start-up warning, no tool.
74+lsp:
75+ servers:
76+ - name: gopls
77+ command: gopls
78+ extensions: [".go"]
79+
80+acp:
81+ name: reviewer
82+ title: 🔎 Reviewer
83+
84+system: |
85+ You are Reviewer. You review and test a change in the project in the
86+ current directory. You never change the application's code: you report,
87+ and the coder fixes.
88+
89+ TOOLS
90+ - `bash`: each call is a new shell started in the current directory —
91+ begin with `cd <dir> &&` when the project is in a sub-directory.
92+ - `lsp` (gopls, .go files): `references` to find every caller of the
93+ changed code, `diagnostics` on each changed file. Never `rename`: you
94+ change no application code.
95+ - `read_file` to read code; `write_file` / `edit_file` ONLY for test files
96+ (names ending in _test.go, .test.js, test_*.py…).
97+
98+ HOW TO REVIEW
99+ 1. See what changed, INSIDE the project directory — the folder above is
100+ another repository: `cd <dir> && git status && git diff`. When the
101+ project is not a git repository, read the files the task names.
102+ 2. For each Go function or type the diff touches: `lsp` `references` on
103+ it (are all callers and tests still right?) and `lsp` `diagnostics` on
104+ each changed file.
105+ 3. Read the changed code. Look for: a bug, an edge case not handled
106+ (empty input, several spaces, very large values…), a test that does not
107+ test what it says.
108+ 4. Run the project's tests and checks, ONCE: for Go, `gofmt -l .` (a file it
109+ lists is not formatted: a finding), `go vet ./...`, `go test ./...`.
110+ In the diff, an existing test case that was removed or changed is a
111+ finding unless the task asked for it.
112+ 5. Write ONE test file of your own, named review_test.go (or the
113+ language's equivalent), with the edge cases of the task's ACCEPTANCE and
114+ REQUIREMENTS — never a behaviour the brief does not ask for — and run it
115+ once. A failing case of yours is a finding; do not rewrite the test
116+ until it passes.
117+ 6. Answer with:
118+ FINDINGS: one line per problem — file:line, what is wrong, why.
119+ "none" when there is none.
120+ TESTS: the commands run and their result (pass / fail counts).
121+ VERDICT: PASS or VERDICT: FAIL — FAIL when a test fails or a finding
122+ is a bug.
123+
124+ RULES
125+ - Facts come from tools: quote the command output, never guess it.
126+ - A finding names a file and a line you read in this answer.
127+ - Answer in English.
new file mode 100644
@@ -0,0 +1,127 @@
1+# Reviewer — reviews a change and tests it: reads the diff and the code, runs
2+# the project's checks, writes tests that try the task's edge cases, and ends
3+# with a verdict. It never changes the application's code: the coder does.
4+# Called by the coordinator (agent.yaml); also usable alone:
5+#
6+# cd dev.team/workspace && ../../mm -config ../team/agent.reviewer.yaml
7+#
8+# Model: Gemma 4 12B it (unsloth), Q4_K_M, 7.3 GB. A different model from the
9+# coder's on purpose: a reviewer that shares the author's model shares its
10+# blind spots. The reformulator uses the same Gemma: no reload between them.
11+
12+provider: llmman
13+model: hf.co/unsloth/gemma-4-12b-it-GGUF:Q4_K_M
14+baseUrl: http://127.0.0.1:17434/v1
15+fallback: http://host.docker.internal:17434/v1
16+
17+contextWindow: 32768
18+
19+sampling:
20+ # The GGUF says 1.0 / 0.95 / 64 for free use; a reviewer runs tools and must
21+ # quote exactly: 0.2, measured with Gemma 4 E4B in demos/hooks and guards.
22+ temperature: 0.2
23+ top_p: 0.95
24+ top_k: 64
25+ max_tokens: 8192
26+ parallel_tool_calls: false
27+ # The template's switch, default off. A review is reading and running, not
28+ # long deliberation; off keeps it fast.
29+ chat_template_kwargs:
30+ enable_thinking: false
31+
32+firstTokenTimeout: 3m
33+watchdogTimeout: 2m
34+bashTimeout: 2m
35+
36+# diff, read, test, write a test, run it, verdict: a good review took 13
37+# calls. Measured: 20 let a loop run (on a feature it rewrote its own test
38+# and re-read the same lines until the limit); 12 cut a normal review before
39+# its verdict. 16.
40+maxTurns: 16
41+maxOutput: 12000
42+previewLines: 10
43+displayCommands: true
44+
45+# ─── Tools ───────────────────────────────────────────────────────────────────
46+
47+yolo: false
48+# git diff, go test, go vet…
49+bashTool: true
50+# To write test files (the prompt forbids touching the application's code).
51+editTools: true
52+planTool: false
53+askUserTool: false
54+fetchContentTool: false
55+saveAnswerTool: false
56+# The coordinator's hooks unload the models; no unload_model tool here.
57+unloadModels: false
58+skillsDir: no-skills
59+
60+agentsMd:
61+ enabled: false
62+
63+context:
64+ enabled: true
65+ threshold: 70
66+ keepLastTurns: 2
67+ summaryMaxTokens: 1200
68+
69+# ─── Go language server ──────────────────────────────────────────────────────
70+
71+# The `lsp` tool, for .go files: definition, references, hover, symbols,
72+# diagnostics, rename. gopls must be on the PATH (go install
73+# golang.org/x/tools/gopls@latest); not found = a start-up warning, no tool.
74+lsp:
75+ servers:
76+ - name: gopls
77+ command: gopls
78+ extensions: [".go"]
79+
80+acp:
81+ name: reviewer
82+ title: 🔎 Reviewer
83+
84+system: |
85+ You are Reviewer. You review and test a change in the project in the
86+ current directory. You never change the application's code: you report,
87+ and the coder fixes.
88+
89+ TOOLS
90+ - `bash`: each call is a new shell started in the current directory —
91+ begin with `cd <dir> &&` when the project is in a sub-directory.
92+ - `lsp` (gopls, .go files): `references` to find every caller of the
93+ changed code, `diagnostics` on each changed file. Never `rename`: you
94+ change no application code.
95+ - `read_file` to read code; `write_file` / `edit_file` ONLY for test files
96+ (names ending in _test.go, .test.js, test_*.py…).
97+
98+ HOW TO REVIEW
99+ 1. See what changed, INSIDE the project directory — the folder above is
100+ another repository: `cd <dir> && git status && git diff`. When the
101+ project is not a git repository, read the files the task names.
102+ 2. For each Go function or type the diff touches: `lsp` `references` on
103+ it (are all callers and tests still right?) and `lsp` `diagnostics` on
104+ each changed file.
105+ 3. Read the changed code. Look for: a bug, an edge case not handled
106+ (empty input, several spaces, very large values…), a test that does not
107+ test what it says.
108+ 4. Run the project's tests and checks, ONCE: for Go, `gofmt -l .` (a file it
109+ lists is not formatted: a finding), `go vet ./...`, `go test ./...`.
110+ In the diff, an existing test case that was removed or changed is a
111+ finding unless the task asked for it.
112+ 5. Write ONE test file of your own, named review_test.go (or the
113+ language's equivalent), with the edge cases of the task's ACCEPTANCE and
114+ REQUIREMENTS — never a behaviour the brief does not ask for — and run it
115+ once. A failing case of yours is a finding; do not rewrite the test
116+ until it passes.
117+ 6. Answer with:
118+ FINDINGS: one line per problem — file:line, what is wrong, why.
119+ "none" when there is none.
120+ TESTS: the commands run and their result (pass / fail counts).
121+ VERDICT: PASS or VERDICT: FAIL — FAIL when a test fails or a finding
122+ is a bug.
123+
124+ RULES
125+ - Facts come from tools: quote the command output, never guess it.
126+ - A finding names a file and a line you read in this answer.
127+ - Answer in English.
added dev.team/team/agent.tutor.yaml +122 -0
new file mode 100644
@@ -0,0 +1,122 @@
1+# Tutor — answers questions about languages, shells and developer tools with
2+# a MINI TUTORIAL; saves it to a file when asked. Built on the "prof"
3+# configuration of ticket 152 (comment 1), adapted to the team:
4+# - the Q4_K_M quantization the ticket asks for (8.1 GB instead of the
5+# Q8_0's 12.9 GB) — the same file as the coder's, loaded once for both;
6+# - no Rust lsp server and no docSearch over docs/: dev.team has no Rust
7+# code and no docs/ folder; fetch_content covers the documentation;
8+# - no sessions: an agent called through ask_agent runs with sessions off
9+# (AGENT_SESSIONS=off), the coordinator keeps the conversation.
10+#
11+# cd dev.team/workspace && ../../mm -config ../team/agent.tutor.yaml # alone, with sessions
12+
13+provider: llmman
14+model: hf.co/JetBrains/Mellum2-12B-A2.5B-Instruct-GGUF-Q4_K_M:Q4_K_M
15+baseUrl: http://127.0.0.1:17434/v1
16+fallback: http://host.docker.internal:17434/v1
17+
18+# mm's budget for compression; llmman served 131072 during the prof's
19+# measurements.
20+contextWindow: 32768
21+
22+sampling:
23+ # Explanations, not code edits: a little more freedom than a coder's 0.2.
24+ temperature: 0.3
25+ top_p: 0.9
26+ # A long answer, maybe after one fetched page.
27+ max_tokens: 16384
28+ parallel_tool_calls: false
29+ # No chat_template_kwargs: the Instruct template has no reasoning switch
30+ # (the prof's enable_thinking: false was for the Thinking model).
31+
32+firstTokenTimeout: 3m
33+watchdogTimeout: 2m
34+bashTimeout: 1m
35+
36+# A mini tutorial is one answer, maybe after one fetch, maybe one save.
37+maxTurns: 8
38+maxOutput: 8000
39+previewLines: 10
40+
41+# ─── Tools: the documentation, trying a snippet, saving on request ───────────
42+
43+yolo: true
44+# Kept from the prof (change of 2026-10-21): bash to try a snippet, files to
45+# read the user's code when a question is about it.
46+bashTool: true
47+editTools: true
48+planTool: false
49+askUserTool: false
50+fetchContentTool: true
51+saveAnswerTool: true
52+# The coordinator's hooks unload the models; no unload_model tool here.
53+unloadModels: false
54+skillsDir: no-skills
55+
56+agentsMd:
57+ enabled: false
58+
59+sessions:
60+ enabled: true
61+ dir: .mm/sessions
62+
63+context:
64+ enabled: true
65+ threshold: 60
66+ keepLastTurns: 2
67+ summaryMaxTokens: 1200
68+
69+# ─── Go language server ──────────────────────────────────────────────────────
70+
71+# The `lsp` tool, for .go files: definition, references, hover, symbols,
72+# diagnostics, rename. gopls must be on the PATH (go install
73+# golang.org/x/tools/gopls@latest); not found = a start-up warning, no tool.
74+lsp:
75+ servers:
76+ - name: gopls
77+ command: gopls
78+ extensions: [".go"]
79+
80+acp:
81+ name: tutor
82+ title: 📘 Tutor
83+
84+system: |
85+ You are Tutor. You answer questions about programming languages, shells and
86+ developer tools with a MINI TUTORIAL, in the language of the question.
87+
88+ HOW TO ANSWER
89+ 1. Decide whether you need the documentation. Fetch ONE page with
90+ fetch_content only when the subject is a recent or niche tool (for
91+ example Nushell, a young framework), when the user names a version, or
92+ when you are not sure a command or an option exists. For well-known
93+ topics (Go, Rust, Python, Kotlin, Java basics) answer from what you know.
94+ 2. When the question names a file or code of the current directory, your
95+ FIRST action is read_file on it — before writing any word of the
96+ lesson. Then base the lesson on that real code and quote it as it is;
97+ never show "a typical" version of a file you were asked about.
98+ For a Go symbol of that code, `lsp` `hover` gives its signature and
99+ documentation, `definition` where it is declared; never `rename`.
100+ 3. Write the mini tutorial directly in your reply, in Markdown:
101+ - a title (#)
102+ - one sentence: what the reader will be able to do
103+ - Prerequisites: a short list
104+ - 3 to 6 numbered steps, each with a code block and, when it prints
105+ something, the expected output
106+ - Pitfalls: 2 or 3 short points
107+ - To go further: one or two lines
108+ Between 300 and 600 words. Short snippets; a complete program only when
109+ the user asks for one.
110+ 4. A follow-up question ("and in Go?") refers to your previous answer:
111+ answer the same need for the new language or tool.
112+
113+ SAVING TO A FILE
114+ - Never save unless the user asks to save, write or export to a file.
115+ - When asked, call save_answer with the file name the user gave (or a short
116+ name made from the subject, ending in .md). Do NOT write the tutorial
117+ again: save_answer saves your previous answer by itself — call it alone,
118+ with no text in your reply.
119+ - When the request is "explain X and save it", write the tutorial in your
120+ reply AND call save_answer in that same reply.
121+ - If save_answer says the file exists, tell the user and ask for another
122+ name.
new file mode 100644
@@ -0,0 +1,122 @@
1+# Tutor — answers questions about languages, shells and developer tools with
2+# a MINI TUTORIAL; saves it to a file when asked. Built on the "prof"
3+# configuration of ticket 152 (comment 1), adapted to the team:
4+# - the Q4_K_M quantization the ticket asks for (8.1 GB instead of the
5+# Q8_0's 12.9 GB) — the same file as the coder's, loaded once for both;
6+# - no Rust lsp server and no docSearch over docs/: dev.team has no Rust
7+# code and no docs/ folder; fetch_content covers the documentation;
8+# - no sessions: an agent called through ask_agent runs with sessions off
9+# (AGENT_SESSIONS=off), the coordinator keeps the conversation.
10+#
11+# cd dev.team/workspace && ../../mm -config ../team/agent.tutor.yaml # alone, with sessions
12+
13+provider: llmman
14+model: hf.co/JetBrains/Mellum2-12B-A2.5B-Instruct-GGUF-Q4_K_M:Q4_K_M
15+baseUrl: http://127.0.0.1:17434/v1
16+fallback: http://host.docker.internal:17434/v1
17+
18+# mm's budget for compression; llmman served 131072 during the prof's
19+# measurements.
20+contextWindow: 32768
21+
22+sampling:
23+ # Explanations, not code edits: a little more freedom than a coder's 0.2.
24+ temperature: 0.3
25+ top_p: 0.9
26+ # A long answer, maybe after one fetched page.
27+ max_tokens: 16384
28+ parallel_tool_calls: false
29+ # No chat_template_kwargs: the Instruct template has no reasoning switch
30+ # (the prof's enable_thinking: false was for the Thinking model).
31+
32+firstTokenTimeout: 3m
33+watchdogTimeout: 2m
34+bashTimeout: 1m
35+
36+# A mini tutorial is one answer, maybe after one fetch, maybe one save.
37+maxTurns: 8
38+maxOutput: 8000
39+previewLines: 10
40+
41+# ─── Tools: the documentation, trying a snippet, saving on request ───────────
42+
43+yolo: true
44+# Kept from the prof (change of 2026-10-21): bash to try a snippet, files to
45+# read the user's code when a question is about it.
46+bashTool: true
47+editTools: true
48+planTool: false
49+askUserTool: false
50+fetchContentTool: true
51+saveAnswerTool: true
52+# The coordinator's hooks unload the models; no unload_model tool here.
53+unloadModels: false
54+skillsDir: no-skills
55+
56+agentsMd:
57+ enabled: false
58+
59+sessions:
60+ enabled: true
61+ dir: .mm/sessions
62+
63+context:
64+ enabled: true
65+ threshold: 60
66+ keepLastTurns: 2
67+ summaryMaxTokens: 1200
68+
69+# ─── Go language server ──────────────────────────────────────────────────────
70+
71+# The `lsp` tool, for .go files: definition, references, hover, symbols,
72+# diagnostics, rename. gopls must be on the PATH (go install
73+# golang.org/x/tools/gopls@latest); not found = a start-up warning, no tool.
74+lsp:
75+ servers:
76+ - name: gopls
77+ command: gopls
78+ extensions: [".go"]
79+
80+acp:
81+ name: tutor
82+ title: 📘 Tutor
83+
84+system: |
85+ You are Tutor. You answer questions about programming languages, shells and
86+ developer tools with a MINI TUTORIAL, in the language of the question.
87+
88+ HOW TO ANSWER
89+ 1. Decide whether you need the documentation. Fetch ONE page with
90+ fetch_content only when the subject is a recent or niche tool (for
91+ example Nushell, a young framework), when the user names a version, or
92+ when you are not sure a command or an option exists. For well-known
93+ topics (Go, Rust, Python, Kotlin, Java basics) answer from what you know.
94+ 2. When the question names a file or code of the current directory, your
95+ FIRST action is read_file on it — before writing any word of the
96+ lesson. Then base the lesson on that real code and quote it as it is;
97+ never show "a typical" version of a file you were asked about.
98+ For a Go symbol of that code, `lsp` `hover` gives its signature and
99+ documentation, `definition` where it is declared; never `rename`.
100+ 3. Write the mini tutorial directly in your reply, in Markdown:
101+ - a title (#)
102+ - one sentence: what the reader will be able to do
103+ - Prerequisites: a short list
104+ - 3 to 6 numbered steps, each with a code block and, when it prints
105+ something, the expected output
106+ - Pitfalls: 2 or 3 short points
107+ - To go further: one or two lines
108+ Between 300 and 600 words. Short snippets; a complete program only when
109+ the user asks for one.
110+ 4. A follow-up question ("and in Go?") refers to your previous answer:
111+ answer the same need for the new language or tool.
112+
113+ SAVING TO A FILE
114+ - Never save unless the user asks to save, write or export to a file.
115+ - When asked, call save_answer with the file name the user gave (or a short
116+ name made from the subject, ending in .md). Do NOT write the tutorial
117+ again: save_answer saves your previous answer by itself — call it alone,
118+ with no text in your reply.
119+ - When the request is "explain X and save it", write the tutorial in your
120+ reply AND call save_answer in that same reply.
121+ - If save_answer says the file exists, tell the user and ask for another
122+ name.
added dev.team/team/agent.yaml +193 -0
new file mode 100644
@@ -0,0 +1,193 @@
1+# Coordinator — the agent you talk to. It hands each request to the right
2+# member of the team with the built-in `ask_agent` tool, in order, and tells
3+# you what came back. It reads no file and writes no code itself.
4+#
5+# reformulator — turns a code request into a task brief; asks you when only
6+# you can decide (Gemma 4 12B)
7+# coder — fixes, adds, changes code; runs the tests (Mellum2 12B)
8+# reviewer — reviews the change, writes edge-case tests, verdict
9+# (Gemma 4 12B)
10+# tutor — explains, gives advice and mini tutorials (Mellum2 12B)
11+#
12+# Memory (32 GB MacBook Air): ONE model in memory at a time. The hooks at the
13+# bottom unload the coordinator's model right before a team member starts
14+# (preAsk) and the member's model right after it answers (postAsk); llmman
15+# reloads the coordinator's 4B by itself to read the answer. Peak: one 12B
16+# model, ~8 GB + its KV cache at 32k.
17+#
18+# This file lives in dev.team/team/, mounted READ-ONLY in the sandbox
19+# (sbxenv.yaml); the team works in dev.team/workspace/. Run it from there:
20+#
21+# sandbox: ./start-zed.sh (AGENT_CONFIG points here), then in workspace/:
22+# mm -tui
23+# host: cd dev.team/workspace
24+# ../../mm -config ../team/agent.yaml -tui
25+
26+# ─── Model and server ────────────────────────────────────────────────────────
27+
28+provider: llmman
29+
30+# Qwen3.5 4B, 3.4 GB: picking an agent and copying a request does not need a
31+# 12B, and the small model reloads in seconds between hand-offs.
32+model: hf.co/unsloth/Qwen3.5-4B-GGUF:Q4_K_M
33+
34+baseUrl: http://127.0.0.1:17434/v1
35+fallback: http://host.docker.internal:17434/v1
36+
37+contextWindow: 32768
38+
39+# The team. `description` is what the coordinator reads to choose. `yolo:
40+# true` = that member's commands and file writes run without asking once the
41+# hand-off is allowed; `askUser: true` = the reformulator's questions come up
42+# to you. Paths: relative to this file.
43+mmConfigs:
44+ reformulator:
45+ path: agent.reformulator.yaml
46+ description: >-
47+ Reformulator. Turns a request about code (a bug, a feature, a change)
48+ into a precise task brief for the coder and the reviewer. May ask the
49+ user a question. Writes no code.
50+ yolo: true
51+ askUser: true
52+ coder:
53+ path: agent.coder.yaml
54+ description: >-
55+ Coder. Implements a task brief in the project: fixes bugs, adds
56+ features, changes code, runs the tests. Also fixes the code from a
57+ reviewer's report.
58+ yolo: true
59+ askUser: false
60+ reviewer:
61+ path: agent.reviewer.yaml
62+ description: >-
63+ Reviewer and tester. Reviews a change against its task brief, runs the
64+ tests, writes edge-case tests, answers VERDICT: PASS or FAIL. Never
65+ changes the application's code.
66+ yolo: true
67+ askUser: false
68+ tutor:
69+ path: agent.tutor.yaml
70+ description: >-
71+ Tutor. Explains a concept, gives advice or a lesson, answers "how do I…
72+ / what is… / why…" questions with a mini tutorial; can save it to a
73+ file. Changes no code.
74+ yolo: true
75+ askUser: false
76+
77+# ─── Generation ──────────────────────────────────────────────────────────────
78+
79+sampling:
80+ # Qwen's non-thinking values are 0.7 / 0.8 / 20; routing and copying a
81+ # request word for word wants less: 0.3 (demos/team's lead).
82+ temperature: 0.3
83+ top_p: 0.8
84+ top_k: 20
85+ max_tokens: 4096
86+ # One hand-off at a time: brief, THEN code, THEN review.
87+ parallel_tool_calls: false
88+ # The template's default is off; set it anyway, routing needs no thoughts.
89+ chat_template_kwargs:
90+ enable_thinking: false
91+
92+# ─── Timeouts ────────────────────────────────────────────────────────────────
93+
94+# During ask_agent every event of the member counts as a sign of life: this
95+# covers the longest silence — a 12B loading after an unload.
96+watchdogTimeout: 5m
97+firstTokenTimeout: 3m
98+
99+# ─── Agent loop ──────────────────────────────────────────────────────────────
100+
101+# brief, code, review, at most one fix + review: 5 calls. Measured: with 8,
102+# on a failing feature the 4B re-asked the reviewer and called the coder four
103+# times; 6 leaves one spare call and stops that sooner.
104+maxTurns: 6
105+maxOutput: 12000
106+previewLines: 10
107+displayCommands: true
108+
109+# ─── Tools ───────────────────────────────────────────────────────────────────
110+
111+# true: the hand-offs run without asking. false = you approve each hand-off
112+# and see the prompt the coordinator wrote before it goes.
113+yolo: true
114+
115+# No tool but ask_agent: a coordinator with file tools starts doing the work
116+# itself (measured in demos/team).
117+bashTool: false
118+editTools: false
119+planTool: false
120+askUserTool: false
121+fetchContentTool: false
122+# The hooks unload; the model must not have to think about it.
123+unloadModels: false
124+skillsDir: no-skills
125+
126+agentsMd:
127+ enabled: false
128+
129+# ─── Memory ──────────────────────────────────────────────────────────────────
130+
131+sessions:
132+ enabled: true
133+ dir: .mm/sessions
134+
135+context:
136+ enabled: true
137+ threshold: 60
138+ keepLastTurns: 2
139+ summaryMaxTokens: 1200
140+
141+# ─── One model in memory at a time ───────────────────────────────────────────
142+
143+hooks:
144+ # onError: warn — an unload is a saving, not a check: the hand-off goes on.
145+ preAsk:
146+ - command: node "$MM_CONFIG_DIR/hooks/unload.js" self
147+ timeout: 30s
148+ onError: warn
149+ postAsk:
150+ - command: node "$MM_CONFIG_DIR/hooks/unload.js" asked
151+ timeout: 30s
152+ onError: warn
153+
154+# ─── Identity ────────────────────────────────────────────────────────────────
155+
156+acp:
157+ name: coordinator
158+ title: 🧭 Coordinator
159+
160+system: |
161+ You are Coordinator. You lead a team of four agents, reached with the
162+ `ask_agent` tool: reformulator, coder, reviewer, tutor. You never write
163+ code, read files or answer technical questions yourself.
164+
165+ CHOOSE THE ROUTE FROM THE REQUEST
166+ A. Code to write or change (fix a bug, add a feature, refactor, write
167+ tests) — steps 1 to 4 in ONE answer, calling the next agent as soon as
168+ the previous one has answered:
169+ 1. ask_agent reformulator: "Write a task brief for this request: <the
170+ user's request, complete, in their words>."
171+ 2. ask_agent coder: "Implement this task brief: <the brief, complete>."
172+ 3. ask_agent reviewer: "Review and test the change for this task brief:
173+ <the brief, complete>. The coder's report: <the coder's answer>."
174+ 4. Only if the reviewer's answer contains "VERDICT: FAIL": ask_agent
175+ coder ONCE: "Fix the code. Reviewer's report: <the reviewer's
176+ answer>." Then ask_agent reviewer again as in step 3. Do not loop
177+ further.
178+ B. A question, a lesson, advice, an explanation ("how do I…", "what is…",
179+ "why…", "explain…", "teach me…") — ask_agent tutor with the user's
180+ question, complete, in their words.
181+ C. Only a review of existing code ("review", "check", "test this") —
182+ ask_agent reviewer with the request.
183+
184+ RULES
185+ - One ask_agent call per step; wait for its result — it is that agent's
186+ answer. Never answer in its place and never skip a step of route A.
187+ - If a result starts with "[ask_agent:", or says an agent failed or hit
188+ its limit, that step failed: tell the user and stop. Never re-ask an
189+ agent with a new prompt.
190+ - Then report to the user, in the language they wrote in, briefly: who did
191+ what, the files changed (from the coder's and the reviewer's answers),
192+ the tests and the verdict. For route B, give
193+ the tutor's answer as it is.
new file mode 100644
@@ -0,0 +1,193 @@
1+# Coordinator — the agent you talk to. It hands each request to the right
2+# member of the team with the built-in `ask_agent` tool, in order, and tells
3+# you what came back. It reads no file and writes no code itself.
4+#
5+# reformulator — turns a code request into a task brief; asks you when only
6+# you can decide (Gemma 4 12B)
7+# coder — fixes, adds, changes code; runs the tests (Mellum2 12B)
8+# reviewer — reviews the change, writes edge-case tests, verdict
9+# (Gemma 4 12B)
10+# tutor — explains, gives advice and mini tutorials (Mellum2 12B)
11+#
12+# Memory (32 GB MacBook Air): ONE model in memory at a time. The hooks at the
13+# bottom unload the coordinator's model right before a team member starts
14+# (preAsk) and the member's model right after it answers (postAsk); llmman
15+# reloads the coordinator's 4B by itself to read the answer. Peak: one 12B
16+# model, ~8 GB + its KV cache at 32k.
17+#
18+# This file lives in dev.team/team/, mounted READ-ONLY in the sandbox
19+# (sbxenv.yaml); the team works in dev.team/workspace/. Run it from there:
20+#
21+# sandbox: ./start-zed.sh (AGENT_CONFIG points here), then in workspace/:
22+# mm -tui
23+# host: cd dev.team/workspace
24+# ../../mm -config ../team/agent.yaml -tui
25+
26+# ─── Model and server ────────────────────────────────────────────────────────
27+
28+provider: llmman
29+
30+# Qwen3.5 4B, 3.4 GB: picking an agent and copying a request does not need a
31+# 12B, and the small model reloads in seconds between hand-offs.
32+model: hf.co/unsloth/Qwen3.5-4B-GGUF:Q4_K_M
33+
34+baseUrl: http://127.0.0.1:17434/v1
35+fallback: http://host.docker.internal:17434/v1
36+
37+contextWindow: 32768
38+
39+# The team. `description` is what the coordinator reads to choose. `yolo:
40+# true` = that member's commands and file writes run without asking once the
41+# hand-off is allowed; `askUser: true` = the reformulator's questions come up
42+# to you. Paths: relative to this file.
43+mmConfigs:
44+ reformulator:
45+ path: agent.reformulator.yaml
46+ description: >-
47+ Reformulator. Turns a request about code (a bug, a feature, a change)
48+ into a precise task brief for the coder and the reviewer. May ask the
49+ user a question. Writes no code.
50+ yolo: true
51+ askUser: true
52+ coder:
53+ path: agent.coder.yaml
54+ description: >-
55+ Coder. Implements a task brief in the project: fixes bugs, adds
56+ features, changes code, runs the tests. Also fixes the code from a
57+ reviewer's report.
58+ yolo: true
59+ askUser: false
60+ reviewer:
61+ path: agent.reviewer.yaml
62+ description: >-
63+ Reviewer and tester. Reviews a change against its task brief, runs the
64+ tests, writes edge-case tests, answers VERDICT: PASS or FAIL. Never
65+ changes the application's code.
66+ yolo: true
67+ askUser: false
68+ tutor:
69+ path: agent.tutor.yaml
70+ description: >-
71+ Tutor. Explains a concept, gives advice or a lesson, answers "how do I…
72+ / what is… / why…" questions with a mini tutorial; can save it to a
73+ file. Changes no code.
74+ yolo: true
75+ askUser: false
76+
77+# ─── Generation ──────────────────────────────────────────────────────────────
78+
79+sampling:
80+ # Qwen's non-thinking values are 0.7 / 0.8 / 20; routing and copying a
81+ # request word for word wants less: 0.3 (demos/team's lead).
82+ temperature: 0.3
83+ top_p: 0.8
84+ top_k: 20
85+ max_tokens: 4096
86+ # One hand-off at a time: brief, THEN code, THEN review.
87+ parallel_tool_calls: false
88+ # The template's default is off; set it anyway, routing needs no thoughts.
89+ chat_template_kwargs:
90+ enable_thinking: false
91+
92+# ─── Timeouts ────────────────────────────────────────────────────────────────
93+
94+# During ask_agent every event of the member counts as a sign of life: this
95+# covers the longest silence — a 12B loading after an unload.
96+watchdogTimeout: 5m
97+firstTokenTimeout: 3m
98+
99+# ─── Agent loop ──────────────────────────────────────────────────────────────
100+
101+# brief, code, review, at most one fix + review: 5 calls. Measured: with 8,
102+# on a failing feature the 4B re-asked the reviewer and called the coder four
103+# times; 6 leaves one spare call and stops that sooner.
104+maxTurns: 6
105+maxOutput: 12000
106+previewLines: 10
107+displayCommands: true
108+
109+# ─── Tools ───────────────────────────────────────────────────────────────────
110+
111+# true: the hand-offs run without asking. false = you approve each hand-off
112+# and see the prompt the coordinator wrote before it goes.
113+yolo: true
114+
115+# No tool but ask_agent: a coordinator with file tools starts doing the work
116+# itself (measured in demos/team).
117+bashTool: false
118+editTools: false
119+planTool: false
120+askUserTool: false
121+fetchContentTool: false
122+# The hooks unload; the model must not have to think about it.
123+unloadModels: false
124+skillsDir: no-skills
125+
126+agentsMd:
127+ enabled: false
128+
129+# ─── Memory ──────────────────────────────────────────────────────────────────
130+
131+sessions:
132+ enabled: true
133+ dir: .mm/sessions
134+
135+context:
136+ enabled: true
137+ threshold: 60
138+ keepLastTurns: 2
139+ summaryMaxTokens: 1200
140+
141+# ─── One model in memory at a time ───────────────────────────────────────────
142+
143+hooks:
144+ # onError: warn — an unload is a saving, not a check: the hand-off goes on.
145+ preAsk:
146+ - command: node "$MM_CONFIG_DIR/hooks/unload.js" self
147+ timeout: 30s
148+ onError: warn
149+ postAsk:
150+ - command: node "$MM_CONFIG_DIR/hooks/unload.js" asked
151+ timeout: 30s
152+ onError: warn
153+
154+# ─── Identity ────────────────────────────────────────────────────────────────
155+
156+acp:
157+ name: coordinator
158+ title: 🧭 Coordinator
159+
160+system: |
161+ You are Coordinator. You lead a team of four agents, reached with the
162+ `ask_agent` tool: reformulator, coder, reviewer, tutor. You never write
163+ code, read files or answer technical questions yourself.
164+
165+ CHOOSE THE ROUTE FROM THE REQUEST
166+ A. Code to write or change (fix a bug, add a feature, refactor, write
167+ tests) — steps 1 to 4 in ONE answer, calling the next agent as soon as
168+ the previous one has answered:
169+ 1. ask_agent reformulator: "Write a task brief for this request: <the
170+ user's request, complete, in their words>."
171+ 2. ask_agent coder: "Implement this task brief: <the brief, complete>."
172+ 3. ask_agent reviewer: "Review and test the change for this task brief:
173+ <the brief, complete>. The coder's report: <the coder's answer>."
174+ 4. Only if the reviewer's answer contains "VERDICT: FAIL": ask_agent
175+ coder ONCE: "Fix the code. Reviewer's report: <the reviewer's
176+ answer>." Then ask_agent reviewer again as in step 3. Do not loop
177+ further.
178+ B. A question, a lesson, advice, an explanation ("how do I…", "what is…",
179+ "why…", "explain…", "teach me…") — ask_agent tutor with the user's
180+ question, complete, in their words.
181+ C. Only a review of existing code ("review", "check", "test this") —
182+ ask_agent reviewer with the request.
183+
184+ RULES
185+ - One ask_agent call per step; wait for its result — it is that agent's
186+ answer. Never answer in its place and never skip a step of route A.
187+ - If a result starts with "[ask_agent:", or says an agent failed or hit
188+ its limit, that step failed: tell the user and stop. Never re-ask an
189+ agent with a new prompt.
190+ - Then report to the user, in the language they wrote in, briefly: who did
191+ what, the files changed (from the coder's and the reviewer's answers),
192+ the tests and the verdict. For route B, give
193+ the tutor's answer as it is.
added dev.team/team/hooks/gofmt.js +30 -0
new file mode 100644
@@ -0,0 +1,30 @@
1+#!/usr/bin/env node
2+// postToolCall hook of the coder (write_file, edit_file): formats the Go file
3+// the tool just wrote, with `gofmt -w`.
4+//
5+// Why a hook: the coder's prompt asks for gofmt, and Mellum2 skipped it in
6+// every measured run (2 out of 2 left the files unformatted). A hook makes it
7+// certain and costs the model nothing.
8+//
9+// Input: {"cwd": "...", "tool": {"name": "write_file", "input": {"path": "..."}}}.
10+// Prints nothing when the file is formatted (or is not Go). When gofmt fails,
11+// the file does not parse: its error goes back to the model as context, so
12+// the next step can fix it.
13+
14+const fs = require("fs");
15+const path = require("path");
16+const { execFileSync } = require("child_process");
17+
18+const input = JSON.parse(fs.readFileSync(0, "utf8"));
19+const file = input.tool && input.tool.input && input.tool.input.path;
20+if (!file || !file.endsWith(".go")) process.exit(0);
21+
22+const full = path.resolve(input.cwd || ".", file);
23+if (!fs.existsSync(full)) process.exit(0); // the tool failed: nothing written
24+
25+try {
26+ execFileSync("gofmt", ["-w", full], { stdio: ["ignore", "ignore", "pipe"] });
27+} catch (err) {
28+ const reason = err.code === "ENOENT" ? "gofmt is not on the PATH" : String(err.stderr || err.message).trim();
29+ console.log(JSON.stringify({ context: `gofmt could not format ${file}: ${reason}` }));
30+}
new file mode 100644
@@ -0,0 +1,30 @@
1+#!/usr/bin/env node
2+// postToolCall hook of the coder (write_file, edit_file): formats the Go file
3+// the tool just wrote, with `gofmt -w`.
4+//
5+// Why a hook: the coder's prompt asks for gofmt, and Mellum2 skipped it in
6+// every measured run (2 out of 2 left the files unformatted). A hook makes it
7+// certain and costs the model nothing.
8+//
9+// Input: {"cwd": "...", "tool": {"name": "write_file", "input": {"path": "..."}}}.
10+// Prints nothing when the file is formatted (or is not Go). When gofmt fails,
11+// the file does not parse: its error goes back to the model as context, so
12+// the next step can fix it.
13+
14+const fs = require("fs");
15+const path = require("path");
16+const { execFileSync } = require("child_process");
17+
18+const input = JSON.parse(fs.readFileSync(0, "utf8"));
19+const file = input.tool && input.tool.input && input.tool.input.path;
20+if (!file || !file.endsWith(".go")) process.exit(0);
21+
22+const full = path.resolve(input.cwd || ".", file);
23+if (!fs.existsSync(full)) process.exit(0); // the tool failed: nothing written
24+
25+try {
26+ execFileSync("gofmt", ["-w", full], { stdio: ["ignore", "ignore", "pipe"] });
27+} catch (err) {
28+ const reason = err.code === "ENOENT" ? "gofmt is not on the PATH" : String(err.stderr || err.message).trim();
29+ console.log(JSON.stringify({ context: `gofmt could not format ${file}: ${reason}` }));
30+}
added dev.team/team/hooks/unload.js +58 -0
new file mode 100644
@@ -0,0 +1,58 @@
1+#!/usr/bin/env node
2+// preAsk / postAsk hook: unloads one model so that two models are never in
3+// memory together during a hand-off.
4+//
5+// node unload.js self preAsk: the ASKING agent's own model, right before
6+// mm starts the asked agent. Only mm can do it at
7+// that moment: the model has just decided to call
8+// ask_agent, and it is needed again only to read the
9+// answer — the server reloads it then by itself. The
10+// model cannot do it (unload_model on its own model
11+// lasts until its next step).
12+// node unload.js asked postAsk: the ASKED agent's model, once it has
13+// answered and before the asking agent reads the
14+// answer — so the asker's model reloads alone.
15+//
16+// Input: {"model": "llmman/<model>", "ask": {"agentModel": "llmman/<model>"},
17+// ...}. Server: $LLMMAN_URL, else llmman's default address, else the one a
18+// Docker sandbox reaches the host by. Prints nothing: the hand-off goes on
19+// whatever happens (onError: warn in agent.yaml), an unload is a saving, not
20+// a check.
21+
22+const fs = require("fs");
23+
24+const input = JSON.parse(fs.readFileSync(0, "utf8"));
25+const which = process.argv[2];
26+const ref = which === "asked" ? input.ask && input.ask.agentModel : input.model;
27+if (!ref) {
28+ console.error(`unload.js ${which}: no model in the input`);
29+ process.exit(1);
30+}
31+const model = ref.replace(/^[^/]+\//, ""); // drop the "llmman/" provider prefix
32+
33+const servers = process.env.LLMMAN_URL
34+ ? [process.env.LLMMAN_URL]
35+ : ["http://127.0.0.1:17434", "http://host.docker.internal:17434"];
36+
37+async function unload() {
38+ for (const server of servers) {
39+ try {
40+ const resp = await fetch(server + "/api/generate", {
41+ method: "POST",
42+ headers: { "Content-Type": "application/json" },
43+ body: JSON.stringify({ model, keep_alive: 0 }),
44+ signal: AbortSignal.timeout(20_000),
45+ });
46+ const reply = await resp.json();
47+ if (reply.done_reason === "unload") return;
48+ throw new Error(`${server}: not confirmed: ${JSON.stringify(reply)}`);
49+ } catch (err) {
50+ if (server === servers[servers.length - 1]) throw err;
51+ }
52+ }
53+}
54+
55+unload().catch((err) => {
56+ console.error(`could not unload ${model}: ${err.message}`);
57+ process.exit(1);
58+});
new file mode 100644
@@ -0,0 +1,58 @@
1+#!/usr/bin/env node
2+// preAsk / postAsk hook: unloads one model so that two models are never in
3+// memory together during a hand-off.
4+//
5+// node unload.js self preAsk: the ASKING agent's own model, right before
6+// mm starts the asked agent. Only mm can do it at
7+// that moment: the model has just decided to call
8+// ask_agent, and it is needed again only to read the
9+// answer — the server reloads it then by itself. The
10+// model cannot do it (unload_model on its own model
11+// lasts until its next step).
12+// node unload.js asked postAsk: the ASKED agent's model, once it has
13+// answered and before the asking agent reads the
14+// answer — so the asker's model reloads alone.
15+//
16+// Input: {"model": "llmman/<model>", "ask": {"agentModel": "llmman/<model>"},
17+// ...}. Server: $LLMMAN_URL, else llmman's default address, else the one a
18+// Docker sandbox reaches the host by. Prints nothing: the hand-off goes on
19+// whatever happens (onError: warn in agent.yaml), an unload is a saving, not
20+// a check.
21+
22+const fs = require("fs");
23+
24+const input = JSON.parse(fs.readFileSync(0, "utf8"));
25+const which = process.argv[2];
26+const ref = which === "asked" ? input.ask && input.ask.agentModel : input.model;
27+if (!ref) {
28+ console.error(`unload.js ${which}: no model in the input`);
29+ process.exit(1);
30+}
31+const model = ref.replace(/^[^/]+\//, ""); // drop the "llmman/" provider prefix
32+
33+const servers = process.env.LLMMAN_URL
34+ ? [process.env.LLMMAN_URL]
35+ : ["http://127.0.0.1:17434", "http://host.docker.internal:17434"];
36+
37+async function unload() {
38+ for (const server of servers) {
39+ try {
40+ const resp = await fetch(server + "/api/generate", {
41+ method: "POST",
42+ headers: { "Content-Type": "application/json" },
43+ body: JSON.stringify({ model, keep_alive: 0 }),
44+ signal: AbortSignal.timeout(20_000),
45+ });
46+ const reply = await resp.json();
47+ if (reply.done_reason === "unload") return;
48+ throw new Error(`${server}: not confirmed: ${JSON.stringify(reply)}`);
49+ } catch (err) {
50+ if (server === servers[servers.length - 1]) throw err;
51+ }
52+ }
53+}
54+
55+unload().catch((err) => {
56+ console.error(`could not unload ${model}: ${err.message}`);
57+ process.exit(1);
58+});
added dev.team/workspace/.gitkeep +0 -0
new file mode 100644
new file mode 100644