@marcomigozzi/trailhead 0.15.0 → 0.16.1

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 (116) hide show
  1. package/README.it.md +52 -14
  2. package/README.md +53 -15
  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/config-dir-templates-docs.test.js +52 -0
  12. package/bin/decision-builds-docs.test.js +75 -0
  13. package/bin/fog-vs-ticket-docs.test.js +56 -0
  14. package/bin/grilling-holistic-review-docs.test.js +90 -0
  15. package/bin/lib/codex-projection.js +13 -6
  16. package/bin/lib/codex-projection.test.js +25 -2
  17. package/bin/lib/host-descriptor.js +14 -1
  18. package/bin/lib/host-descriptor.test.js +18 -0
  19. package/bin/lib/runtime-command-literals.test.js +184 -0
  20. package/bin/needs-you-docs.test.js +41 -0
  21. package/bin/pick-stress-test-docs.test.js +105 -0
  22. package/bin/report-status-docs.test.js +1 -1
  23. package/bin/trailhead.js +3 -3
  24. package/bin/trailhead.test.js +17 -18
  25. package/package.json +1 -1
  26. package/plugins/trailhead/.claude-plugin/plugin.json +1 -1
  27. package/plugins/trailhead/agents/trailhead-code-review.md +2 -2
  28. package/plugins/trailhead/agents/trailhead-verify.md +2 -2
  29. package/plugins/trailhead/commands/auto.md +2 -2
  30. package/plugins/trailhead/hooks/lib/auto-merge.test.js +467 -0
  31. package/plugins/trailhead/hooks/lib/auto-queue.test.js +245 -0
  32. package/plugins/trailhead/hooks/lib/auto-run.test.js +318 -0
  33. package/plugins/trailhead/hooks/lib/auto-slot.test.js +278 -0
  34. package/plugins/trailhead/hooks/lib/checkout-in.test.js +173 -0
  35. package/plugins/trailhead/hooks/lib/dashboard.test.js +66 -3
  36. package/plugins/trailhead/hooks/lib/merge-ticket.test.js +394 -0
  37. package/plugins/trailhead/hooks/lib/model-defaults-review.js +50 -21
  38. package/plugins/trailhead/hooks/lib/model-defaults-review.test.js +50 -0
  39. package/plugins/trailhead/hooks/lib/needs-you.test.js +177 -0
  40. package/plugins/trailhead/hooks/lib/plan-review-rounds.test.js +319 -0
  41. package/plugins/trailhead/hooks/lib/report-status.test.js +68 -1
  42. package/plugins/trailhead/hooks/lib/shell-scan.js +25 -0
  43. package/plugins/trailhead/hooks/lib/statusline-context.test.js +161 -0
  44. package/plugins/trailhead/hooks/lib/uat-followup.test.js +169 -0
  45. package/plugins/trailhead/hooks/lib/unblock.test.js +207 -0
  46. package/plugins/trailhead/hooks/lib/unmodelled.js +6 -1
  47. package/plugins/trailhead/hooks/mod/README.md +185 -39
  48. package/plugins/trailhead/hooks/mod/auto-run-view.ts +93 -0
  49. package/plugins/trailhead/hooks/mod/auto-run.ts +165 -0
  50. package/plugins/trailhead/hooks/mod/map-pane-view.ts +150 -25
  51. package/plugins/trailhead/hooks/mod/map-pane.tsx +557 -99
  52. package/plugins/trailhead/hooks/mod/register.tsx +298 -39
  53. package/plugins/trailhead/hooks/mod/repo-pin.ts +167 -0
  54. package/plugins/trailhead/hooks/mod/session-registry.ts +65 -0
  55. package/plugins/trailhead/hooks/mod/turn-state.ts +14 -0
  56. package/plugins/trailhead/hooks/mod/whiteboard-follow.ts +98 -0
  57. package/plugins/trailhead/hooks/mod/work-state-locate.ts +191 -0
  58. package/plugins/trailhead/hooks/mod/work-state-prompt.ts +13 -15
  59. package/plugins/trailhead/hooks/mod/work-state-view.ts +21 -5
  60. package/plugins/trailhead/hooks/mod/worktree-porcelain.ts +17 -0
  61. package/plugins/trailhead/hooks/trailhead-bash-guards.js +8 -8
  62. package/plugins/trailhead/hooks/trailhead-bash-guards.test.js +22 -16
  63. package/plugins/trailhead/hooks/trailhead-labels-guard.js +231 -0
  64. package/plugins/trailhead/hooks/trailhead-labels-guard.test.js +212 -0
  65. package/plugins/trailhead/hooks/trailhead-secret-read-guard.js +336 -23
  66. package/plugins/trailhead/hooks/trailhead-secret-read-guard.test.js +321 -0
  67. package/plugins/trailhead/skills/_shared/choices.md +7 -2
  68. package/plugins/trailhead/skills/_shared/configuration-reference.md +5 -5
  69. package/plugins/trailhead/skills/_shared/session-handoff.md +1 -1
  70. package/plugins/trailhead/skills/_shared/substrate-commands.md +26 -4
  71. package/plugins/trailhead/skills/_shared/substrate.md +4 -1
  72. package/plugins/trailhead/skills/_shared/teamwork.md +5 -5
  73. package/plugins/trailhead/skills/_shared/techniques/acceptance-testing.md +2 -2
  74. package/plugins/trailhead/skills/_shared/techniques/code-review.md +1 -1
  75. package/plugins/trailhead/skills/_shared/techniques/grilling.md +4 -2
  76. package/plugins/trailhead/skills/_shared/techniques/plan-review.md +3 -1
  77. package/plugins/trailhead/skills/_shared/techniques/verify.md +1 -1
  78. package/plugins/trailhead/skills/_shared/techniques.md +2 -2
  79. package/plugins/trailhead/skills/trailhead-capture/references/capture.md +1 -1
  80. package/plugins/trailhead/skills/trailhead-chart/SKILL.md +1 -1
  81. package/plugins/trailhead/skills/trailhead-chart/references/charting.md +5 -5
  82. package/plugins/trailhead/skills/trailhead-chart/references/inbox.md +2 -2
  83. package/plugins/trailhead/skills/trailhead-manage/references/auditing.md +4 -3
  84. package/plugins/trailhead/skills/trailhead-view/SKILL.md +1 -0
  85. package/plugins/trailhead/skills/trailhead-work/SKILL.md +23 -12
  86. package/plugins/trailhead/skills/trailhead-work/references/auto.md +70 -22
  87. package/plugins/trailhead/skills/trailhead-work/references/ticket-engines.md +7 -6
  88. package/plugins/trailhead/templates/statusline-trailhead.sh +80 -15
  89. package/plugins/trailhead/templates/trailhead-auto-merge.js +318 -0
  90. package/plugins/trailhead/templates/trailhead-auto-queue.js +186 -0
  91. package/plugins/trailhead/templates/trailhead-auto-run.js +294 -0
  92. package/plugins/trailhead/templates/trailhead-auto-slot.js +239 -0
  93. package/plugins/trailhead/templates/trailhead-checkout-in.js +88 -0
  94. package/plugins/trailhead/templates/trailhead-dashboard.js +32 -12
  95. package/plugins/trailhead/templates/trailhead-merge-ticket.js +366 -0
  96. package/plugins/trailhead/templates/trailhead-needs-you.js +193 -0
  97. package/plugins/trailhead/templates/trailhead-plan-review-rounds.js +289 -0
  98. package/plugins/trailhead/templates/trailhead-report-status.js +67 -3
  99. package/plugins/trailhead/templates/trailhead-uat-followup.js +128 -0
  100. package/plugins/trailhead/templates/trailhead-unblock.js +111 -0
  101. package/plugins/trailhead/tests/auto-run-ui.test.ts +444 -0
  102. package/plugins/trailhead/tests/auto-run-view.test.ts +111 -0
  103. package/plugins/trailhead/tests/map-pane-ui.test.ts +1839 -72
  104. package/plugins/trailhead/tests/map-pane-view.test.ts +308 -14
  105. package/plugins/trailhead/tests/map-pane-write.test.ts +51 -0
  106. package/plugins/trailhead/tests/repo-pin-late-repo.test.ts +77 -0
  107. package/plugins/trailhead/tests/repo-pin.test.ts +123 -0
  108. package/plugins/trailhead/tests/session-registry.test.ts +50 -0
  109. package/plugins/trailhead/tests/whiteboard-follow.test.ts +90 -0
  110. package/plugins/trailhead/tests/work-state-locate.test.ts +33 -0
  111. package/plugins/trailhead/tests/work-state-prompt-hook.test.ts +276 -2
  112. package/plugins/trailhead/tests/work-state-prompt.test.ts +29 -35
  113. package/plugins/trailhead/tests/work-state-ui.test.ts +73 -1
  114. package/plugins/trailhead/tests/work-state-view.test.ts +19 -0
  115. package/plugins/trailhead/tests/work-state-worktree.test.ts +310 -0
  116. 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
 
@@ -89,7 +90,7 @@ Ciò che **non** ha bisogno di una propria mappa è un singolo pezzo di lavoro d
89
90
 
90
91
  Ogni **ticket** è una child issue con una label di tipo e un corpo di una sola domanda. La **frontier** è l'insieme dei ticket aperti, non assegnati, i cui blocker sono tutti chiusi e che non sono in pausa: ciò che è prendibile proprio ora. Rivendichi un ticket assegnandolo a te stesso, lo risolvi con il motore per il suo tipo, poi registri la risposta come commento, lo chiudi, e ne fai il gist di nuovo sulla mappa.
91
92
 
92
- Man mano che i ticket si risolvono, la fog si dirada: domande che erano troppo vaghe da formulare diventano abbastanza nitide da diventare ticket, una alla volta, finché non resta nulla da decidere o costruire e la destination è raggiunta.
93
+ La fog contiene solo ciò che non si sa ancora formulare: una domanda già formulabile diventa un ticket al charting, creato bloccato quando aspetta un'altra risposta, così entra in frontier da solo appena quella risposta arriva. Man mano che i ticket si risolvono, la fog si dirada: dopo ogni risoluzione ogni voce della fog viene riletta, e quelle ormai abbastanza nitide da diventare ticket vengono proposte insieme, finché non resta nulla da decidere o costruire e la destination è raggiunta.
93
94
 
94
95
  Una mappa ha come scope **un** solo sforzo, quindi la conoscenza che appartiene al *repo* (non a una singola mappa) vive in **anchor issue con scope di repo**, create una volta e condivise da ogni mappa (le Notes di ciascuna mappa le collegano soltanto, così nulla resta orfano quando una mappa finisce):
95
96
 
@@ -106,12 +107,48 @@ 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, poi apre i ticket build che portano avanti la risposta · `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]` svuota una mappa (o la whiteboard, o tutto) senza supervisione: prende un ticket dopo l'altro, in 1-4 slot paralleli, e risolve ciascuno con lo stesso motore che userebbe `/trailhead:work`, senza restituirti il controllo nel mezzo. Non parte mai da sola: ogni run la avvii tu.
119
+
120
+ **`auto` o `work`?** Entrambi risolvono ticket; cambia chi resta nel loop. `/trailhead:work` è la modalità interattiva: un ticket per sessione, e si ferma a chiederti le cose lungo la strada (il brief dello scope, il grilling, l'acceptance). `auto` chiede solo all'inizio, poi fa da sé le scelte di processo e parcheggia per te ciò che solo tu puoi decidere. Usa `auto` per ticket build e bug ben specificati che non devi guidare; usa `work` per le decisioni, i ticket vaghi o quando vuoi guidare tu.
121
+
122
+ **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.
123
+
124
+ 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:
125
+
126
+ ```text
127
+ auto: slot 1 #42 add social login (verify) | slot 2 #43 session cookie (execute) | resolved #41, next #44
128
+
129
+ Run report
130
+ Stopped: frontier empty
131
+ Resolved: #41, #42, #43, #44
132
+ Merged: #41, #42, #43, #44 into main; local main is 7 commits ahead of origin (pushing is yours)
133
+ Remains: #45 needs you (which providers to support); #46 blocked by #45
134
+ ```
135
+
136
+ **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).
137
+
138
+ - **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.
139
+ - **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.
140
+ - **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.
141
+ - **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.
142
+ - **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.
143
+ - **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.
144
+ - **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).
145
+ - **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.
146
+ - **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.
147
+ - **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.
148
+ - **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.
149
+
150
+ `auto` è ortogonale a `effort` e `models.*` e non ha una superficie di config propria: le domande iniziali la sostituiscono.
151
+
115
152
  ### Un esempio pratico: "add social login"
116
153
 
117
154
  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 +218,7 @@ Ogni verbo è anche un comando con namespace (`/trailhead:new`, `/trailhead:work
181
218
  | `/trailhead:pause [note]` | mette un checkpoint sul ticket in gioco così chiunque può riprenderlo |
182
219
  | `/trailhead:ticket <type> <title>` | apre un ticket al volo (diverge brevemente prima: un atto di micro-charting) |
183
220
  | `/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 |
221
+ | `/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
222
  | `/trailhead:grill [topic]` | esegue una sessione di grilling autonoma su una decision/topic |
186
223
  | `/trailhead:map` | mostra la mappa a bassa risoluzione (destination, decisions, frontier, fog) |
187
224
  | `/trailhead:dashboard` | mostra la dashboard del repo: l'indice pinnato di ogni mappa aperta, la whiteboard, e i conteggi live |
@@ -193,7 +230,7 @@ La mod di trailhead (Claude Code 2.1.287+) è inclusa nel plugin e disegna il la
193
230
 
194
231
  ![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
232
 
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.
233
+ 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
234
 
198
235
  ![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
236
 
@@ -340,7 +377,7 @@ trailhead › ✓ Sign in with GitHub → lands on /dashboard (screenshot
340
377
  ? Step 6 is the haptic on "Signed in": does it buzz once, softly?
341
378
  ```
342
379
 
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.
380
+ 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
381
 
345
382
  ---
346
383
 
@@ -351,7 +388,7 @@ Tutto ciò di cui la mappa ha bisogno è espresso come label GitHub, così lo st
351
388
  - **Strutturali:** `trailhead:map`, `trailhead:ticket`
352
389
  - **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
390
  - **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)
391
+ - **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
392
  - **Container:** `trailhead:whiteboard` (un ticket sciolto, senza mappa, fuori dalla frontier di ogni mappa, sulla frontier propria della whiteboard)
356
393
  - **Marker:** `trailhead:uat` (un follow-up UAT in sospeso; mai un filtro della frontier) · `trailhead:map-<n>` (lega un ticket alla sua mappa)
357
394
 
@@ -392,14 +429,14 @@ I ticket che nascono da altro lavoro portano un puntatore di lignaggio nel loro
392
429
  Tre layer: **il più vicino vince**, chiave per chiave (una chiave non impostata a un layer eredita il successivo):
393
430
 
394
431
  - **Project**: un file `.trailhead/config.json` alla radice del repo: override per questo progetto, committato così un team condivide una config. La config vive in un file semplice, **mai nella issue della mappa**; è tua da cambiare in qualsiasi momento.
395
- - **Global**: `~/.claude/trailhead/config.json`: i tuoi default permanenti su ogni progetto.
432
+ - **Global**: `trailhead/config.json` nella directory di config dell'host: `$CLAUDE_CONFIG_DIR` (default `~/.claude`) su Claude Code, `$CODEX_HOME` (default `~/.codex`) su Codex. I tuoi default permanenti su ogni progetto.
396
433
  - **Defaults**: i valori integrati.
397
434
 
398
435
  `/trailhead:config` esegue un **setup guidato, a menu**: scegli lo scope, poi percorri ogni impostazione (⚙️ modo di lavorare (git · release · isolation) · 🌐 ticket language · 🧠 models (un profilo High / Balanced / Low, o Manual) · 🎨 design + approval · 🧪 TDD · ⚖️ effort · 🖥️ acceptance testing (browser, setup UAT, un account di test) · 🧑‍⚖️ plan review · 📊 statusline) come un menu con icone; niente editing manuale del JSON. Ogni passo è chiesto (nessuno saltato), e i **modelli plan ed execute sono sempre due scelte separate e version-pinned**. `config get` stampa la config effettiva unita; `config set <key> <value>` scrive una chiave.
399
436
 
400
437
  `/trailhead:prune [map]` recupera gli slot di sub-issue di una mappa sotto il cap di 100 di GitHub (elimina gli edge chiusi/orfani, mantiene ogni riferimento). Offerto automaticamente quando una mappa è vicina o al cap, ed eseguibile anche su richiesta.
401
438
 
402
- `/trailhead:audit [mappa] [--all]` cerca le regressioni: verifica che i ticket chiusi dall'ultimo audit siano ancora veri rispetto al codice attuale. Il codice che i commit di un ticket non hanno più toccato è classificato invariato, senza agente (un controllo git veloce e deterministico che si basa sui trailer `Refs: #<n>` dei commit), decisioni, ricerche, task e prototipi ricevono un controllo leggero inline, e un agente di verifica completo parte solo dove il codice è cambiato o il controllo veloce non può decidere (per esempio senza commit `Refs:`). Ogni ticket è classificato come implementato, invariato, evoluto (un ticket successivo l'ha cambiato di proposito, elencato senza proposte), implementato male o non implementato. Poi confronta quelli aperti per trovare duplicati e cluster raggruppabili. Resta in sola lettura finché non confermi: ogni correzione (un bug di regressione, una riapertura, la chiusura di un duplicato, un supersede-and-merge) è una proposta applicata solo con un sì esplicito. `--all` riverifica tutti i ticket chiusi.
439
+ `/trailhead:audit [mappa] [--all]` cerca le regressioni: verifica che i ticket chiusi dall'ultimo audit siano ancora veri rispetto al codice attuale. Il codice che i commit di un ticket non hanno più toccato è classificato invariato, senza agente (un controllo git veloce e deterministico che si basa sui trailer `Refs: #<n>` dei commit), decisioni, ricerche, task e prototipi ricevono un controllo leggero inline, e un agente di verifica completo parte solo dove il codice è cambiato o il controllo veloce non può decidere (per esempio senza commit `Refs:`). Ogni ticket è classificato come implementato, invariato, evoluto (un ticket successivo l'ha cambiato di proposito, elencato senza proposte), implementato male o non implementato. Segnala anche le decisioni chiuse senza build, cioè senza alcun ticket che porti avanti la loro risposta. Poi confronta quelli aperti per trovare duplicati e cluster raggruppabili. Resta in sola lettura finché non confermi: ogni correzione (un bug di regressione, una riapertura, i ticket build mancanti di una decisione, la chiusura di un duplicato, un supersede-and-merge) è una proposta applicata solo con un sì esplicito. `--all` riverifica tutti i ticket chiusi.
403
440
 
404
441
  Il passo **📊 statusline** offre di installare la status bar di trailhead per Claude Code: una riga con **model · project · branch · plan usage (`5h %` · reset · `7d %`) · una barra della context-window**, più una **seconda riga con il ticket attivo** (`▸ #N Title`) ogni volta che ne stai lavorando uno, e un flag `⬆ trailhead <version>` quando è disponibile un trailhead più recente (esegui `/trailhead:update`). Il project è sempre il nome del repo principale anche da un checkout isolato, e il branch porta un tag `(WT)` in un worktree o `(C)` in un clone per-ticket (niente sul checkout originale). È un'impostazione globale di Claude Code; se già usi una statusline (es. `ccstatusline`) il setup chiede prima di rimpiazzarla, e lo script espone anche i segmenti `--ticket-only` / `--context-only` / `--usage-only` da inserire in un tool esistente.
405
442
 
@@ -436,7 +473,7 @@ Scegli `plan` ed `execute` separatamente, ciascuno per **id completo versionato*
436
473
 
437
474
  La **sessione principale resta l'orchestratore**. Tiene i momenti interattivi (il Discuss di un build, e l'acceptance/UAT del Verify) e fa charting, grilling e scrittura dei ticket sul proprio modello. Nessuna chiave governa la qualità dei ticket, quindi esegui le sessioni di charting sul tuo modello forte.
438
475
 
439
- Esempio di file di config (la stessa forma funziona per il `.trailhead/config.json` di progetto e il `~/.claude/trailhead/config.json` globale):
476
+ Esempio di file di config (la stessa forma funziona per il `.trailhead/config.json` di progetto e il `trailhead/config.json` globale nella tua directory di config):
440
477
 
441
478
  ```json
442
479
  { "ticket": { "language": "en" }, "models": { "plan": "claude-opus-5-5", "execute": "claude-sonnet-5-5" }, "tdd": "seams", "acceptance": { "browser": "auto" } }
@@ -450,24 +487,25 @@ Esempio di file di config (la stessa forma funziona per il `.trailhead/config.js
450
487
 
451
488
  ## 🪝 Hooks
452
489
 
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:
490
+ 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
491
 
455
492
  - **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
493
  - **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
494
  - **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
495
  - **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
496
  - **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.
460
- - **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.
497
+ - **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.
498
+ - **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 `<`), compreso il file di script o di pattern passato con `-f`/`--file` a `grep`, `sed` e `awk`. Viene bloccato anche un wrapper di comando (`env`, `nice`, `timeout`, `nohup`, `sudo`, `stdbuf`, `time`, `setsid`, `doas`) davanti a una shell annidata (`bash -c`) o a `eval` vicino a un path che sembra un segreto, perché il wrapper nasconderebbe il comando interno. È 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
499
  - **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
500
  - **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
501
 
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.
502
+ 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
503
 
466
504
  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
505
 
468
506
  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
507
 
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.
508
+ 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
509
 
472
510
  ---
473
511
 
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
 
@@ -89,7 +90,7 @@ What does **not** need its own map is a single, defined piece of work with no op
89
90
 
90
91
  Each **ticket** is a child issue with a type label and a one-question body. The **frontier** is the set of open, unassigned tickets whose blockers are all closed and that are not paused: what's takeable right now. You claim a ticket by assigning it to yourself, resolve it with the engine for its type, then record the answer as a comment, close it, and gist it back onto the map.
91
92
 
92
- As tickets resolve, the fog clears: questions that were too vague to phrase become sharp enough to ticket, one at a time, until nothing is left to decide or build and the destination is reached.
93
+ The fog holds only what you cannot phrase yet: a question you can already phrase becomes a ticket at charting, created blocked when it waits on another answer, so it reaches the frontier by itself once that answer lands. As tickets resolve, the fog clears: after each resolution every fog entry is re-read, and the ones now sharp enough to ticket are proposed together, until nothing is left to decide or build and the destination is reached.
93
94
 
94
95
  A map is scoped to **one** effort, so knowledge that belongs to the *repo* (not to any single map) lives in **repo-scoped anchor issues**, created once and shared by every map (each map's Notes just links them, so nothing is stranded when a map finishes):
95
96
 
@@ -106,12 +107,48 @@ 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, then opens the build tickets that carry the answer · `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]` drains a map (or the whiteboard, or everything) unattended: it takes ticket after ticket, in 1 to 4 parallel slots, and resolves each with the same engine `/trailhead:work` would run, without handing back to you in between. It never starts on its own: you launch every run.
119
+
120
+ **`auto` or `work`?** Both resolve tickets; what changes is who stays in the loop. `/trailhead:work` is the interactive mode: one ticket per session, and it stops to ask you along the way (the scope brief, grilling, acceptance). `auto` asks only at the start, then makes the process choices itself and parks for you whatever only you can settle. Use `auto` for well-specified build and bug tickets you don't need to steer; use `work` for decisions, fuzzy tickets, or when you want to drive.
121
+
122
+ **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.
123
+
124
+ 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:
125
+
126
+ ```text
127
+ auto: slot 1 #42 add social login (verify) | slot 2 #43 session cookie (execute) | resolved #41, next #44
128
+
129
+ Run report
130
+ Stopped: frontier empty
131
+ Resolved: #41, #42, #43, #44
132
+ Merged: #41, #42, #43, #44 into main; local main is 7 commits ahead of origin (pushing is yours)
133
+ Remains: #45 needs you (which providers to support); #46 blocked by #45
134
+ ```
135
+
136
+ **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).
137
+
138
+ - **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.
139
+ - **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.
140
+ - **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.
141
+ - **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.
142
+ - **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.
143
+ - **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.
144
+ - **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).
145
+ - **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.
146
+ - **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.
147
+ - **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.
148
+ - **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.
149
+
150
+ `auto` is orthogonal to `effort` and `models.*` and has no config surface of its own: the asks replace it.
151
+
115
152
  ### A worked example: "add social login"
116
153
 
117
154
  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 +218,7 @@ Every verb is also a namespaced command (`/trailhead:new`, `/trailhead:work`, `/
181
218
  | `/trailhead:pause [note]` | checkpoint the ticket in play so anyone can resume it |
182
219
  | `/trailhead:ticket <type> <title>` | open a ticket on the fly (diverges briefly first: a micro-charting act) |
183
220
  | `/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 |
221
+ | `/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
222
  | `/trailhead:grill [topic]` | run a standalone grilling session on a decision/topic |
186
223
  | `/trailhead:map` | show the low-res map (destination, decisions, frontier, fog) |
187
224
  | `/trailhead:dashboard` | show the repo dashboard: the pinned index of every open map, the whiteboard, and live counts |
@@ -193,7 +230,7 @@ The trailhead mod (Claude Code 2.1.287+) ships inside the plugin and draws the w
193
230
 
194
231
  ![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
232
 
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.
233
+ 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
234
 
198
235
  ![The panel viewing another map, with a viewing and active header and a back to active button](assets/screenshots/map-panel-viewing.png)
199
236
 
@@ -340,7 +377,7 @@ trailhead › ✓ Sign in with GitHub → lands on /dashboard (screenshot
340
377
  ? Step 6 is the haptic on "Signed in": does it buzz once, softly?
341
378
  ```
342
379
 
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.
380
+ 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
381
 
345
382
  ---
346
383
 
@@ -351,7 +388,7 @@ Everything the map needs is expressed as GitHub labels, so state is queryable in
351
388
  - **Structural:** `trailhead:map`, `trailhead:ticket`
352
389
  - **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
390
  - **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)
391
+ - **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
392
  - **Container:** `trailhead:whiteboard` (a loose, map-less ticket, off every map's frontier, on the whiteboard's own)
356
393
  - **Marker:** `trailhead:uat` (a pending UAT follow-up; never a frontier filter) · `trailhead:map-<n>` (scopes a ticket to its map)
357
394
 
@@ -392,14 +429,14 @@ Tickets that spin off from other work carry a lineage pointer in their body, so
392
429
  Three layers: **nearest wins**, key by key (a key unset at one layer inherits the next):
393
430
 
394
431
  - **Project**: a `.trailhead/config.json` file at the repo root: overrides for this project, committed so a team shares one config. Config lives in a plain file, **never in the map issue**; it's yours to change at any time.
395
- - **Global**: `~/.claude/trailhead/config.json`: your standing defaults across every project.
432
+ - **Global**: `trailhead/config.json` in your host's config directory: `$CLAUDE_CONFIG_DIR` (default `~/.claude`) on Claude Code, `$CODEX_HOME` (default `~/.codex`) on Codex. Your standing defaults across every project.
396
433
  - **Defaults**: the built-in values.
397
434
 
398
435
  `/trailhead:config` runs a **guided, menu-driven setup**: pick the scope, then walk each setting (⚙️ way of working (git · release · isolation) · 🌐 ticket language · 🧠 models (a High / Balanced / Low profile, or Manual) · 🎨 design + approval · 🧪 TDD · ⚖️ effort · 🖥️ acceptance testing (browser, UAT setup, a test account) · 🧑‍⚖️ plan review · 📊 statusline) as an icon-labelled menu; no hand-editing JSON. Every step is asked (none skipped), and **plan and execute models are always two separate, version-pinned choices**. `config get` prints the effective merged config; `config set <key> <value>` writes one key.
399
436
 
400
437
  `/trailhead:prune [map]` reclaims a map's sub-issue slots under GitHub's 100-cap (drops closed/orphan edges, keeps every reference). Offered automatically when a map is near or at the cap, and runnable on demand.
401
438
 
402
- `/trailhead:audit [map] [--all]` hunts regressions: it checks that the tickets closed since the last audit are still true against the current code. Code a ticket's commits left untouched is classed unchanged with no agent (a fast, deterministic git check that relies on the `Refs: #<n>` commit trailers), decisions, research, tasks and prototypes get a light inline check, and a full verification agent runs only where the code changed or the fast path cannot decide (no `Refs:` commits, for example). Each ticket is classed implemented, unchanged, evolved (a later ticket changed it on purpose, listed with no proposal), implemented wrong, or not implemented. It then compares the open ones for duplicates and groupable clusters. It is read-only until you confirm: every fix (a regression bug, a reopen, a duplicate close, a supersede-and-merge) is a proposal applied only on an explicit yes. `--all` re-audits every closed ticket.
439
+ `/trailhead:audit [map] [--all]` hunts regressions: it checks that the tickets closed since the last audit are still true against the current code. Code a ticket's commits left untouched is classed unchanged with no agent (a fast, deterministic git check that relies on the `Refs: #<n>` commit trailers), decisions, research, tasks and prototypes get a light inline check, and a full verification agent runs only where the code changed or the fast path cannot decide (no `Refs:` commits, for example). Each ticket is classed implemented, unchanged, evolved (a later ticket changed it on purpose, listed with no proposal), implemented wrong, or not implemented. It also flags decisions closed with no build ticket carrying their answer. It then compares the open ones for duplicates and groupable clusters. It is read-only until you confirm: every fix (a regression bug, a reopen, the missing build tickets of a decision, a duplicate close, a supersede-and-merge) is a proposal applied only on an explicit yes. `--all` re-audits every closed ticket.
403
440
 
404
441
  The **📊 statusline** step offers to install trailhead's Claude Code status bar: one line with **model · project · branch · plan usage (`5h %` · reset · `7d %`) · a context-window bar**, plus a **second line with the active ticket** (`▸ #N Title`) whenever you're working one, and a `⬆ trailhead <version>` flag when a newer trailhead is available (run `/trailhead:update`). The project is always the main repo's name even from an isolated checkout, and the branch carries a `(WT)` tag in a worktree or `(C)` in a per-ticket clone (nothing on the original checkout). It's a global Claude Code setting; if you already run a statusline (e.g. `ccstatusline`) the setup asks before replacing it, and the script also exposes `--ticket-only` / `--context-only` / `--usage-only` segments to slot into an existing tool.
405
442
 
@@ -436,7 +473,7 @@ Choose `plan` and `execute` separately, each by **full versioned id** (never a b
436
473
 
437
474
  The **main session stays the orchestrator**. It holds the interactive moments (a build's Discuss, and Verify's acceptance/UAT) and it does charting, grilling, and ticket-writing on its own model. No key governs ticket quality, so run charting sessions on your strong model.
438
475
 
439
- Example config file (the same shape works for the project `.trailhead/config.json` and the global `~/.claude/trailhead/config.json`):
476
+ Example config file (the same shape works for the project `.trailhead/config.json` and the global `trailhead/config.json` in your config directory):
440
477
 
441
478
  ```json
442
479
  { "ticket": { "language": "en" }, "models": { "plan": "claude-opus-5-5", "execute": "claude-sonnet-5-5" }, "tdd": "seams", "acceptance": { "browser": "auto" } }
@@ -450,24 +487,25 @@ Example config file (the same shape works for the project `.trailhead/config.jso
450
487
 
451
488
  ## 🪝 Hooks
452
489
 
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:
490
+ 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
491
 
455
492
  - **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
493
  - **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
494
  - **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
495
  - **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
496
  - **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.
460
- - **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.
497
+ - **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.
498
+ - **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), including the `-f`/`--file` script or pattern file of `grep`, `sed`, and `awk`. A command wrapper (`env`, `nice`, `timeout`, `nohup`, `sudo`, `stdbuf`, `time`, `setsid`, `doas`) in front of a nested shell (`bash -c`) or `eval` next to a secret-looking path is blocked too, since the wrapper would hide the inner command. 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
499
  - **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
500
  - **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
501
 
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.
502
+ 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
503
 
466
504
  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
505
 
468
506
  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
507
 
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.
508
+ 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
509
 
472
510
  ---
473
511
 
@@ -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`);