@marcomigozzi/trailhead 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.it.md +45 -9
  2. package/README.md +46 -10
  3. package/bin/auto-intro-docs.test.js +72 -0
  4. package/bin/auto-merge-docs.test.js +33 -0
  5. package/bin/auto-merge-ticket-docs.test.js +32 -0
  6. package/bin/auto-review-rounds-docs.test.js +45 -0
  7. package/bin/auto-run-docs.test.js +60 -0
  8. package/bin/auto-scope-docs.test.js +33 -0
  9. package/bin/auto-slots-docs.test.js +45 -0
  10. package/bin/auto-uat-docs.test.js +47 -0
  11. package/bin/grilling-holistic-review-docs.test.js +90 -0
  12. package/bin/lib/codex-projection.js +5 -5
  13. package/bin/lib/codex-projection.test.js +2 -2
  14. package/bin/needs-you-docs.test.js +41 -0
  15. package/bin/pick-stress-test-docs.test.js +105 -0
  16. package/bin/trailhead.js +3 -3
  17. package/bin/trailhead.test.js +17 -18
  18. package/package.json +1 -1
  19. package/plugins/trailhead/.claude-plugin/plugin.json +1 -1
  20. package/plugins/trailhead/commands/auto.md +2 -2
  21. package/plugins/trailhead/hooks/lib/auto-merge.test.js +467 -0
  22. package/plugins/trailhead/hooks/lib/auto-queue.test.js +245 -0
  23. package/plugins/trailhead/hooks/lib/auto-run.test.js +318 -0
  24. package/plugins/trailhead/hooks/lib/auto-slot.test.js +278 -0
  25. package/plugins/trailhead/hooks/lib/checkout-in.test.js +173 -0
  26. package/plugins/trailhead/hooks/lib/dashboard.test.js +66 -3
  27. package/plugins/trailhead/hooks/lib/merge-ticket.test.js +394 -0
  28. package/plugins/trailhead/hooks/lib/needs-you.test.js +177 -0
  29. package/plugins/trailhead/hooks/lib/plan-review-rounds.test.js +319 -0
  30. package/plugins/trailhead/hooks/lib/statusline-context.test.js +140 -0
  31. package/plugins/trailhead/hooks/lib/uat-followup.test.js +169 -0
  32. package/plugins/trailhead/hooks/lib/unblock.test.js +207 -0
  33. package/plugins/trailhead/hooks/mod/README.md +180 -39
  34. package/plugins/trailhead/hooks/mod/auto-run-view.ts +93 -0
  35. package/plugins/trailhead/hooks/mod/auto-run.ts +165 -0
  36. package/plugins/trailhead/hooks/mod/map-pane-view.ts +150 -25
  37. package/plugins/trailhead/hooks/mod/map-pane.tsx +526 -92
  38. package/plugins/trailhead/hooks/mod/register.tsx +298 -39
  39. package/plugins/trailhead/hooks/mod/repo-pin.ts +167 -0
  40. package/plugins/trailhead/hooks/mod/session-registry.ts +65 -0
  41. package/plugins/trailhead/hooks/mod/turn-state.ts +14 -0
  42. package/plugins/trailhead/hooks/mod/whiteboard-follow.ts +98 -0
  43. package/plugins/trailhead/hooks/mod/work-state-locate.ts +191 -0
  44. package/plugins/trailhead/hooks/mod/work-state-prompt.ts +13 -15
  45. package/plugins/trailhead/hooks/mod/work-state-view.ts +21 -5
  46. package/plugins/trailhead/hooks/mod/worktree-porcelain.ts +17 -0
  47. package/plugins/trailhead/hooks/trailhead-bash-guards.js +8 -8
  48. package/plugins/trailhead/hooks/trailhead-bash-guards.test.js +22 -16
  49. package/plugins/trailhead/hooks/trailhead-labels-guard.js +231 -0
  50. package/plugins/trailhead/hooks/trailhead-labels-guard.test.js +212 -0
  51. package/plugins/trailhead/hooks/trailhead-secret-read-guard.js +296 -22
  52. package/plugins/trailhead/hooks/trailhead-secret-read-guard.test.js +253 -0
  53. package/plugins/trailhead/skills/_shared/choices.md +7 -2
  54. package/plugins/trailhead/skills/_shared/configuration-reference.md +3 -3
  55. package/plugins/trailhead/skills/_shared/substrate-commands.md +26 -4
  56. package/plugins/trailhead/skills/_shared/substrate.md +4 -1
  57. package/plugins/trailhead/skills/_shared/teamwork.md +2 -2
  58. package/plugins/trailhead/skills/_shared/techniques/acceptance-testing.md +2 -2
  59. package/plugins/trailhead/skills/_shared/techniques/grilling.md +4 -2
  60. package/plugins/trailhead/skills/_shared/techniques/plan-review.md +3 -1
  61. package/plugins/trailhead/skills/trailhead-capture/references/capture.md +1 -1
  62. package/plugins/trailhead/skills/trailhead-chart/SKILL.md +1 -1
  63. package/plugins/trailhead/skills/trailhead-chart/references/charting.md +4 -4
  64. package/plugins/trailhead/skills/trailhead-chart/references/inbox.md +2 -2
  65. package/plugins/trailhead/skills/trailhead-view/SKILL.md +1 -0
  66. package/plugins/trailhead/skills/trailhead-work/SKILL.md +21 -10
  67. package/plugins/trailhead/skills/trailhead-work/references/auto.md +70 -22
  68. package/plugins/trailhead/skills/trailhead-work/references/ticket-engines.md +6 -6
  69. package/plugins/trailhead/templates/statusline-trailhead.sh +77 -12
  70. package/plugins/trailhead/templates/trailhead-auto-merge.js +318 -0
  71. package/plugins/trailhead/templates/trailhead-auto-queue.js +186 -0
  72. package/plugins/trailhead/templates/trailhead-auto-run.js +294 -0
  73. package/plugins/trailhead/templates/trailhead-auto-slot.js +239 -0
  74. package/plugins/trailhead/templates/trailhead-checkout-in.js +88 -0
  75. package/plugins/trailhead/templates/trailhead-dashboard.js +32 -12
  76. package/plugins/trailhead/templates/trailhead-merge-ticket.js +366 -0
  77. package/plugins/trailhead/templates/trailhead-needs-you.js +193 -0
  78. package/plugins/trailhead/templates/trailhead-plan-review-rounds.js +289 -0
  79. package/plugins/trailhead/templates/trailhead-uat-followup.js +128 -0
  80. package/plugins/trailhead/templates/trailhead-unblock.js +111 -0
  81. package/plugins/trailhead/tests/auto-run-ui.test.ts +444 -0
  82. package/plugins/trailhead/tests/auto-run-view.test.ts +111 -0
  83. package/plugins/trailhead/tests/map-pane-ui.test.ts +1732 -142
  84. package/plugins/trailhead/tests/map-pane-view.test.ts +308 -14
  85. package/plugins/trailhead/tests/map-pane-write.test.ts +51 -0
  86. package/plugins/trailhead/tests/repo-pin-late-repo.test.ts +77 -0
  87. package/plugins/trailhead/tests/repo-pin.test.ts +123 -0
  88. package/plugins/trailhead/tests/session-registry.test.ts +50 -0
  89. package/plugins/trailhead/tests/whiteboard-follow.test.ts +90 -0
  90. package/plugins/trailhead/tests/work-state-locate.test.ts +33 -0
  91. package/plugins/trailhead/tests/work-state-prompt-hook.test.ts +276 -2
  92. package/plugins/trailhead/tests/work-state-prompt.test.ts +29 -35
  93. package/plugins/trailhead/tests/work-state-ui.test.ts +73 -1
  94. package/plugins/trailhead/tests/work-state-view.test.ts +19 -0
  95. package/plugins/trailhead/tests/work-state-worktree.test.ts +310 -0
  96. package/plugins/trailhead/types/index.d.ts +26 -1
package/README.it.md CHANGED
@@ -32,6 +32,7 @@ Le scelte di design che ne derivano:
32
32
 
33
33
  - **Tutto vive su GitHub Issues.** La mappa è la parent issue; ogni ticket è una child issue; discussione, piano e verifica sono *commenti*. Non c'è alcuna directory `.planning/`: il repo contiene solo codice, e il piano è sempre visibile a chiunque abbia accesso al repo.
34
34
  - **L'esecuzione avviene dentro la mappa.** A differenza del Wayfinder puro ("plan, don't do"), i build ticket si diplomano dalla fog e vengono eseguiti come figli della mappa. La destination è l'**artefatto funzionante** (un'app deployata), non un documento di specifica, anche se un progetto può ridefinire questo e fermarsi alla specifica.
35
+ - **Dai una mappa e torna dopo.** `/trailhead:auto` svuota una mappa o la whiteboard senza supervisione, in fino a quattro slot paralleli, e lascia un run report, i ticket che hanno bisogno di te e i commit locali da pushare. Vedi [Run autonoma](#-run-autonoma-auto).
35
36
  - **Lean by default.** Il macchinario pesante (codebase mapping, debugging sistematico) gira una volta dove vale la pena; il ciclo per-ticket resta leggero.
36
37
 
37
38
  - **Costruito per i team, perché il piano è condiviso.** Il tracker è l'unica fonte di verità, quindi la mappa è un **workspace condiviso**, non il file locale di una singola persona. Molte persone (e i loro agenti) ci lavorano contemporaneamente, e i meccanismi impediscono che si scontrino:
@@ -70,7 +71,7 @@ npx @marcomigozzi/trailhead --dir=<path> # punta a una config dir specifica
70
71
  ```
71
72
  Su **Claude Code** copia la skill (+ le sue `references/`), i comandi `/trailhead:*`, gli hook (in `hooks/`, registrati in `settings.json`) incluso un **update check** al SessionStart, e i template (workflow label-guard, statusline, l'hook git `commit-msg` e gli script di supporto che il motore esegue), in modo idempotente.
72
73
 
73
- Su **Codex CLI** (`--codex`) proietta una skill nativa Codex invocata come **`$trailhead`** (`$trailhead work`, `$trailhead new "idea"`, …), una skill sottile **`$trailhead-<verb>`** per ogni verbo per la discoverability, le sette guardrail come **hook** nativi di Codex (`~/.codex/hooks.json`, abilitando `features.hooks`; i quattro guard Bash condividono un solo processo dietro una singola registrazione; la search guard è solo per Claude Code e non è proiettata su Codex), e i pin di modello per-tecnica da **`models.codex.*`** in `~/.codex/agents/`. Serve Codex **0.145.0+** (l'installer fa un gate su `codex --version`). La modalità mockup `artifacts` (il tool `Artifact` di Claude Code) non è disponibile su Codex; lì l'opzione hosted è la modalità cross-host `design: stitch` (MCP Google Stitch), con il disco locale come ripiego.
74
+ Su **Codex CLI** (`--codex`) proietta una skill nativa Codex invocata come **`$trailhead`** (`$trailhead work`, `$trailhead new "idea"`, …), una skill sottile **`$trailhead-<verb>`** per ogni verbo per la discoverability, le otto guardrail come **hook** nativi di Codex (`~/.codex/hooks.json`, abilitando `features.hooks`; i cinque guard Bash condividono un solo processo dietro una singola registrazione; la search guard è solo per Claude Code e non è proiettata su Codex), e i pin di modello per-tecnica da **`models.codex.*`** in `~/.codex/agents/`. Serve Codex **0.145.0+** (l'installer fa un gate su `codex --version`). La modalità mockup `artifacts` (il tool `Artifact` di Claude Code) non è disponibile su Codex; lì l'opzione hosted è la modalità cross-host `design: stitch` (MCP Google Stitch), con il disco locale come ripiego.
74
75
 
75
76
  Ri-esegui l'install per aggiornare, oppure esegui **`/trailhead:update`** (Claude) / **`$trailhead update`** (Codex) da dentro l'agente: rileva come trailhead è stato installato e installa la versione più recente dove è sicuro farlo (un `git pull` per un dev-symlink, una ri-esecuzione `npx` per npm, o `/plugin update` per il plugin). Quando esiste una versione più recente, la statusline mostra un flag `⬆ trailhead <version>` (Claude).
76
77
 
@@ -106,12 +107,46 @@ Questi tre riempiono i **3 slot di issue pinnate** di GitHub, così gli anchor d
106
107
  **Il ciclo di vita.** Ogni progetto percorre lo stesso loop:
107
108
 
108
109
  1. **Start**: `/trailhead:new "<idea>"` (greenfield) o `/trailhead:adopt` (codice esistente: mappa la codebase una volta, poi vai lean). Questo *traccia la mappa*: dai un nome alla destination, poi mappa la frontier in ampiezza (breadth-first) nei primi ticket e nella fog.
109
- 2. **Lavora la frontier, un ticket per sessione**: `/trailhead:work` prende il prossimo ticket prendibile (o quello che nomini) ed esegue il motore per il suo tipo: `research` raccoglie un fatto · `decision` dirama le opzioni poi fa grilling per scegliere · `prototype` crea un artefatto grezzo a cui reagire · `build` esegue discuss → plan → execute → verify · `bug` esegue repro → diagnose → fix → verify · `task` è plumbing manuale che sblocca una decision. `/trailhead:auto` incatena la frontier in autonomia, un ticket dopo l'altro, fermandosi solo al safety rail, alla fog, o a una decisione che serve l'umano. All'avvio di un ticket, `work` e `quick` mostrano un breve riepilogo con la scelta **Proceed / Change something**; viene saltata (al suo posto una riga di nota) per un ticket che hai creato o appena chiarito con un grilling in questa sessione, e `auto` non chiede mai.
110
+ 2. **Lavora la frontier, un ticket per sessione**: `/trailhead:work` prende il prossimo ticket prendibile (o quello che nomini) ed esegue il motore per il suo tipo: `research` raccoglie un fatto · `decision` dirama le opzioni poi fa grilling per scegliere · `prototype` crea un artefatto grezzo a cui reagire · `build` esegue discuss → plan → execute → verify · `bug` esegue repro → diagnose → fix → verify · `task` è plumbing manuale che sblocca una decision. `/trailhead:auto` esegue in autonomia uno scope scelto, fino a 4 slot paralleli, finché la frontier è esaurita o lo fermi tu, parcheggiando ciò che serve a te (un'azione del safety rail, la fog, una decisione che serve l'umano; vedi sotto). All'avvio di un ticket, `work` e `quick` mostrano un breve riepilogo con la scelta **Proceed / Change something**; viene saltata (al suo posto una riga di nota) per un ticket che hai creato o appena chiarito con un grilling in questa sessione, e `auto` non lo mostra mai.
110
111
  3. **Chiudi e sblocca**: risolvi il ticket (commento + chiusura), fanne il gist in `Decisions so far`, e rimuovi `trailhead:blocked` da ogni dipendente il cui ultimo blocker si è appena chiuso, diplomandolo sulla frontier.
111
112
  4. **Ripeti** finché la frontier è vuota e la fog si è diradata: la destination (un artefatto funzionante) è raggiunta.
112
113
 
113
114
  Lungo il cammino: **cattura** idee in corso d'opera senza deragliare, **splitta** un ticket cresciuto troppo, **pause/resume** tra le sessioni, e lascia che più persone lavorino in parallelo i ticket sbloccati.
114
115
 
116
+ ### 🤖 Run autonoma: `auto`
117
+
118
+ `/trailhead:auto [map]` esegue in autonomia uno scope scelto, ticket dopo ticket, risolvendo ciascuno con il suo motore normale invece di restituirti il controllo nel mezzo. Sospende la regola un-ticket-per-sessione e non si riattiva mai da solo: ogni run la avvii tu.
119
+
120
+ `auto` svuota una mappa (o la whiteboard, o tutto) senza supervisione, ticket dopo ticket, in 1-4 slot paralleli. `/trailhead:work` è l'altra modalità: un ticket per sessione, con te nel loop (brief, grilling, scelte UAT). Usa `auto` quando la frontier contiene lavoro build o bug ben specificato che non devi guidare; usa `work` per le decisioni, i ticket vaghi o quando vuoi guidare tu. Quando torni trovi il run report in chat (postato anche su ogni mappa toccata), i ticket che hanno bisogno di te con label `trailhead:needs-you` e un commento `NEEDS YOU`, e i commit locali che aspettano il tuo push: la run non pusha mai.
121
+
122
+ Mentre gira, la chat mostra una riga di avanzamento con i ticket negli slot e cosa si è appena risolto o viene dopo; alla fine, il run report. Qualcosa come:
123
+
124
+ ```text
125
+ auto: slot 1 #42 add social login (verify) | slot 2 #43 session cookie (execute) | resolved #41, next #44
126
+
127
+ Run report
128
+ Stopped: frontier empty
129
+ Resolved: #41, #42, #43, #44
130
+ Merged: #41, #42, #43, #44 into main; local main is 7 commits ahead of origin (pushing is yours)
131
+ Remains: #45 needs you (which providers to support); #46 blocked by #45
132
+ ```
133
+
134
+ **Checkout principale.** Con più di 1 slot, la run unisce ogni branch di slot verificato nel trunk locale del checkout principale (un fast-forward quando possibile, altrimenti un merge commit), quindi se stai lavorando a mano in quel checkout il tuo branch può spostarsi sotto di te. Il lavoro tracked non committato resta protetto: il merge si ferma su un albero sporco e il ticket viene parcheggiato needs-you. I file untracked non fanno scattare quel controllo (uno che intralcia un file portato dal merge lo ferma comunque, parcheggiato allo stesso modo).
135
+
136
+ - **Chiede prima, prima di prendere in carico qualsiasi cosa, senza opzione delegate.** Lo scope della run (una mappa, la whiteboard o tutto, ciascuno con il suo conteggio di frontier, poi quale mappa se ne scegli una e ce ne sono più aperte; `/trailhead:auto <n>` salta solo questa domanda). Gli slot paralleli (1, 2, 3 o 4, con 2 proposto; chiesti anche con `<n>`). I round di review esterna (come da config, 1, 2, 3 o fino a convergenza, che si ferma a un round senza progressi, tetto rigido 5; al massimo quattro opzioni nel menu strutturato, le cinque complete come elenco numerato su un host che non lo ha). La domanda sulla review viene saltata se la plan review è disattivata o non c'è nessuna CLI di reviewer indipendente installata (la CLI dell'host stesso non conta), e la scelta vale per ogni ticket `build` della run, sovrascrivendo `plan_review.rounds` per quella run.
137
+ - **Ordine.** Con tutto, lavora una mappa alla volta in ordine crescente di numero, la whiteboard per ultima. Dentro uno scope, prima il ticket lavorabile più vecchio. I ticket bloccati non vengono mai messi in coda, quelli segnati needs-you vengono saltati e i tuoi ticket interrotti (in pausa) nello scope vengono ripresi per primi.
138
+ - **Slot paralleli.** Con 1 slot, `isolation: auto` e `none` lavorano nel checkout corrente (un `worktree` o `clone` esplicito vale ancora, e il suo push o PR resta in attesa di te). Con più di 1 slot, ogni ticket ha il proprio workspace `trailhead/t<n>` accanto al checkout (`<repo>-t<n>`): un worktree qualunque cosa dica `isolation:`, un `clone` esplicito resta un clone, e un superrepo (submodule) passa a clone. Resta una sola sessione orchestratrice, ogni passo del motore gira come agente in background per il suo slot, e uno slot liberato prende il prossimo ticket più vecchio, così ticket di mappe diverse possono girare insieme.
139
+ - **Merge locale.** Con `git: main`, un branch di slot worktree verificato viene unito al trunk locale (fast-forward, altrimenti un merge commit), mai pushato, uno alla volta dopo il controllo della base; poi il ticket si risolve e i suoi dipendenti si sbloccano. Una run sequenziale con `worktree` o `clone` esplicito, una run con `git: pr` e gli slot clone trattengono il branch: il ticket è segnato needs-you per l'integrazione, con branch e workspace conservati ed elencati.
140
+ - **Ticket di merge.** Quando un branch di slot va in conflitto, il merge viene annullato (il trunk resta intatto), il branch resta separato con il suo workspace, e la run apre o aggiorna un **ticket di merge** per mappa (o per la whiteboard) nel momento in cui nasce il conflitto: un task needs-you che elenca ogni branch non unito con il suo ticket, workspace e file in conflitto, più un ordine di merge suggerito (dal più vecchio). Lo chiudi tu quando ogni branch è unito o scartato. Uno scope senza branch non uniti non ne ha, e i branch trattenuti non ne aprono. Il ticket in conflitto resta aperto, quindi i suoi dipendenti restano bloccati.
141
+ - **Acceptance.** La run esegue da sola i passi di acceptance che l'agente può fare, non sceglie mai skip e non simula mai un passo solo umano. I passi solo umani vanno in un **follow-up UAT** (riusato se ce n'è uno aperto, altrimenti un task sulla stessa mappa o sulla whiteboard, o un seed se i passi aspettano un trigger esterno) con la checklist numerata; poi il genitore si risolve e i suoi dipendenti si sbloccano. Gira solo l'`uat.setup` configurato, e un setup che fallisce segna il ticket needs-you.
142
+ - **Needs you.** Un ticket il cui prossimo passo serve a te viene parcheggiato subito e la run continua: la convergenza di una `decision`, un cambio di scope o di destination, un verdetto out-of-scope, l'instradamento di una riga rinviata, la chiusura di una mappa esaurita, una collisione di claim, fog da dissolvere, la checklist di un `task` HITL che solo tu puoi fare, un'azione del safety rail, uno stop sulla base, un setup UAT che fallisce, un errore nella ricerca del follow-up UAT, o una plan review i cui concern bloccanti sopravvivono ai round (citati per intero). Riceve un commento `NEEDS YOU` che dice cosa devi fare, viene disassegnato e porta l'etichetta `trailhead:needs-you`. Resta sulla tua frontier e sulla mappa e `/trailhead:work` lo offre ancora, ma le run lo saltano finché non lo sblocchi (un claim con `work` o `quick` lo pulisce, oppure togli l'etichetta a mano).
143
+ - **Safety rail (sempre attivo, hardcoded).** Una release, un `git push`, l'apertura di una PR, una cancellazione o un force, o qualsiasi altra azione verso l'esterno segna il ticket needs-you e la run continua. I commit locali e il merge locale dello slot non sono azioni del rail.
144
+ - **Budget di fallimento.** Un ticket che non verifica riceve round di fix extra (una regola di nessun progresso più un tetto hardcoded di 3), poi viene messo in pausa mantenendo il claim e saltato. Un solo ticket bloccato non chiude mai la run.
145
+ - **Record, interruzione, ripresa.** Ogni record (commenti del motore, commit, follow-up UAT, commenti `NEEDS YOU`, ticket di merge, checkpoint `PAUSED`) viene scritto sul tracker quando nasce, quindi un crash non perde nulla; la riga di avanzamento in chat, che nomina i ticket negli slot, è solo uno specchio. Stop/Esc è l'unico arresto a metà run e non scrive nulla: ogni ticket in corso mantiene il claim ed Esc lascia la stessa traccia di un crash. Per riprendere, rilancia `/trailhead:auto`: prima recupera un merge interrotto, poi riprende i ticket in pausa o in crash nei loro workspace, scrivendo il checkpoint `PAUSED` mancante (che nomina workspace e branch) su ogni ticket interrotto mentre lo riprende, e continua. Nella stessa sessione riusa le scelte della run; in una nuova sessione chiede di nuovo. I ticket needs-you non vengono riconsiderati.
146
+ - **Riepilogo della run.** Ogni run finisce con un riepilogo completo in chat, che linka i record che riassume: perché si è fermata, cosa è stato risolto, cosa è stato unito (ticket uniti, branch lasciati non uniti con i file in conflitto, ticket di merge, e di quanti commit il trunk locale è avanti rispetto a `origin`; il push è tuo), e cosa resta (ticket needs-you e perché, ticket in pausa o bloccati, dipendenti bloccati, follow-up UAT aperti). Viene anche pubblicato come commento su ogni mappa toccata, con solo la parte di quella mappa; la parte della whiteboard resta solo in chat, e le mappe non toccate non ricevono nulla.
147
+
148
+ `auto` è ortogonale a `effort` e `models.*` e non ha una superficie di config propria: le domande iniziali la sostituiscono.
149
+
115
150
  ### Un esempio pratico: "add social login"
116
151
 
117
152
  Destination: *gli utenti accedono con Google e GitHub, accanto a email/password.* Il charting la dirama in sei ticket (uno per ogni tipo) cablati dalle loro dipendenze:
@@ -181,7 +216,7 @@ Ogni verbo è anche un comando con namespace (`/trailhead:new`, `/trailhead:work
181
216
  | `/trailhead:pause [note]` | mette un checkpoint sul ticket in gioco così chiunque può riprenderlo |
182
217
  | `/trailhead:ticket <type> <title>` | apre un ticket al volo (diverge brevemente prima: un atto di micro-charting) |
183
218
  | `/trailhead:split [ticket]` | splitta un ticket sovradimensionato in figli, supersede l'originale |
184
- | `/trailhead:auto [map]` | esegue la frontier in autonomia, un ticket dopo l'altro, fino a uno stop condition (safety rail / fog / una decisione che serve l'umano) o all'esaurimento; sospende la regola un-ticket-per-sessione |
219
+ | `/trailhead:auto [map]` | esegue uno scope scelto (una mappa, la whiteboard o tutto) fino a 4 slot paralleli, fino all'esaurimento o a uno stop; parcheggia ciò che serve a te |
185
220
  | `/trailhead:grill [topic]` | esegue una sessione di grilling autonoma su una decision/topic |
186
221
  | `/trailhead:map` | mostra la mappa a bassa risoluzione (destination, decisions, frontier, fog) |
187
222
  | `/trailhead:dashboard` | mostra la dashboard del repo: l'indice pinnato di ogni mappa aperta, la whiteboard, e i conteggi live |
@@ -193,7 +228,7 @@ La mod di trailhead (Claude Code 2.1.287+) è inclusa nel plugin e disegna il la
193
228
 
194
229
  ![Il pannello della mappa affiancato al transcript: mappe aperte, frontier con un pulsante work per ticket, in corso, bloccati e un footer con refresh e close](assets/screenshots/map-panel.png)
195
230
 
196
- Il pannello della mappa elenca le mappe aperte da consultare, la frontier con un pulsante `work` per ogni ticket che pulisce il contesto e avvia il ticket, chi tiene cosa (il tuo ticket è segnato `this session`), i follow-up UAT in sospeso in un gruppo `UAT` (ognuno con il ticket che collauda), i ticket in pausa in un gruppo `Paused` e cosa è bloccato. Il footer ha `refresh` e `close`. `/trailhead:panel` lo apre e lo chiude, e si apre da solo una volta in fullscreen.
231
+ Il pannello della mappa elenca le mappe aperte da consultare, i ticket che una run autonoma ha parcheggiato per te in un gruppo `Needs you` in cima, la frontier con un pulsante `work` per ogni ticket che pulisce il contesto e avvia il ticket, chi tiene cosa (il tuo ticket è segnato `this session`), i follow-up UAT in sospeso in un gruppo `UAT` (ognuno con il ticket che collauda), i ticket in pausa in un gruppo `Paused` e cosa è bloccato. Una riga dim sotto la riga Whiteboard somma i ticket aperti di tutte le mappe aperte più la whiteboard, lo stesso numero che dichiara `/trailhead:map` (`at least N` quando le mappe aperte sono più di quelle lette dal pannello). Il footer ha `refresh` e `close`. `/trailhead:panel` lo apre e lo chiude, e si apre da solo una volta in fullscreen.
197
232
 
198
233
  ![Il pannello che mostra un'altra mappa, con l'intestazione viewing e active e il pulsante back to active](assets/screenshots/map-panel-viewing.png)
199
234
 
@@ -340,7 +375,7 @@ trailhead › ✓ Sign in with GitHub → lands on /dashboard (screenshot
340
375
  ? Step 6 is the haptic on "Signed in": does it buzz once, softly?
341
376
  ```
342
377
 
343
- Config: `acceptance.browser` (`auto` default, `on`, `off`) governa la guida del browser; `off` mantiene i controlli AI-driven via comandi e suite, e chiede comunque chi guida. `testing.webapp` / `testing.url` dicono se l'app è guidabile da browser e dove. Sotto `/trailhead:auto` va in AI-driven; i passi che solo tu puoi fare non vengono mai simulati: il ticket viene messo da parte ed elencato nel riepilogo della run; `/trailhead:auto` non sceglie mai skip.
378
+ Config: `acceptance.browser` (`auto` default, `on`, `off`) governa la guida del browser; `off` mantiene i controlli AI-driven via comandi e suite, e chiede comunque chi guida. `testing.webapp` / `testing.url` dicono se l'app è guidabile da browser e dove. Sotto `/trailhead:auto` va in AI-driven; i passi che solo tu puoi fare non vengono mai simulati: vanno in un follow-up UAT con la checklist, e il ticket genitore si risolve; `/trailhead:auto` non sceglie mai skip.
344
379
 
345
380
  ---
346
381
 
@@ -351,7 +386,7 @@ Tutto ciò di cui la mappa ha bisogno è espresso come label GitHub, così lo st
351
386
  - **Strutturali:** `trailhead:map`, `trailhead:ticket`
352
387
  - **Anchor con scope di repo (uno ciascuno per repo, pinnati):** `trailhead:codebase` 🧱 (la codebase map distillata), `trailhead:conventions` 📜 (il modo di lavorare), `trailhead:dashboard` 📊 (l'indice pinnato di mappe + whiteboard + conteggi); ogni titolo porta la sua icona (come il 🗺 della mappa) così gli anchor pinnati si distinguono a colpo d'occhio
353
388
  - **Type (una per ticket):** 🧭 `trailhead:decision` · 🔬 `research` · 🎨 `prototype` · 🔨 `build` · 🐛 `bug` · 🔧 `task`
354
- - **State:** `trailhead:blocked` (ha un blocker aperto) · `seed` (parcheggiato su un trigger) · `out-of-scope` (chiuso, oltre la destination) · `superseded` (chiuso, splittato in figli) · `paused` (con checkpoint, fuori dalla frontier) · `unverified` (in quarantena finché un maintainer non lo adotta) · `fog` (tenuto aperto per chiarimenti)
389
+ - **State:** `trailhead:blocked` (ha un blocker aperto) · `seed` (parcheggiato su un trigger) · `out-of-scope` (chiuso, oltre la destination) · `superseded` (chiuso, splittato in figli) · `paused` (con checkpoint, fuori dalla frontier) · `needs-you` (parcheggiato per te da una run autonoma: le run lo saltano, resta sulla tua frontier) · `unverified` (in quarantena finché un maintainer non lo adotta) · `fog` (tenuto aperto per chiarimenti)
355
390
  - **Container:** `trailhead:whiteboard` (un ticket sciolto, senza mappa, fuori dalla frontier di ogni mappa, sulla frontier propria della whiteboard)
356
391
  - **Marker:** `trailhead:uat` (un follow-up UAT in sospeso; mai un filtro della frontier) · `trailhead:map-<n>` (lega un ticket alla sua mappa)
357
392
 
@@ -450,24 +485,25 @@ Esempio di file di config (la stessa forma funziona per il `.trailhead/config.js
450
485
 
451
486
  ## 🪝 Hooks
452
487
 
453
- Oltre alle istruzioni della skill, trailhead include otto hook propri (in `hooks/`, self-contained via `${CLAUDE_PLUGIN_ROOT}`) che *impongono* le parti della disciplina che non ci si dovrebbe fidare che il modello ricordi:
488
+ Oltre alle istruzioni della skill, trailhead include nove hook propri (in `hooks/`, self-contained via `${CLAUDE_PLUGIN_ROOT}`) che *impongono* le parti della disciplina che non ci si dovrebbe fidare che il modello ricordi:
454
489
 
455
490
  - **Commit guard** (`PreToolUse` su `git commit`): blocca in modo netto un commit il cui subject non è [Conventional Commits](https://www.conventionalcommits.org), e blocca in modo netto qualsiasi trailer `Co-Authored-By`. Questo rispecchia la validazione dei commit di GSD.
456
491
  - **Secret guard** (`PreToolUse` sulle scritture `gh` di issue/PR): trailhead pubblica molto sul tracker (corpi dei ticket, commenti del motore, issue codebase/conventions, risoluzioni). Questo scansiona ciò che una scrittura `gh issue`/`gh pr`/`gh api` sta per pubblicare (l'inline `--body`, un heredoc, o un `--body-file`) e lo **blocca in modo netto** se corrisponde a un pattern di credenziale (chiavi private, token GitHub/AWS/OpenAI/Slack/Google/Stripe, JWT, o un `password`/`secret`/`token=…` hardcoded). Il blocco è un **segnale di pulisci-e-riprova, non un vicolo cieco**: trailhead redige il valore segnalato sul posto (`<REDACTED>` o un riferimento a env-var) e ripubblica automaticamente, così il contenuto pulito atterra comunque: il segreto semplicemente non raggiunge mai il tracker. Resta un *blocco* fail-safe anziché una silenziosa auto-riscrittura di proposito: un blocco non fa mai leak, mentre una redazione che silenziosamente non è stata applicata sì. Non scansiona mai le letture, solo le scritture in uscita.
457
492
  - **Install guard** (`PreToolUse` sulle installazioni di pacchetti): una barriera anti-slopsquatting. Blocca in modo netto un'installazione da registry pubblico (npm / PyPI / crates.io / Go / RubyGems) finché il pacchetto non è verificato (similarità typosquat a nomi popolari, età / prima apparizione, numero di download, metadati del repository presenti e coerenti); una volta verificato, ri-esegui il comando esatto con il prefisso `TRAILHEAD_VERIFIED_INSTALL=1` per procedere. Il motore Execute / Fix porta lo stesso block-and-ask. Legge i comandi come una shell: il testo dentro virgolette o heredoc (un `echo`, un messaggio di commit) non è un'installazione, le installazioni da lockfile passano e wrapper come `sudo`, `time`, `env` o `xargs` vengono attraversati.
458
493
  - **Search guard** (`PreToolUse` su `Bash`): un backstop strutturale per la regola di igiene dei comandi di ricerca (mai `cd` seguito da una lettura/ricerca su percorso relativo). Blocca in modo netto un singolo comando Bash che sia insieme un cambio di directory (`cd`/`pushd`/`env -C`) e, in un'istruzione unita o successiva, una lettura/ricerca su percorso relativo (`grep`/`rg`/`ag`/`cat`/`head`/`tail`/`wc`/`nl`/`sed`/`awk`/`less`/`more`, o `git grep`/`log`/`show`/`diff` senza `-C`), così la forma che fa scattare una regola di deny su `Read()` sotto bypass permissions (ripiegando su un prompt di approvazione manuale invece di girare headless) non viene mai eseguita, anziché essere solo scoraggiata nella prosa dell'agente. I target di redirezione (`2>/dev/null`) non contano come letture, e un `cd` alla directory della sessione stessa non la attiva. Solo Claude Code; non è proiettata su Codex, che non ha una regola di deny su `Read()`.
459
494
  - **Body guard** (`PreToolUse` sulle scritture `gh` di body issue/PR): blocca in modo netto una scrittura `gh issue edit` / `gh pr edit` / `gh api` che imposterebbe il body di un issue a vuoto o solo spazi, così un read-modify-write la cui lettura è tornata con un body vuoto/degradato (es. un HTTP 5xx transitorio su `gh issue view --json body`) non può mai cancellare il body esistente.
495
+ - **Labels guard** (`PreToolUse` su `gh issue create`): controlla il set di label di un ticket già alla creazione. Una create con un'etichetta `trailhead:*` deve portare la forma completa del ticket: `trailhead:ticket`, esattamente un tipo (`decision`, `research`, `prototype`, `build`, `bug`, `task`) e esattamente una destinazione (una `trailhead:map-<n>` oppure `trailhead:whiteboard`, mai entrambe, mai due mappe); il blocco nomina cosa manca o è in conflitto e chiede di riprovare con tutte le label nella stessa create, mai con un `--add-label` successivo. Gli issue non-ticket (`trailhead:map`, `dashboard`, `codebase`, `conventions`, `fog`, `unverified`) sono esenti. Anche una create senza alcuna label `trailhead:*` viene bloccata, ma solo dentro un repo trailhead (la working directory è in un work tree git il cui top level ha `.trailhead/`) e non quando punta a un altro repo con `-R`/`--repo`; un issue non-trailhead voluto si esenta anteponendo `TRAILHEAD_PLAIN_ISSUE=1` al comando. Un valore di label non risolvibile staticamente (`$VAR`, backtick, `$(...)`) non viene mai bloccato.
460
496
  - **Secret-read guard** (`PreToolUse` su `Read|Bash`): blocca in modo netto la lettura di `.env`/`.env.*`/`.secrets`, sia tramite il tool Read sia tramite un comando Bash (un operando file, il valore di un'opzione, o un target di redirect `<`). È una decisione `deny` a livello di hook anziché una regola di permesso su `Read()`, quindi i segreti restano illeggibili senza attivare il prompt di approvazione `cd-compound-read` di Claude Code 2.1.259.
461
497
  - **Mockup-link guard** (`PreToolUse` su `AskUserQuestion`): rete di sicurezza strutturale per la regola della tecnica Prototype secondo cui la richiesta di approvazione di un mockup porta con sé il link al mockup. Blocca un `AskUserQuestion` che chiede di approvare un mockup (o di scegliere tra varianti) quando né il testo della risposta subito prima né la domanda di approvazione stessa contengono un URL o un path (se il messaggio corrente non è ancora nel transcript, la guardia non può giudicare e lascia passare), così l'agente mette il link cliccabile nel messaggio prima di richiedere. Su Codex, che non ha il tool `AskUserQuestion` e pone le domande come elenco numerato in testo, il gemello `mockup-link-stop` gira su `Stop`: se la risposta finale chiede quell'approvazione senza link, Codex fa proseguire il turno e l'agente riscrive la domanda col link.
462
498
  - **Issue injection scanner** (`PostToolUse` sulle letture `gh`): trailhead legge testo di issue/PR/commenti scritto da chiunque abbia accesso al repo, cioè input non fidato. Quando quel testo contiene frasi di prompt-injection, l'hook inietta un avviso che ricorda all'agente di trattarlo come dati, mai come comandi. Solo advisory: non blocca mai.
463
499
 
464
- I guard di commit, secret, install, search e body girano in **un solo processo**: una singola registrazione `trailhead-bash-guards.js` li valuta in quell'ordine, così una chiamata Bash paga un solo avvio di node invece di cinque (tre processi hook per chiamata Bash in tutto: l'hook unificato, il secret-read guard e l'injection scanner). Restano cinque guard distinti con verdetti invariati; vince il primo blocco, e un guard che va in errore consente da solo senza fermare gli altri. Anche un guard il cui modulo manca o è rotto viene saltato, ma mai in silenzio: l'hook segnala alla sessione quale guard non ha controllato il comando, così un'installazione parziale non passa inosservata. Su Codex lo stesso hook è registrato con `--guards=commit,secret,install,body`, perché lì non c'è la search guard.
500
+ I guard di commit, secret, install, search, body e labels girano in **un solo processo**: una singola registrazione `trailhead-bash-guards.js` li valuta in quell'ordine, così una chiamata Bash paga un solo avvio di node invece di sei (tre processi hook per chiamata Bash in tutto: l'hook unificato, il secret-read guard e l'injection scanner). Restano sei guard distinti con verdetti invariati; vince il primo blocco, e un guard che va in errore consente da solo senza fermare gli altri. Anche un guard il cui modulo manca o è rotto viene saltato, ma mai in silenzio: l'hook segnala alla sessione quale guard non ha controllato il comando, così un'installazione parziale non passa inosservata. Su Codex lo stesso hook è registrato con `--guards=commit,secret,install,body,labels`, perché lì non c'è la search guard.
465
501
 
466
502
  I guard secret, body e secret-read **falliscono in modo chiuso sui costrutti shell che non sanno interpretare con certezza** (`eval`, `source` / `.`, un `${ cmd; }` di bash 5.3, `printf -v`, la definizione di una funzione, un alias, una chiave body di `gh api` quotata, un backslash-a-capo dentro una parola): quando uno di questi può nascondere o precedere una scrittura (o sta accanto a un path segreto) il comando viene bloccato con un messaggio "riscrivilo in forma semplice" invece di essere emulato; gli stessi costrutti senza scritture o segreti vicini passano.
467
503
 
468
504
  Accanto a questi trailhead installa un **hook git `commit-msg`** (un wrapper POSIX `sh`, quindi funziona anche nei repo con `"type": "module"`) che impone le stesse regole di commit su ogni host, più il trailer `Refs: #<n>` mentre c'è un ticket di sessione attivo. Se non può girare (script mancante, nessun `node`) stampa `trailhead: commit-msg hook inactive (<reason>); commit rules not enforced.` su stderr e lascia passare il commit; un hook già installato viene aggiornato al prossimo avvio di un ticket.
469
505
 
470
- Tutti e otto sono crash-safe (qualsiasi errore → allow) e attivi ogni volta che il plugin è installato. I guard di commit, secret, install, search, body e secret-read quindi si applicano in **ogni** repo, non solo nei progetti trailhead: intenzionale, dato che i conventional commit, niente `Co-Authored-By`, nessun segreto leakato, nessuna installazione non verificata, niente `cd`-poi-lettura-relativa, nessun body issue cancellato, e niente `.env`/`.secrets` leggibili sono regole permanenti di trailhead. Per fare opt-out, disabilita gli hook del plugin nelle impostazioni di Claude Code.
506
+ Tutti e nove sono crash-safe (qualsiasi errore → allow) e attivi ogni volta che il plugin è installato. I guard di commit, secret, install, search, body e secret-read quindi si applicano in **ogni** repo, non solo nei progetti trailhead: intenzionale, dato che i conventional commit, niente `Co-Authored-By`, nessun segreto leakato, nessuna installazione non verificata, niente `cd`-poi-lettura-relativa, nessun body issue cancellato, e niente `.env`/`.secrets` leggibili sono regole permanenti di trailhead. Per fare opt-out, disabilita gli hook del plugin nelle impostazioni di Claude Code.
471
507
 
472
508
  ---
473
509
 
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  🇬🇧 **English** · 🇮🇹 [Italiano](README.it.md)
8
8
 
9
- [![website](https://img.shields.io/badge/website-trailhead.marcomigozzi.it-3C7A5A?style=flat-square)](https://trailhead.marcomigozzi.it) [![site CI](https://img.shields.io/github/actions/workflow/status/ToRvaLDz/trailhead/site-ci.yml?branch=main&style=flat-square&label=site%20CI)](https://github.com/ToRvaLDz/trailhead/actions/workflows/site-ci.yml) [![npm](https://img.shields.io/npm/v/@marcomigozzi/trailhead?style=flat-square&label=npm&color=CB3837)](https://www.npmjs.com/package/@marcomigozzi/trailhead) [![license](https://img.shields.io/github/license/ToRvaLDz/trailhead?style=flat-square&color=3C7A5A)](LICENSE) ![hosts](https://img.shields.io/badge/hosts-Claude%20Code%20%C2%B7%20Codex-1E2A54?style=flat-square) ![commands](https://img.shields.io/badge/commands-24%20verbs-0e8a16?style=flat-square) ![guardrails](https://img.shields.io/badge/guardrails-8%20hooks-e8710a?style=flat-square)
9
+ [![website](https://img.shields.io/badge/website-trailhead.marcomigozzi.it-3C7A5A?style=flat-square)](https://trailhead.marcomigozzi.it) [![site CI](https://img.shields.io/github/actions/workflow/status/ToRvaLDz/trailhead/site-ci.yml?branch=main&style=flat-square&label=site%20CI)](https://github.com/ToRvaLDz/trailhead/actions/workflows/site-ci.yml) [![npm](https://img.shields.io/npm/v/@marcomigozzi/trailhead?style=flat-square&label=npm&color=CB3837)](https://www.npmjs.com/package/@marcomigozzi/trailhead) [![license](https://img.shields.io/github/license/ToRvaLDz/trailhead?style=flat-square&color=3C7A5A)](LICENSE) ![hosts](https://img.shields.io/badge/hosts-Claude%20Code%20%C2%B7%20Codex-1E2A54?style=flat-square) ![commands](https://img.shields.io/badge/commands-24%20verbs-0e8a16?style=flat-square) ![guardrails](https://img.shields.io/badge/guardrails-9%20hooks-e8710a?style=flat-square)
10
10
 
11
11
  </div>
12
12
 
@@ -32,6 +32,7 @@ The design choices that fall out of it:
32
32
 
33
33
  - **Everything lives on GitHub Issues.** The map is the parent issue; each ticket is a child issue; discussion, plan, and verification are *comments*. There is no `.planning/` directory: the repo holds code only, and the plan is always visible to anyone with the repo.
34
34
  - **Execution happens inside the map.** Unlike pure Wayfinder ("plan, don't do"), build tickets graduate from the fog and are executed as children of the map. The destination is the **working artifact** (a deployed app), not a spec document, though a project can override that and stop at the spec.
35
+ - **Hand it a map and come back.** `/trailhead:auto` drains a map or the whiteboard unattended, in up to four parallel slots, and leaves a run report, the tickets that need you, and local commits for you to push. See [Autonomous run](#-autonomous-run-auto).
35
36
  - **Lean by default.** The heavy machinery (codebase mapping, systematic debugging) runs once where it earns its keep; the per-ticket cycle stays light.
36
37
 
37
38
  - **Built for teams, because the plan is shared.** The tracker is the single source of truth, so the map is a **shared workspace**, not one person's local file. Many people (and their agents) work it at once, and the mechanics keep them from colliding:
@@ -70,7 +71,7 @@ npx @marcomigozzi/trailhead --dir=<path> # target a specific config dir
70
71
  ```
71
72
  On **Claude Code** it copies the skill (+ its `references/`), the `/trailhead:*` commands, the hooks (into `hooks/`, registered in `settings.json`) including a SessionStart **update check**, and the templates (label-guard workflow, statusline, the git `commit-msg` hook and the helper scripts the engine runs), idempotently.
72
73
 
73
- On **Codex CLI** (`--codex`) it projects a native Codex skill invoked as **`$trailhead`** (`$trailhead work`, `$trailhead new "idea"`, …), one thin **`$trailhead-<verb>`** skill per verb for discoverability, the seven guardrails as native Codex **hooks** (`~/.codex/hooks.json`, enabling `features.hooks`; the four Bash guards share one process behind a single registration; the search guard is Claude-Code-only and isn't projected to Codex), and per-technique subagent model pins from **`models.codex.*`** into `~/.codex/agents/`. Codex **0.145.0+** is required (the installer gates on `codex --version`). The `artifacts` mockup mode (Claude Code's `Artifact` tool) isn't available on Codex; the cross-host `design: stitch` mode (Google Stitch MCP) is the hosted option there, with local disk as the fallback.
74
+ On **Codex CLI** (`--codex`) it projects a native Codex skill invoked as **`$trailhead`** (`$trailhead work`, `$trailhead new "idea"`, …), one thin **`$trailhead-<verb>`** skill per verb for discoverability, the eight guardrails as native Codex **hooks** (`~/.codex/hooks.json`, enabling `features.hooks`; the five Bash guards share one process behind a single registration; the search guard is Claude-Code-only and isn't projected to Codex), and per-technique subagent model pins from **`models.codex.*`** into `~/.codex/agents/`. Codex **0.145.0+** is required (the installer gates on `codex --version`). The `artifacts` mockup mode (Claude Code's `Artifact` tool) isn't available on Codex; the cross-host `design: stitch` mode (Google Stitch MCP) is the hosted option there, with local disk as the fallback.
74
75
 
75
76
  Re-run the install to update, or run **`/trailhead:update`** (Claude) / **`$trailhead update`** (Codex) from inside the agent: it detects how trailhead was installed and installs the newer version where that is safe (a `git pull` for a dev-symlink, an `npx` re-run for npm, or `/plugin update` for the plugin). When a newer version exists, the statusline shows a `⬆ trailhead <version>` flag (Claude).
76
77
 
@@ -106,12 +107,46 @@ These three fill GitHub's **3 pinned-issue slots**, so a repo's trailhead anchor
106
107
  **The lifecycle.** Every project walks the same loop:
107
108
 
108
109
  1. **Start**: `/trailhead:new "<idea>"` (greenfield) or `/trailhead:adopt` (existing code: map the codebase once, then go lean). This *charts the map*: name the destination, then map the frontier breadth-first into the first tickets and the fog.
109
- 2. **Work the frontier, one ticket per session**: `/trailhead:work` takes the next takeable ticket (or one you name) and runs the engine for its type: `research` gathers a fact · `decision` diverges options then grills to choose · `prototype` makes a rough artifact to react to · `build` runs discuss → plan → execute → verify · `bug` runs repro → diagnose → fix → verify · `task` is manual plumbing that unblocks a decision. `/trailhead:auto` chains the frontier unattended, one ticket after another, stopping only at the safety rail, fog, or a human-necessary decision. At a ticket's start, `work` and `quick` show a short brief with a **Proceed / Change something** choice; it is skipped (a one-line note instead) for a ticket you created or just settled in a grilling this session, and `auto` never asks.
110
+ 2. **Work the frontier, one ticket per session**: `/trailhead:work` takes the next takeable ticket (or one you name) and runs the engine for its type: `research` gathers a fact · `decision` diverges options then grills to choose · `prototype` makes a rough artifact to react to · `build` runs discuss → plan → execute → verify · `bug` runs repro → diagnose → fix → verify · `task` is manual plumbing that unblocks a decision. `/trailhead:auto` runs a chosen scope unattended, in up to 4 parallel slots, until the frontier is exhausted or you stop it, parking whatever needs you (a safety-rail action, fog, a human-necessary decision; see below). At a ticket's start, `work` and `quick` show a short brief with a **Proceed / Change something** choice; it is skipped (a one-line note instead) for a ticket you created or just settled in a grilling this session, and `auto` never shows it.
110
111
  3. **Close & unblock**: resolve the ticket (comment + close), gist it into `Decisions so far`, and drop `trailhead:blocked` from any dependent whose last blocker just closed, graduating it onto the frontier.
111
112
  4. **Repeat** until the frontier is empty and the fog has cleared: the destination (a working artifact) is reached.
112
113
 
113
114
  Along the way: **capture** ideas mid-work without derailing, **split** a ticket that grew too big, **pause/resume** across sessions, and let several people work unblocked tickets in parallel.
114
115
 
116
+ ### 🤖 Autonomous run: `auto`
117
+
118
+ `/trailhead:auto [map]` runs a chosen scope unattended, ticket after ticket, resolving each with its normal engine instead of handing back to you in between. It suspends the one-ticket-per-session rule, and it never re-engages on its own: you start every run.
119
+
120
+ `auto` drains a map (or the whiteboard, or everything) unattended, ticket after ticket, in 1 to 4 parallel slots. `/trailhead:work` is the other mode: one ticket per session, with you in the loop (briefs, grilling, UAT choices). Reach for `auto` when the frontier holds well-specified build or bug work you don't need to steer; use `work` for decisions, fuzzy tickets, or when you want to drive. When you come back you find the run report in chat (also posted on each touched map), the tickets that need you labelled `trailhead:needs-you` with a `NEEDS YOU` comment, and local commits waiting for you to push: the run never pushes.
121
+
122
+ While it runs, the chat shows a progress line naming the tickets in the slots and what just resolved or comes next; at the end, the run report. Something like:
123
+
124
+ ```text
125
+ auto: slot 1 #42 add social login (verify) | slot 2 #43 session cookie (execute) | resolved #41, next #44
126
+
127
+ Run report
128
+ Stopped: frontier empty
129
+ Resolved: #41, #42, #43, #44
130
+ Merged: #41, #42, #43, #44 into main; local main is 7 commits ahead of origin (pushing is yours)
131
+ Remains: #45 needs you (which providers to support); #46 blocked by #45
132
+ ```
133
+
134
+ **Main checkout.** With more than one slot, the run merges each verified slot branch into the local trunk of the main checkout (a fast-forward when possible, else a merge commit), so if you are working in that checkout by hand your branch can move under you. Uncommitted tracked work stays protected: the merge stops on a dirty tree and the ticket is parked needs-you. Untracked files do not trip that check (one in the way of a file the merge brings in still stops it, parked the same way).
135
+
136
+ - **It asks first, before claiming anything, with no delegate option.** The run scope (one map, the whiteboard, or everything, each with its frontier count, then which map when you pick one map and several are open; `/trailhead:auto <n>` skips only this ask). The parallel slots (1, 2, 3 or 4, 2 proposed; asked under `<n>` too). The external review rounds (as configured, 1, 2, 3 or until convergence, which stops on a round with no progress, hard ceiling 5; at most four options in the structured menu, the full five as a numbered list on a host without one). The review ask is skipped when plan review is off or no independent reviewer CLI is installed (the host's own CLI does not count), and the choice holds for every `build` ticket of the run, overriding `plan_review.rounds` for that run.
137
+ - **Order.** Under everything it works one map at a time by ascending map number, the whiteboard last. Inside a scope, the oldest workable ticket first. Blocked tickets are never queued, tickets marked needs-you are skipped, and your own interrupted (paused) tickets in the scope are resumed first.
138
+ - **Parallel slots.** With 1 slot, `isolation: auto` and `none` work in the current checkout (an explicit `worktree` or `clone` still applies, and its push or PR is held for you). Above 1 slot, each ticket gets its own `trailhead/t<n>` workspace next to the checkout (`<repo>-t<n>`): a worktree whatever `isolation:` says, an explicit `clone` stays a clone, and a superrepo (submodules) escalates to a clone. It stays one orchestrating session, every engine step runs as a background agent for its slot, and a freed slot takes the next oldest ticket, so tickets of different maps can run together.
139
+ - **Local merge.** Under `git: main`, a verified worktree slot branch is merged into the local trunk (fast-forward, else a merge commit), never pushed, one at a time after the stale-base check; then the ticket resolves and its dependents unblock. A sequential run under an explicit `worktree` or `clone`, a run under `git: pr`, and clone slots hold the branch: the ticket is marked needs-you for integration, with its branch and workspace kept and listed.
140
+ - **Merge tickets.** When a slot branch conflicts, the merge is aborted (the trunk stays untouched), the branch stays separate with its workspace, and the run opens or updates one **merge ticket** per map (or for the whiteboard) the moment the conflict arises: a needs-you task listing each unmerged branch with its ticket, workspace and conflicting files, plus a suggested merge order (oldest first). You close it once every branch is merged or dropped. A scope with no unmerged branch gets none, and held branches open none. The conflicting ticket stays open, so its dependents stay blocked.
141
+ - **Acceptance.** The run drives the agent-performable acceptance steps itself, never picks skip and never fakes a human-only step. Human-only steps go to a **UAT follow-up** (reused if one is open, else a task on the same map or the whiteboard, or a seed when the steps wait on an external trigger) carrying the numbered checklist; then the parent resolves and its dependents unblock. Only the configured `uat.setup` runs, and a failing setup marks the ticket needs-you.
142
+ - **Needs you.** A ticket whose next step needs you is parked at once and the run continues: a `decision` convergence, a scope or destination change, an out-of-scope ruling, routing a deferred line, closing an exhausted map, a claim collision, fog to dissolve, a HITL `task` checklist only you can do, a safety-rail action, a stale-base stop, a failing UAT setup, an error looking up the UAT follow-up, or a plan review whose blocking concerns survive the rounds (quoted in full). It gets a `NEEDS YOU` comment saying what you must do, is unassigned, and carries the `trailhead:needs-you` label. It stays on your frontier and map and `/trailhead:work` still offers it, but runs skip it until you clear it (a `work` or `quick` claim clears it, or remove the label by hand).
143
+ - **Safety rail (always on, hardcoded).** A release, `git push`, opening a PR, a delete or force, or any other outward-facing action marks the ticket needs-you and the run continues. Local commits and the local slot merge are not rail actions.
144
+ - **Failure budget.** A ticket that will not verify gets extra fix rounds (a no-progress rule plus a hardcoded ceiling of 3), then is paused keeping its claim and skipped. One stuck ticket never ends the run.
145
+ - **Records, interruption, resume.** Every record (engine comments, commits, UAT follow-ups, `NEEDS YOU` comments, merge tickets, `PAUSED` checkpoints) is written on the tracker when it arises, so a crash loses nothing; the chat progress line, which names the tickets in the slots, is only a mirror. Stop/Esc is the only mid-run halt, and it writes nothing: every in-flight ticket keeps its claim and Esc leaves the same trace as a crash. To resume, run `/trailhead:auto` again: it first recovers an interrupted merge, then resumes paused or crashed tickets in their own workspaces, writing the missing `PAUSED` checkpoint (naming workspace and branch) on each crashed one as it resumes it, then continues. In the same session it reuses the run's choices; a new session asks again. Needs-you tickets are not reconsidered.
146
+ - **Run report.** Every run ends with one summary in full in chat, linking the records it summarises: why it stopped, what resolved, what merged (merged tickets, branches left unmerged with their conflicting files, merge tickets, and how many commits the local trunk is ahead of `origin`; pushing is yours), and what remains (needs-you tickets and why, paused or stuck tickets, blocked dependents, UAT follow-ups opened). It is also posted as a comment on each touched map with only that map's part; the whiteboard part stays in chat only, and untouched maps get nothing.
147
+
148
+ `auto` is orthogonal to `effort` and `models.*` and has no config surface of its own: the asks replace it.
149
+
115
150
  ### A worked example: "add social login"
116
151
 
117
152
  Destination: *users sign in with Google and GitHub, alongside email/password.* Charting fans it into six tickets (one of each type) wired by their dependencies:
@@ -181,7 +216,7 @@ Every verb is also a namespaced command (`/trailhead:new`, `/trailhead:work`, `/
181
216
  | `/trailhead:pause [note]` | checkpoint the ticket in play so anyone can resume it |
182
217
  | `/trailhead:ticket <type> <title>` | open a ticket on the fly (diverges briefly first: a micro-charting act) |
183
218
  | `/trailhead:split [ticket]` | split an oversized ticket into children, supersede the original |
184
- | `/trailhead:auto [map]` | run the frontier autonomously, one ticket after another, until a stop condition (safety rail / fog / a human-necessary decision) or exhaustion; suspends one-ticket-per-session |
219
+ | `/trailhead:auto [map]` | run a chosen scope (one map, the whiteboard or everything) in up to 4 parallel slots, until exhaustion or a stop; parks what needs you |
185
220
  | `/trailhead:grill [topic]` | run a standalone grilling session on a decision/topic |
186
221
  | `/trailhead:map` | show the low-res map (destination, decisions, frontier, fog) |
187
222
  | `/trailhead:dashboard` | show the repo dashboard: the pinned index of every open map, the whiteboard, and live counts |
@@ -193,7 +228,7 @@ The trailhead mod (Claude Code 2.1.287+) ships inside the plugin and draws the w
193
228
 
194
229
  ![The map panel docked beside the transcript: open maps, frontier with a work button per ticket, in progress, blocked, and a footer with refresh and close](assets/screenshots/map-panel.png)
195
230
 
196
- The map panel lists the open maps to view, the frontier with a `work` button per ticket that clears the context and starts the ticket, who holds what (your own ticket is marked `this session`), the pending UAT follow-ups in a `UAT` group (each naming the ticket it tests), the paused ones in a `Paused` group, and what is blocked. The footer has `refresh` and `close`. `/trailhead:panel` toggles it, and it opens by itself once in fullscreen.
231
+ The map panel lists the open maps to view, the tickets an autonomous run parked for you in a `Needs you` group at the top, the frontier with a `work` button per ticket that clears the context and starts the ticket, who holds what (your own ticket is marked `this session`), the pending UAT follow-ups in a `UAT` group (each naming the ticket it tests), the paused ones in a `Paused` group, and what is blocked. A dim line under the Whiteboard row totals the open tickets across every open map plus the whiteboard, the same number `/trailhead:map` states (`at least N` when more open maps exist than the panel reads). The footer has `refresh` and `close`. `/trailhead:panel` toggles it, and it opens by itself once in fullscreen.
197
232
 
198
233
  ![The panel viewing another map, with a viewing and active header and a back to active button](assets/screenshots/map-panel-viewing.png)
199
234
 
@@ -340,7 +375,7 @@ trailhead › ✓ Sign in with GitHub → lands on /dashboard (screenshot
340
375
  ? Step 6 is the haptic on "Signed in": does it buzz once, softly?
341
376
  ```
342
377
 
343
- Config: `acceptance.browser` (`auto` default, `on`, `off`) governs browser driving; `off` keeps AI-driven checks via commands and the suite, and still asks who drives. `testing.webapp` / `testing.url` say whether the app is browser-drivable and where. Under `/trailhead:auto`, it goes AI-driven; steps only you can do are never faked: the ticket is set aside and listed in the run's summary; `/trailhead:auto` never picks skip.
378
+ Config: `acceptance.browser` (`auto` default, `on`, `off`) governs browser driving; `off` keeps AI-driven checks via commands and the suite, and still asks who drives. `testing.webapp` / `testing.url` say whether the app is browser-drivable and where. Under `/trailhead:auto`, it goes AI-driven; steps only you can do are never faked: they go to a UAT follow-up carrying the checklist, and the parent ticket resolves; `/trailhead:auto` never picks skip.
344
379
 
345
380
  ---
346
381
 
@@ -351,7 +386,7 @@ Everything the map needs is expressed as GitHub labels, so state is queryable in
351
386
  - **Structural:** `trailhead:map`, `trailhead:ticket`
352
387
  - **Repo-scoped anchors (one each per repo, pinned):** `trailhead:codebase` 🧱 (the distilled codebase map), `trailhead:conventions` 📜 (the way of working), `trailhead:dashboard` 📊 (the pinned index of maps + whiteboard + counts); each title carries its icon (like the map's 🗺) so the pinned anchors stand apart at a glance
353
388
  - **Type (one per ticket):** 🧭 `trailhead:decision` · 🔬 `research` · 🎨 `prototype` · 🔨 `build` · 🐛 `bug` · 🔧 `task`
354
- - **State:** `trailhead:blocked` (has an open blocker) · `seed` (parked on a trigger) · `out-of-scope` (closed, beyond the destination) · `superseded` (closed, split into children) · `paused` (checkpointed, off the frontier) · `unverified` (quarantined until a maintainer adopts it) · `fog` (kept open for clarification)
389
+ - **State:** `trailhead:blocked` (has an open blocker) · `seed` (parked on a trigger) · `out-of-scope` (closed, beyond the destination) · `superseded` (closed, split into children) · `paused` (checkpointed, off the frontier) · `needs-you` (parked by an autonomous run for you: runs skip it, it stays on your frontier) · `unverified` (quarantined until a maintainer adopts it) · `fog` (kept open for clarification)
355
390
  - **Container:** `trailhead:whiteboard` (a loose, map-less ticket, off every map's frontier, on the whiteboard's own)
356
391
  - **Marker:** `trailhead:uat` (a pending UAT follow-up; never a frontier filter) · `trailhead:map-<n>` (scopes a ticket to its map)
357
392
 
@@ -450,24 +485,25 @@ Example config file (the same shape works for the project `.trailhead/config.jso
450
485
 
451
486
  ## 🪝 Hooks
452
487
 
453
- Beyond the skill's instructions, trailhead ships eight of its own hooks (in `hooks/`, self-contained via `${CLAUDE_PLUGIN_ROOT}`) that *enforce* the parts of the discipline the model shouldn't be trusted to remember:
488
+ Beyond the skill's instructions, trailhead ships nine of its own hooks (in `hooks/`, self-contained via `${CLAUDE_PLUGIN_ROOT}`) that *enforce* the parts of the discipline the model shouldn't be trusted to remember:
454
489
 
455
490
  - **Commit guard** (`PreToolUse` on `git commit`): hard-blocks a commit whose subject isn't [Conventional Commits](https://www.conventionalcommits.org), and hard-blocks any `Co-Authored-By` trailer. This mirrors GSD's commit validation.
456
491
  - **Secret guard** (`PreToolUse` on `gh` issue/PR writes): trailhead posts a lot to the tracker (ticket bodies, engine comments, codebase/conventions issues, resolutions). This scans what a `gh issue`/`gh pr`/`gh api` write is about to post (the inline `--body`, a heredoc, or a `--body-file`) and **hard-blocks** it if it matches a credential pattern (private keys, GitHub/AWS/OpenAI/Slack/Google/Stripe tokens, JWTs, or a hardcoded `password`/`secret`/`token=…`). The block is a **clean-and-retry cue, not a dead end**: trailhead redacts the flagged value in place (`<REDACTED>` or an env-var reference) and re-posts automatically, so the cleaned content still lands: the secret just never reaches the tracker. It stays a fail-safe *block* rather than a silent auto-rewrite on purpose: a block never leaks, whereas a redaction that quietly failed to apply would. It never scans reads, only outbound writes.
457
492
  - **Install guard** (`PreToolUse` on package installs): a slopsquatting barrier. It hard-blocks a named public-registry install (npm / PyPI / crates.io / Go / RubyGems) until the package is vetted (typosquat similarity to popular names, age / first-seen date, download counts, repository metadata present and matching); once vetted, re-run the exact command prefixed with `TRAILHEAD_VERIFIED_INSTALL=1` to proceed. The Execute / Fix engine carries the same block-and-ask. It reads commands the way a shell does: text inside quotes or a heredoc (an `echo`, a commit message) is not an install, lockfile installs pass, and wrappers such as `sudo`, `time`, `env` or `xargs` are seen through.
458
493
  - **Search guard** (`PreToolUse` on `Bash`): structural backstop for the search-command-hygiene rule (never `cd` then read/search a relative path). It hard-blocks a single Bash command that both changes directory (`cd`/`pushd`/`env -C`) and, in a joined or later statement, reads/searches a relative path (`grep`/`rg`/`ag`/`cat`/`head`/`tail`/`wc`/`nl`/`sed`/`awk`/`less`/`more`, or `git grep`/`log`/`show`/`diff` without a `-C`), so the shape that trips a `Read()` deny rule under bypass permissions (falling back to a manual approval prompt instead of running headless) never runs at all, rather than merely being discouraged in the agent's own prose. Redirection targets (`2>/dev/null`) are not treated as reads, and a `cd` to the session's own directory does not arm it. Claude Code only; not projected to Codex, which has no such `Read()` deny rule.
459
494
  - **Body guard** (`PreToolUse` on `gh` issue/PR body writes): hard-blocks a `gh issue edit` / `gh pr edit` / `gh api` write that would set an issue body to empty or whitespace-only, so a read-modify-write whose read returned an empty/degraded body (e.g. a transient HTTP 5xx on `gh issue view --json body`) can never clobber the existing body.
495
+ - **Labels guard** (`PreToolUse` on `gh issue create`): checks the label set of a ticket at the create call. A create carrying any `trailhead:*` label must carry the whole ticket shape: `trailhead:ticket`, exactly one type (`decision`, `research`, `prototype`, `build`, `bug`, `task`) and exactly one destination (one `trailhead:map-<n>` or `trailhead:whiteboard`, never both, never two maps); the block names what is missing or conflicting and asks to retry with every label in the same create call, never a later `--add-label`. Non-ticket issues (`trailhead:map`, `dashboard`, `codebase`, `conventions`, `fog`, `unverified`) are exempt. A create with no `trailhead:*` label at all is blocked too, but only inside a trailhead repo (the working directory sits in a git work tree whose top level has `.trailhead/`) and not when aimed at another repo with `-R`/`--repo`; a deliberate non-trailhead issue opts out by prefixing the command with `TRAILHEAD_PLAIN_ISSUE=1`. A label value the guard cannot resolve statically (`$VAR`, backticks, `$(...)`) is never blocked.
460
496
  - **Secret-read guard** (`PreToolUse` on `Read|Bash`): denies reads of `.env`/`.env.*`/`.secrets`, whether via the Read tool or a Bash command (a file operand, an option value, or a `<` redirect target). This is a hook `deny` decision rather than a `Read()` permission rule, so secrets stay unreadable without arming Claude Code 2.1.259's `cd-compound-read` approval prompt.
461
497
  - **Mockup-link guard** (`PreToolUse` on `AskUserQuestion`): structural backstop for the Prototype rule that a mockup approval ask carries the mockup's link. It hard-blocks an `AskUserQuestion` that asks to approve a mockup (or pick between mockup variants) when neither the reply text right before it nor the approval question itself holds a URL or file path (when the current message is not yet in the transcript, the guard cannot judge and allows), so the agent puts the clickable link in the message before asking again. On Codex, which has no `AskUserQuestion` tool and asks as a plain-text numbered list, its twin `mockup-link-stop` runs on `Stop`: when the final reply asks for that approval without a link, Codex continues the turn so the agent rewrites the ask with it.
462
498
  - **Issue injection scanner** (`PostToolUse` on `gh` reads): trailhead reads issue/PR/comment text written by anyone with repo access, i.e. untrusted input. When that text contains prompt-injection phrases, the hook injects an advisory reminding the agent to treat it as data, never as commands. Advisory only: it never blocks.
463
499
 
464
- The commit, secret, install, search, and body guards run in **one process**: a single `trailhead-bash-guards.js` registration evaluates them in that order, so a Bash call pays one node start-up instead of five (three hook processes per Bash call in all: the merged hook, the secret-read guard, and the injection scanner). They remain five distinct guards with unchanged verdicts; the first block wins, and a guard that errors allows on its own without stopping the others. A guard whose module is missing or broken is skipped too, but never silently: the hook tells the session which guard did not check the command, so a partial install shows up. On Codex the same hook is registered with `--guards=commit,secret,install,body`, since there is no search guard there.
500
+ The commit, secret, install, search, body, and labels guards run in **one process**: a single `trailhead-bash-guards.js` registration evaluates them in that order, so a Bash call pays one node start-up instead of six (three hook processes per Bash call in all: the merged hook, the secret-read guard, and the injection scanner). They remain six distinct guards with unchanged verdicts; the first block wins, and a guard that errors allows on its own without stopping the others. A guard whose module is missing or broken is skipped too, but never silently: the hook tells the session which guard did not check the command, so a partial install shows up. On Codex the same hook is registered with `--guards=commit,secret,install,body,labels`, since there is no search guard there.
465
501
 
466
502
  The secret, body, and secret-read guards also **fail closed on shell constructs they cannot interpret with certainty** (`eval`, `source` / `.`, a bash 5.3 `${ cmd; }`, `printf -v`, a function definition, an alias, a quoted `gh api` body key, a backslash-newline inside a word): when one can hide or precede a write (or sits next to a secret path), the command is blocked with a "rewrite it in a simple form" message instead of being emulated; the same constructs with no write or secret nearby pass.
467
503
 
468
504
  Alongside them trailhead installs a git **`commit-msg` hook** (a POSIX `sh` wrapper, so it also works in `"type": "module"` repos) that enforces the same commit rules on every host, plus the `Refs: #<n>` trailer while a session ticket is active. If it cannot run (script missing, no `node`) it prints `trailhead: commit-msg hook inactive (<reason>); commit rules not enforced.` to stderr and lets the commit through; an installed hook is upgraded at the next ticket start.
469
505
 
470
- All eight are crash-safe (any error → allow) and active whenever the plugin is installed. The commit, secret, install, search, body, and secret-read guards therefore apply in **every** repo, not only trailhead projects: intentional, since conventional commits, no `Co-Authored-By`, no leaked secrets, no unvetted installs, no `cd`-then-relative-read, no clobbered issue bodies, and no readable `.env`/`.secrets` are trailhead's standing rules. To opt out, disable the plugin's hooks in your Claude Code settings.
506
+ All nine are crash-safe (any error → allow) and active whenever the plugin is installed. The commit, secret, install, search, body, and secret-read guards therefore apply in **every** repo, not only trailhead projects: intentional, since conventional commits, no `Co-Authored-By`, no leaked secrets, no unvetted installs, no `cd`-then-relative-read, no clobbered issue bodies, and no readable `.env`/`.secrets` are trailhead's standing rules. To opt out, disable the plugin's hooks in your Claude Code settings.
471
507
 
472
508
  ---
473
509
 
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ // Docs test for #313: /trailhead:auto is introduced at the entry points and each `auto`
3
+ // section opens with an intro, a run example and the main-checkout note.
4
+ // Plain asserts, no framework. Run: node bin/auto-intro-docs.test.js
5
+ const assert = require('assert');
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ const root = path.resolve(__dirname, '..');
10
+ const read = (rel) => fs.readFileSync(path.join(root, rel), 'utf8');
11
+ let passed = 0;
12
+ const ok = (name, cond) => { assert.ok(cond, name); passed++; };
13
+ const EM = String.fromCharCode(0x2014);
14
+
15
+ // Sezione dal suo heading fino al prossimo heading dello stesso livello (o superiore).
16
+ const section = (text, headingRe) => {
17
+ const m = headingRe.exec(text);
18
+ if (!m) return '';
19
+ const level = m[0].match(/^#+/)[0].length;
20
+ const rest = text.slice(m.index + m[0].length);
21
+ const next = new RegExp(`^#{1,${level}} `, 'm').exec(rest);
22
+ return next ? rest.slice(0, next.index) : rest;
23
+ };
24
+ // Testa della sezione: prima del primo bullet o sotto-heading.
25
+ const head = (sec) => {
26
+ const i = sec.search(/^(- |### )/m);
27
+ return i < 0 ? sec : sec.slice(0, i);
28
+ };
29
+
30
+ const docs = [
31
+ { name: 'README.md', rel: 'README.md', re: /^### .*Autonomous run: `auto`\s*$/m, it: false },
32
+ { name: 'README.it.md', rel: 'README.it.md', re: /^### .*Run autonoma: `auto`\s*$/m, it: true },
33
+ { name: 'workflow.md', rel: 'site/src/content/docs/docs/workflow.md', re: /^## Autonomous run: `auto`\s*$/m, it: false },
34
+ ];
35
+
36
+ for (const d of docs) {
37
+ const text = read(d.rel);
38
+ const sec = section(text, d.re);
39
+ const intro = head(sec);
40
+ ok(`${d.name}: auto section found`, sec.length > 0);
41
+ ok(`${d.name}: intro says it drains a map or the whiteboard unattended`, d.it
42
+ ? /svuota/i.test(intro) && /mappa/i.test(intro) && /whiteboard/i.test(intro) && /senza supervisione/i.test(intro)
43
+ : /drains/i.test(intro) && /map/i.test(intro) && /whiteboard/i.test(intro) && /unattended/i.test(intro));
44
+ ok(`${d.name}: intro contrasts with /trailhead:work`, intro.includes('/trailhead:work'));
45
+ ok(`${d.name}: intro names the run report, needs-you tickets and local commits to push`,
46
+ /run report/i.test(intro) && /needs-you/.test(intro) && /push/i.test(intro));
47
+ const block = /```[^\n]*\n([\s\S]*?)```/.exec(intro + sec.slice(intro.length));
48
+ const ex = block ? block[1] : '';
49
+ ok(`${d.name}: run example has a progress line`, /slot/i.test(ex) && /(resolved|next)/i.test(ex));
50
+ ok(`${d.name}: run example has a report excerpt`, ['Stopped:', 'Resolved:', 'Merged:', 'Remains:'].every((k) => ex.includes(k)));
51
+ ok(`${d.name}: main-checkout note (fast-forward or merge commit, dirty tracked tree stops, untracked does not)`, d.it
52
+ ? /fast-forward/.test(sec) && /merge commit/.test(sec) && /checkout principale/i.test(sec) && /non committat/i.test(sec) && /untracked/i.test(sec)
53
+ : /fast-forward/.test(sec) && /merge commit/.test(sec) && /main checkout/i.test(sec) && /uncommitted/i.test(sec) && /untracked/i.test(sec));
54
+ ok(`${d.name}: no em-dash`, !text.includes(EM));
55
+ }
56
+
57
+ for (const [name, rel] of [['README.md', 'README.md'], ['README.it.md', 'README.it.md']]) {
58
+ const t = read(rel);
59
+ const pitch = t.slice(0, t.search(/^## .*Install/m));
60
+ ok(`${name}: the pitch names /trailhead:auto`, pitch.includes('/trailhead:auto'));
61
+ }
62
+
63
+ const landing = read('site/src/components/landing/HowItWorks.astro');
64
+ ok('landing: mentions /trailhead:auto', /<code>\/trailhead:auto<\/code>/.test(landing));
65
+ const sentence = (landing.match(/[^.>]*<code>\/trailhead:auto<\/code>[^.<]*/) || [''])[0];
66
+ ok('landing: the new sentence has no em-dash', sentence.length > 0 && !sentence.includes(EM));
67
+
68
+ const gs = read('site/src/content/docs/docs/getting-started.mdx');
69
+ ok('getting-started: links the autonomous run section', gs.includes('(/docs/workflow#autonomous-run-auto)'));
70
+ ok('getting-started: no em-dash', !gs.includes(EM));
71
+
72
+ console.log(`auto-intro-docs.test.js: ${passed} checks passed`);
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env node
2
+ // Docs test for #286: under `git: main` the autonomous run merges a verified slot branch
3
+ // into the local trunk and unblocks its dependents. Plain asserts, no framework.
4
+ // Run: node bin/auto-merge-docs.test.js
5
+ const assert = require('assert');
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ const plugin = path.resolve(__dirname, '..', 'plugins', 'trailhead');
10
+ const auto = fs.readFileSync(path.join(plugin, 'skills/trailhead-work/references/auto.md'), 'utf8');
11
+ let passed = 0;
12
+ const ok = (name, cond) => { assert.ok(cond, name); passed++; };
13
+
14
+ ok('auto.md: integration under git: main replaces the held rule', auto.includes('**Integration under `git: main`.**') && !auto.includes('**Integration is held.**'));
15
+ ok('auto.md: the stale-base preflight runs before the merge', /Resolve stale-base preflight[\s\S]{0,200}main checkout `--sync`, then the ticket branch `--sync`[\s\S]{0,80}re-review/.test(auto));
16
+ ok('auto.md: names the merge helper call', auto.includes('trailhead-auto-merge.js merge --repo <main-checkout> --ticket <n>'));
17
+ ok('auto.md: merges are one at a time, never a push', /one merge at a time/.test(auto) && /never a push/.test(auto));
18
+ ok('auto.md: merged / already-merged resolves, unblocks, removes, frees the slot', /`merged` or `already-merged`[^\n]*resolution comment[^\n]*close the ticket[^\n]*Decisions so far/.test(auto) && /trailhead-auto-merge\.js remove --repo <main-checkout> --ticket <n>/.test(auto) && /free the slot/.test(auto));
19
+ ok('auto.md: unblocks dependents with the helper', auto.includes('trailhead-unblock.js --issue <n> --repo <owner/name>'));
20
+ ok('auto.md: dependents open their slot at the new trunk SHA', /open their slot at the new trunk SHA/.test(auto));
21
+ ok('auto.md: a conflict stays separate, is parked and recorded, dependents stay blocked', /`conflict`[^\n]*stays \*\*separate\*\*/.test(auto) && /conflicting files/.test(auto) && /unmerged list/.test(auto) && /dependents stay blocked/.test(auto));
22
+ ok('auto.md: the other statuses park needs-you naming the status', /`dirty`, `not-on-trunk`, `slot-dirty`, `in-progress` and `merge-failed`: park the ticket needs-you/.test(auto) && !/`not-merged`: park/.test(auto));
23
+ ok('auto.md: a foreign marker never blocks the merge', !/marker-mismatch/.test(auto) && /never blocks it[^)]*--no-verify[^)]*only when the marker names another ticket/.test(auto));
24
+ ok('auto.md: error hands back at once with no further merge', /`error`: hand back at once, with no further merge/.test(auto));
25
+ ok('auto.md: other slots are never rebased mid-step', /Other in-flight slots are never rebased mid-step/.test(auto) && /strictly ahead of `origin`/.test(auto));
26
+ ok('auto.md: a remove failure is a cleanup error, not a park', /`remove` failure after the close is a cleanup error, not a park/.test(auto) && /other than `removed` \(`error`, `not-merged`, `kept`\)[^.]*never park or reopen it/.test(auto) && /free the slot, list the workspace and the branch in the run-end summary/.test(auto));
27
+ ok('auto.md: git: pr and clone slots stay held', /Integration stays held under `git: pr`, and for a clone slot/.test(auto) && /gitlink is a rail action|publishing a gitlink is a rail action/.test(auto));
28
+ ok('auto.md: a local merge is not rail-tripping, the push stays the human\'s', /local merge of a verified slot branch into the trunk is not rail-tripping/.test(auto) && /the push stays the human's/.test(auto));
29
+ ok('auto.md: resume runs recover first, aborted retried, foreign handed back', /trailhead-auto-merge\.js recover --repo <main-checkout>/.test(auto) && /`aborted`[^.]*merge is retried/.test(auto) && /`foreign`[^.]*hands the run back without touching it/.test(auto));
30
+ ok('auto.md: a resolve-step slot ticket is re-driven idempotently', /work state step is `resolve`\*\* is re-driven through Resolve idempotently/.test(auto) && /already-merged/.test(auto) && /resolution comment is posted only if none exists/.test(auto));
31
+ ok('auto.md: run-end summary names merged, unmerged with files, trunk ahead of origin', /\*\*what merged\*\*/.test(auto) && /left unmerged/.test(auto) && /ahead of `origin` by N commits/.test(auto));
32
+ ok('auto.md: no em-dash', !auto.includes(String.fromCharCode(0x2014)));
33
+ console.log(`auto-merge-docs.test.js: ${passed} checks passed`);
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+ // Docs test for #287: an autonomous run opens, or updates, one merge ticket per map (or
3
+ // the whiteboard) for the slot branches it leaves unmerged, right when each arises.
4
+ // Plain asserts, no framework. Run: node bin/auto-merge-ticket-docs.test.js
5
+ const assert = require('assert');
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ const plugin = path.resolve(__dirname, '..', 'plugins', 'trailhead');
10
+ const auto = fs.readFileSync(path.join(plugin, 'skills/trailhead-work/references/auto.md'), 'utf8');
11
+ const commands = fs.readFileSync(path.join(plugin, 'skills/_shared/substrate-commands.md'), 'utf8');
12
+ let passed = 0;
13
+ const ok = (name, cond) => { assert.ok(cond, name); passed++; };
14
+
15
+ ok('auto.md: names the add call with scope, ticket, file, workspace and repo', auto.includes('trailhead-merge-ticket.js add --scope <map-<n>|whiteboard> --ticket <n> --file <path> [--file <path> ...] --workspace <path> --repo <owner/name>'));
16
+ ok('auto.md: one --file per conflicting path of the merge result', /one `--file` per path in the merge result's `conflicts`/.test(auto));
17
+ ok('auto.md: the add runs right after the abort, before parking', /right after the abort, before parking/i.test(auto));
18
+ ok('auto.md: the park reason names the branch, workspace, files and the merge ticket', /park reason names the branch, the workspace, the conflicting files and the merge ticket/.test(auto));
19
+ ok('auto.md: one merge ticket per map or the whiteboard, opened at the first unmerged branch, updated after each later one', /one merge ticket per map \(or the whiteboard\)[^\n]*opened at the first unmerged branch[^\n]*updated after each later one/.test(auto));
20
+ ok('auto.md: never only at run end', /never only at run end/.test(auto));
21
+ ok('auto.md: a needs-you task so no run takes it, closed by the human', /needs-you `task`[^\n]*no run takes it[^\n]*closed by the human/.test(auto));
22
+ ok('auto.md: a map or the whiteboard with no unmerged branch gets no merge ticket', /no unmerged branch gets no merge ticket/.test(auto));
23
+ ok('auto.md: records as it goes names the merge ticket', /Records as it goes\n[^\n]*merge ticket/.test(auto));
24
+ ok('auto.md: run-end summary names the merge tickets', /\*\*what merged\*\*[^\n]*merge tickets/.test(auto));
25
+ ok('auto.md: a helper error is reported, named in the park reason, retried once at run end', /helper error[^\n]*reported in chat[^\n]*named in the park reason[^\n]*retried once at run end/.test(auto));
26
+ ok('auto.md: a still-failing retry is listed as no merge ticket written', /no merge ticket written/.test(auto));
27
+ ok('substrate-commands.md: names the helper and its call', commands.includes('trailhead-merge-ticket.js add --scope') && commands.includes('--workspace <path> --repo <owner/name>'));
28
+ ok('substrate-commands.md: finds through GraphQL OPEN with the marker', /trailhead:merge-ticket/.test(commands) && /states OPEN|OPEN issues/.test(commands));
29
+ ok('substrate-commands.md: an error is unknown, never none found', /merge ticket[^\n]*unknown, never as none found/.test(commands));
30
+ ok('no em-dash in auto.md', !auto.includes(String.fromCharCode(0x2014)));
31
+ ok('no em-dash in substrate-commands.md', !commands.includes(String.fromCharCode(0x2014)));
32
+ console.log(`auto-merge-ticket-docs.test.js: ${passed} checks passed`);
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env node
2
+ // Docs test for #284: the autonomous run asks the external review rounds at start-up and
3
+ // parks a ticket whose plan review does not converge. Plain asserts, no framework.
4
+ // Run: node bin/auto-review-rounds-docs.test.js
5
+ const assert = require('assert');
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ const plugin = path.resolve(__dirname, '..', 'plugins', 'trailhead');
10
+ const read = (rel) => fs.readFileSync(path.join(plugin, rel), 'utf8');
11
+ let passed = 0;
12
+ const ok = (name, cond) => { assert.ok(cond, name); passed++; };
13
+
14
+ const auto = read('skills/trailhead-work/references/auto.md');
15
+ const review = read('skills/_shared/techniques/plan-review.md');
16
+ const config = read('skills/_shared/configuration-reference.md');
17
+
18
+ ok('auto.md has the External review rounds bullet', auto.includes('**External review rounds.**'));
19
+ ok('auto.md names the ask call', auto.includes('trailhead-plan-review-rounds.js ask'));
20
+ ok('auto.md: skip when plan review is off or no independent reviewer CLI is installed', /`skip`[^.]*plan review off[^.]*no independent reviewer CLI/.test(auto));
21
+ ok('auto.md: one AskUserQuestion with the helper options, at most four, config Recommended first', /one `AskUserQuestion`[^.]*`options`[^.]*at most four[^.]*Recommended/.test(auto));
22
+ ok('auto.md names the five choices', /as in config, 1, 2, 3, or until convergence/.test(auto));
23
+ ok('auto.md: until convergence stops on no progress, hard ceiling 5', /no progress[^.]*hard ceiling 5/.test(auto));
24
+ ok('auto.md: numbered list of all five without a structured question tool', /full five as a numbered list/.test(auto));
25
+ ok('auto.md: an ask error hands back before any claim', /`error`[^.]*reports it and hands back before any claim/.test(auto));
26
+ ok('auto.md: the ask is the human\'s, no delegate, also under <n>', /no delegate[^.]*before any claim/.test(auto) && /also under `\/trailhead:auto <n>`/.test(auto));
27
+ ok('auto.md: the choice holds for every build ticket, each slot included', /holds for every `build` ticket of the run[^.]*slot/.test(auto));
28
+ ok('auto.md: the wired-separately aside is gone', !/wired separately/.test(auto));
29
+ ok('auto.md: names the parking trigger statuses', /`exhausted`[^.]*`no-progress`[^.]*`ceiling`/.test(auto));
30
+ ok('auto.md: parks after PLAN and PLAN-REVIEW are posted, quoting each concern\'s full text', /after the `PLAN` and `PLAN-REVIEW` comments are posted/.test(auto) && /full `text`[^.]*`surviving`[^.]*never just (its|the) key/.test(auto));
31
+ ok('auto.md: names the round call', auto.includes('trailhead-plan-review-rounds.js round'));
32
+
33
+ ok('auto.md: the ask helper warnings are printed in chat before the ask or skip', /non-empty `warnings`[\s\S]{0,200}printed in chat first, before the ask or the skip/.test(auto));
34
+ ok('plan-review.md: rounds come from config or the run start-up choice', /`config\.plan_review\.rounds`[^.]*autonomous run[^.]*start-up choice/.test(review));
35
+ ok('plan-review.md: calls the round helper and follows its decision', review.includes('trailhead-plan-review-rounds.js round') && /follows its decision/.test(review));
36
+ ok('plan-review.md: under an autonomous run the checkpoint is not posed, the ticket is parked', /autonomous run[^.]*checkpoint is not posed[\s\S]{0,400}trailhead-needs-you\.js park/.test(review));
37
+ ok('plan-review.md: the park reason lists the surviving blocking concerns', /reason listing the surviving blocking concerns/.test(review));
38
+ ok('plan-review.md: interactive behaviour unchanged', /interactive[^.]*unchanged/i.test(review));
39
+
40
+ ok('configuration-reference.md: plan_review.rounds notes the run ask override', /plan_review\.rounds[^\n]*autonomous run asks at start[^\n]*overrides the key for that run/.test(config));
41
+
42
+ for (const [name, text] of [['auto.md', auto], ['plan-review.md', review], ['configuration-reference.md', config]]) {
43
+ ok(`${name}: no em-dash`, !text.includes(String.fromCharCode(0x2014)));
44
+ }
45
+ console.log(`auto-review-rounds-docs.test.js: ${passed} checks passed`);