@marcomigozzi/trailhead 0.16.1 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/README.it.md +82 -25
  2. package/README.md +82 -25
  3. package/bin/base-preflight-docs.test.js +4 -1
  4. package/bin/bulk-cross-reference-docs.test.js +36 -0
  5. package/bin/capture-action.test.js +167 -0
  6. package/bin/capture-docs.test.js +32 -0
  7. package/bin/engine-route-docs.test.js +155 -0
  8. package/bin/facet-stamp-docs.test.js +79 -0
  9. package/bin/forced-mutation-docs.test.js +38 -0
  10. package/bin/grill-model-docs.test.js +155 -0
  11. package/bin/grilling-holistic-review-docs.test.js +7 -3
  12. package/bin/guard-axis-docs.test.js +69 -0
  13. package/bin/inbox-needs-info-docs.test.js +68 -0
  14. package/bin/lib/codex-projection.js +28 -19
  15. package/bin/lib/codex-projection.test.js +77 -20
  16. package/bin/lib/host-descriptor.js +1 -1
  17. package/bin/lib/host-descriptor.test.js +6 -0
  18. package/bin/needs-you-docs.test.js +9 -2
  19. package/bin/options-in-chat-docs.test.js +65 -0
  20. package/bin/pick-stress-test-docs.test.js +1 -1
  21. package/bin/pipeline-review-docs.test.js +118 -0
  22. package/bin/plan-review-budget-docs.test.js +63 -0
  23. package/bin/repo-target-docs.test.js +41 -0
  24. package/bin/report-status-docs.test.js +10 -2
  25. package/bin/seam-coverage-docs.test.js +46 -0
  26. package/bin/session-trust-docs.test.js +37 -0
  27. package/bin/standards-brief-docs.test.js +112 -0
  28. package/bin/surfaced-scope-docs.test.js +57 -0
  29. package/bin/test-once-docs.test.js +101 -0
  30. package/bin/test-run-docs.test.js +47 -0
  31. package/bin/trailhead.js +15 -8
  32. package/bin/trailhead.test.js +71 -17
  33. package/bin/unblock-docs.test.js +56 -0
  34. package/bin/yes-accepts-recommended-docs.test.js +54 -0
  35. package/package.json +1 -1
  36. package/plugins/trailhead/.claude-plugin/plugin.json +1 -1
  37. package/plugins/trailhead/agents/trailhead-code-review.md +2 -1
  38. package/plugins/trailhead/agents/trailhead-codebase-map.md +1 -1
  39. package/plugins/trailhead/agents/trailhead-grill.md +41 -0
  40. package/plugins/trailhead/agents/trailhead-plan.md +1 -1
  41. package/plugins/trailhead/agents/trailhead-verify.md +1 -0
  42. package/plugins/trailhead/hooks/lib/audit-walk.test.js +62 -0
  43. package/plugins/trailhead/hooks/lib/capture.test.js +439 -0
  44. package/plugins/trailhead/hooks/lib/checkout-find.test.js +195 -0
  45. package/plugins/trailhead/hooks/lib/checkout-in.test.js +83 -7
  46. package/plugins/trailhead/hooks/lib/dashboard.test.js +19 -0
  47. package/plugins/trailhead/hooks/lib/gh-auth-preflight.js +89 -0
  48. package/plugins/trailhead/hooks/lib/gh-auth-preflight.test.js +132 -0
  49. package/plugins/trailhead/hooks/lib/git-env-mod.test.js +55 -0
  50. package/plugins/trailhead/hooks/lib/git-env-spawn.test.js +73 -0
  51. package/plugins/trailhead/hooks/lib/git-env.test.js +51 -0
  52. package/plugins/trailhead/hooks/lib/global-config-dir.test.js +48 -0
  53. package/plugins/trailhead/hooks/lib/heredoc-mod.test.js +85 -0
  54. package/plugins/trailhead/hooks/lib/inbox-needs-info.test.js +179 -0
  55. package/plugins/trailhead/hooks/lib/inherit-session.js +17 -0
  56. package/plugins/trailhead/hooks/lib/issue-body.test.js +165 -0
  57. package/plugins/trailhead/hooks/lib/model-defaults-review.js +22 -1
  58. package/plugins/trailhead/hooks/lib/model-defaults-review.test.js +64 -1
  59. package/plugins/trailhead/hooks/lib/model-route.js +207 -0
  60. package/plugins/trailhead/hooks/lib/model-route.test.js +231 -0
  61. package/plugins/trailhead/hooks/lib/needs-you.test.js +45 -1
  62. package/plugins/trailhead/hooks/lib/plan-review-rounds.test.js +272 -0
  63. package/plugins/trailhead/hooks/lib/repo-target.test.js +256 -0
  64. package/plugins/trailhead/hooks/lib/report-status.test.js +56 -1
  65. package/plugins/trailhead/hooks/lib/statusline-git-env.test.js +47 -0
  66. package/plugins/trailhead/hooks/lib/test-run.test.js +757 -0
  67. package/plugins/trailhead/hooks/lib/trailhead-git-env.js +40 -0
  68. package/plugins/trailhead/hooks/mod/README.md +42 -17
  69. package/plugins/trailhead/hooks/mod/auto-run.ts +2 -1
  70. package/plugins/trailhead/hooks/mod/git-env.ts +31 -0
  71. package/plugins/trailhead/hooks/mod/heredoc.ts +79 -0
  72. package/plugins/trailhead/hooks/mod/map-pane-view.ts +3 -1
  73. package/plugins/trailhead/hooks/mod/map-pane.tsx +204 -49
  74. package/plugins/trailhead/hooks/mod/my-tickets.ts +76 -0
  75. package/plugins/trailhead/hooks/mod/repo-pin.ts +113 -9
  76. package/plugins/trailhead/hooks/mod/work-state-locate.ts +2 -1
  77. package/plugins/trailhead/hooks/trailhead-bash-guards.js +8 -8
  78. package/plugins/trailhead/hooks/trailhead-bash-guards.test.js +28 -14
  79. package/plugins/trailhead/hooks/trailhead-check-update.js +20 -2
  80. package/plugins/trailhead/hooks/trailhead-check-update.test.js +60 -0
  81. package/plugins/trailhead/hooks/trailhead-labels-guard.js +2 -2
  82. package/plugins/trailhead/hooks/trailhead-labels-guard.test.js +1 -1
  83. package/plugins/trailhead/hooks/trailhead-test-guard.js +232 -0
  84. package/plugins/trailhead/hooks/trailhead-test-guard.test.js +172 -0
  85. package/plugins/trailhead/skills/_shared/capture-core.md +123 -0
  86. package/plugins/trailhead/skills/_shared/choices.md +2 -2
  87. package/plugins/trailhead/skills/_shared/configuration-reference.md +30 -16
  88. package/plugins/trailhead/skills/_shared/configuration.md +1 -1
  89. package/plugins/trailhead/skills/_shared/load-first.md +2 -0
  90. package/plugins/trailhead/skills/_shared/session-handoff.md +1 -1
  91. package/plugins/trailhead/skills/_shared/substrate-commands.md +19 -6
  92. package/plugins/trailhead/skills/_shared/substrate.md +4 -3
  93. package/plugins/trailhead/skills/_shared/surfaced-scope.md +23 -0
  94. package/plugins/trailhead/skills/_shared/teamwork.md +11 -5
  95. package/plugins/trailhead/skills/_shared/techniques/acceptance-testing.md +1 -1
  96. package/plugins/trailhead/skills/_shared/techniques/code-review-pipeline.md +24 -0
  97. package/plugins/trailhead/skills/_shared/techniques/code-review.md +22 -6
  98. package/plugins/trailhead/skills/_shared/techniques/codebase-map.md +6 -3
  99. package/plugins/trailhead/skills/_shared/techniques/debug.md +5 -2
  100. package/plugins/trailhead/skills/_shared/techniques/grilling.md +28 -3
  101. package/plugins/trailhead/skills/_shared/techniques/plan-review.md +16 -4
  102. package/plugins/trailhead/skills/_shared/techniques/research.md +4 -1
  103. package/plugins/trailhead/skills/_shared/techniques/tdd.md +4 -2
  104. package/plugins/trailhead/skills/_shared/techniques/verify.md +5 -1
  105. package/plugins/trailhead/skills/_shared/techniques.md +8 -6
  106. package/plugins/trailhead/skills/trailhead-capture/SKILL.md +2 -4
  107. package/plugins/trailhead/skills/trailhead-capture/references/capture.md +29 -4
  108. package/plugins/trailhead/skills/trailhead-chart/SKILL.md +2 -2
  109. package/plugins/trailhead/skills/trailhead-chart/references/charting.md +5 -5
  110. package/plugins/trailhead/skills/trailhead-chart/references/inbox.md +11 -6
  111. package/plugins/trailhead/skills/trailhead-manage/references/auditing.md +18 -3
  112. package/plugins/trailhead/skills/trailhead-work/SKILL.md +12 -9
  113. package/plugins/trailhead/skills/trailhead-work/references/auto.md +3 -1
  114. package/plugins/trailhead/skills/trailhead-work/references/executor-brief.md +2 -0
  115. package/plugins/trailhead/skills/trailhead-work/references/ticket-engines.md +13 -13
  116. package/plugins/trailhead/templates/statusline-trailhead.sh +10 -3
  117. package/plugins/trailhead/templates/trailhead-audit-walk.js +56 -4
  118. package/plugins/trailhead/templates/trailhead-auto-merge.js +2 -1
  119. package/plugins/trailhead/templates/trailhead-auto-queue.js +2 -1
  120. package/plugins/trailhead/templates/trailhead-auto-slot.js +2 -1
  121. package/plugins/trailhead/templates/trailhead-base-preflight.js +10 -5
  122. package/plugins/trailhead/templates/trailhead-capture.js +419 -0
  123. package/plugins/trailhead/templates/trailhead-checkout-find.js +257 -0
  124. package/plugins/trailhead/templates/trailhead-checkout-in.js +78 -21
  125. package/plugins/trailhead/templates/trailhead-commit-msg-sync.js +2 -1
  126. package/plugins/trailhead/templates/trailhead-dashboard.js +15 -2
  127. package/plugins/trailhead/templates/trailhead-git-env.js +40 -0
  128. package/plugins/trailhead/templates/trailhead-inbox-needs-info.js +161 -0
  129. package/plugins/trailhead/templates/trailhead-isolation-answer.js +2 -1
  130. package/plugins/trailhead/templates/trailhead-isolation-copy.js +9 -4
  131. package/plugins/trailhead/templates/trailhead-issue-body.js +161 -12
  132. package/plugins/trailhead/templates/trailhead-merge-ticket.js +2 -1
  133. package/plugins/trailhead/templates/trailhead-needs-you.js +38 -4
  134. package/plugins/trailhead/templates/trailhead-plan-review-rounds.js +261 -7
  135. package/plugins/trailhead/templates/trailhead-repo-target.js +302 -0
  136. package/plugins/trailhead/templates/trailhead-report-status.js +57 -9
  137. package/plugins/trailhead/templates/trailhead-test-run.js +752 -0
  138. package/plugins/trailhead/templates/trailhead-uat-backfill.js +2 -1
  139. package/plugins/trailhead/templates/trailhead-uat-followup.js +2 -1
  140. package/plugins/trailhead/templates/trailhead-uat-setup.js +9 -4
  141. package/plugins/trailhead/templates/trailhead-unblock.js +2 -1
  142. package/plugins/trailhead/tests/auto-run-ui.test.ts +2 -1
  143. package/plugins/trailhead/tests/git-env.test.ts +20 -0
  144. package/plugins/trailhead/tests/map-pane-ui.test.ts +386 -50
  145. package/plugins/trailhead/tests/map-pane-view.test.ts +6 -0
  146. package/plugins/trailhead/tests/map-pane-write.test.ts +57 -1
  147. package/plugins/trailhead/tests/my-tickets.test.ts +66 -0
  148. package/plugins/trailhead/tests/repo-pin-late-repo.test.ts +3 -2
  149. package/plugins/trailhead/tests/repo-pin-relocate.test.ts +175 -0
  150. package/plugins/trailhead/tests/repo-pin.test.ts +20 -5
  151. package/plugins/trailhead/tests/unscrub.ts +8 -0
  152. package/plugins/trailhead/tests/work-state-prompt-hook.test.ts +3 -2
  153. package/plugins/trailhead/tests/work-state-worktree.test.ts +2 -1
  154. package/plugins/trailhead/types/index.d.ts +7 -0
@@ -28,7 +28,7 @@ Read **`references/charting.md`** and follow it. It holds both protocols: **Mode
28
28
 
29
29
  ## Open a ticket on the fly: `ticket <type> <title>`
30
30
 
31
- Open ticket(s) of the map on the fly, for any of the six types (`decision`, `research`, `prototype`, `build`, `bug`, `task`), the escape hatch the capture verbs do not cover. **Adding a ticket is a micro-charting act, so diverge briefly first, do not blind-commit to a single piece:** run a short breadth-first pass around the request: is this really *one* session-sized ticket, or a small **cluster** (a `decision` that needs a `research` before it, a UI `build` that needs a `prototype`, obvious siblings)? Does it imply a blocker? Surface the neighbours, *then* create the ticket(s): each gets `trailhead:ticket` plus its `trailhead:<type>`, its map's `trailhead:map-<n>` label and native sub-issue edge, a `Parent:` line, and a `## Question`; put each on the frontier, or, for a known-blocked one, create it already carrying `trailhead:blocked` (in its `--label` list) and wire its `## Blocked by` + native dependency (the three-move blocker wiring, with the label riding at creation, and the child-to-map link are in `../_shared/substrate.md`; the exact gh commands are in `../_shared/substrate-commands.md`). This is a framing brainstorm (is this the right work?), distinct from the `decision` engine's option brainstorm (which choice?). If `<type>` is missing or invalid, ask which of the six. *(The zero-friction captures, `bug` / `todo` / `idea` / `seed` / `note`, deliberately skip this diverge-first pass; they are one action, one confirmation, and are served by the capture cluster.)*
31
+ Open ticket(s) of the map on the fly, for any of the six types (`decision`, `research`, `prototype`, `build`, `bug`, `task`), the escape hatch the capture verbs do not cover. **Adding a ticket is a micro-charting act, so diverge briefly first, do not blind-commit to a single piece:** run a short breadth-first pass around the request: is this really *one* session-sized ticket, or a small **cluster** (a `decision` that needs a `research` before it, a UI `build` that needs a `prototype`, obvious siblings)? Does it imply a blocker? Surface the neighbours, *then* create the ticket(s): each gets `trailhead:ticket` plus its `trailhead:<type>`, its map's `trailhead:map-<n>` label and native sub-issue edge, a `Parent:` line, and a `## Question`; put each on the frontier, or, for a known-blocked one, create it already carrying `trailhead:blocked` (in its `--label` list) and wire its `## Blocked by` + native dependency (the three-move blocker wiring, with the label riding at creation, and the child-to-map link are in `../_shared/substrate.md`; the exact gh commands are in `../_shared/substrate-commands.md`; a cluster follows **Cross-references in a bulk create** there). This is a framing brainstorm (is this the right work?), distinct from the `decision` engine's option brainstorm (which choice?). If `<type>` is missing or invalid, ask which of the six. *(The zero-friction captures, `bug` / `todo` / `idea` / `seed` / `note`, deliberately skip this diverge-first pass; they are one action, one confirmation, and are served by the capture cluster.)*
32
32
 
33
33
  ## Triage the inbox: `inbox [issue]`
34
34
 
@@ -36,7 +36,7 @@ Read **`references/inbox.md`** and follow it. It triages issues opened by others
36
36
 
37
37
  ## Standalone grill: `grill [topic|ticket]`
38
38
 
39
- Run a standalone **Grilling** (plus **Domain vocabulary**) session on a decision or topic, or on a named ticket, without committing to the full work cycle. Read the two techniques and follow them: `../_shared/techniques/grilling.md` and `../_shared/techniques/domain-vocabulary.md` (these are multi-cluster techniques, so their bodies live in `_shared/`). Before the final confirmation of the choices, run the **Holistic review before the final confirmation** defined in that technique. Record the outcome where it belongs: a ticket's resolution, the map's `Decisions so far`, or a fresh `decision` ticket. **When the session resolves a `decision` ticket**, its close follows the decision engine's **Carry it into build** step (`../trailhead-work/references/ticket-engines.md`): the build split goes into the final confirmation, the build tickets are created, and the resolution comment carries the `Builds:` line before `gh issue close`. **When the session's decision unblocks or spawns follow-on tickets** (builds graduating from a resolved blocker, or siblings the decision implies), opening them **defers to the shared "Map or whiteboard" rule** (`../trailhead-capture/references/capture.md`): when one or more maps are open, ask the user which destination (a thematically eligible map vs the whiteboard), never silently picking the whiteboard and never treating "no coherent map" as a reason to auto-drop them there. Only with **no** open map does the whiteboard follow without asking.
39
+ Run a standalone **Grilling** (plus **Domain vocabulary**) session on a decision or topic, or on a named ticket, without committing to the full work cycle. Read the two techniques and follow them: `../_shared/techniques/grilling.md` and `../_shared/techniques/domain-vocabulary.md` (these are multi-cluster techniques, so their bodies live in `_shared/`). Before any question, first run the routing step at the top of `grilling.md`, then follow **Who runs a round** there (`models.grill`); this session keeps asking the user, with the options in chat per **The ask is answerable from the chat alone**. Before the final confirmation of the choices, run the **Holistic review before the final confirmation** defined in that technique. Record the outcome where it belongs: a ticket's resolution, the map's `Decisions so far`, or a fresh `decision` ticket. **When the session resolves a `decision` ticket**, its close follows the decision engine's **Carry it into build** step (`../trailhead-work/references/ticket-engines.md`): the build split goes into the final confirmation, the build tickets are created, and the resolution comment carries the `Builds:` line before `gh issue close`. **When the session's decision unblocks or spawns follow-on tickets** (builds graduating from a resolved blocker, or siblings the decision implies), opening them **defers to the shared "Map or whiteboard" rule** (`../trailhead-capture/references/capture.md`): when one or more maps are open, ask the user which destination (a thematically eligible map vs the whiteboard), never silently picking the whiteboard and never treating "no coherent map" as a reason to auto-drop them there. Only with **no** open map does the whiteboard follow without asking.
40
40
 
41
41
  ## First-time config offer (end of chart / adopt)
42
42
 
@@ -6,10 +6,10 @@ The two ways a map is born. Read this when the invocation is `/trailhead:new` or
6
6
  The user invokes with a loose idea.
7
7
 
8
8
  0. **Ensure a tracker repo exists (offer to create one if missing).** Everything trailhead writes lives on a GitHub repo's Issues, so before charting, confirm the current directory has one. Detect it with `gh repo view`: on success a GitHub repo with a remote is present, so say nothing and proceed. If there is **no GitHub repo** (no `origin` / GitHub remote, or `gh repo view` fails), **stop and ask the user** whether to create one, offering **private (default)** or **public**. On their choice: if the directory is not yet a git repo, `git init` and make an initial commit first; then `gh repo create <name> --private|--public --source=. --remote=origin --push` (take `<name>` from the directory, confirmed with the user), so the repo exists on GitHub with `origin` set. Charting continues only once a GitHub repo is in place: every step below writes to its Issues. If the user declines to create one, there is nowhere to chart, so stop and say so. This never fires when a repo already exists (`/trailhead:adopt` and re-charting on an existing repo skip straight past it).
9
- 1. **Name the destination.** Run the **Grilling** + **Domain vocabulary** techniques to pin down what this map tends toward. The destination fixes the scope, so it's settled first. Default: a working artifact.
10
- 2. **Map the frontier.** Grill again, **breadth-first**: **enumerate every functional area the destination implies** and **capture each one (headed for a fog entry under *Not yet specified* at minimum) before deep-diving any single area**. Fan out across the whole space rather than deep on one thread, surfacing the open decisions and the first steps takeable now. **The deliverable is the coverage the map will carry, not the grilling transcript: every area the destination implies must land on the map at step 3, as fog or as a ticket, so nothing is silently missing.** **If no fog surfaces** (the way is already clear, the whole thing fits one session) you don't need a map: stop and ask how to proceed. **Harvest parked work:** from **earlier maps on this repo**, gather the artifacts their *Out of scope* `→ future map` breadcrumbs point at (**parked seeds/todos**, see **Out of scope**) **and the ideas** left in their *Not yet specified* fog, then **ask the user which belong in *this* map**; pull the chosen seeds/todos in as tickets and the chosen ideas in as fog (or as tickets, if now sharp enough), and leave (or update the destination on) the rest.
9
+ 1. **Name the destination.** Run the **Grilling** + **Domain vocabulary** techniques (first run the routing step at the top of `../../_shared/techniques/grilling.md`, then the rounds as **Who runs a round** there says) to pin down what this map tends toward. The destination fixes the scope, so it's settled first. Default: a working artifact.
10
+ 2. **Map the frontier.** Grill again (first run the routing step at the top of `../../_shared/techniques/grilling.md`, then the rounds as **Who runs a round** there says), **breadth-first**: **enumerate every functional area the destination implies** and **capture each one (headed for a fog entry under *Not yet specified* at minimum) before deep-diving any single area**. Fan out across the whole space rather than deep on one thread, surfacing the open decisions and the first steps takeable now. **The deliverable is the coverage the map will carry, not the grilling transcript: every area the destination implies must land on the map at step 3, as fog or as a ticket, so nothing is silently missing.** **If no fog surfaces** (the way is already clear, the whole thing fits one session) you don't need a map: stop and ask how to proceed. **Harvest parked work:** from **earlier maps on this repo**, gather the artifacts their *Out of scope* `→ future map` breadcrumbs point at (**parked seeds/todos**, see **Out of scope**) **and the ideas** left in their *Not yet specified* fog, then **ask the user which belong in *this* map**; pull the chosen seeds/todos in as tickets and the chosen ideas in as fog (or as tickets, if now sharp enough), and leave (or update the destination on) the rest.
11
11
  3. **Create the map** (`trailhead:map`), after the **Holistic review before the final confirmation** of the Grilling technique has run on the step 1 destination and the step 2 choices together: Destination and Notes filled in, Decisions-so-far empty, the fog sketched into *Not yet specified*. **Every functional area enumerated in step 2 must appear here, as a fog entry or a ticket, before moving on: the map is the coverage deliverable.** Run the **first-use repo setup** (see **Substrate** → `../../_shared/substrate-commands.md`) now: labels **and** the label-guard check/install. Once the map issue has its number `<n>`, **create its per-map label `trailhead:map-<n>`** (every ticket of this map carries it and is a native sub-issue of the map; the map issue itself is found by its number, so it does **not** carry the label; see **Multiple maps on one repo**). **If this repo already has another open `trailhead:map`**, tell the user the new map runs concurrently (its own scoped frontier) and set it as the active map (`.trailhead/active-map`). Then set up **conventions**: if no `trailhead:conventions` issue exists (singleton lookup: see **Singletons** in `../../_shared/substrate.md`), run a **brief conventions brainstorming** (breadth-first, in the spirit of grilling but short: a handful of questions, not a full session) to surface the project's way of working: `git:` `main`|`pr`, `release:` `command`|`auto`, and `isolation:` `auto`|`none`|`worktree`|`clone` (default **`auto`**, which decides per ticket at work-start by asking *"will more than one ticket be worked at once on this machine this session?"* and picking `worktree` for a plain repo or `clone` for a submodule superrepo; a project can force `none`/`worktree`/`clone` explicitly), **plus** the narrative a new contributor would need: release/deploy specifics (environments, manual steps, OTA vs full build, versioning), tooling that must be driven a certain way (a specific MCP/CLI, e.g. a Lovable project piloted via its MCP), code & style rules, testing/UAT norms. Distil into the issue (header + prose); link it from the map's Notes. Don't invent: leave out or ask what you can't determine. If one already exists, just link it (reuse across maps). Finally, **ensure the repo's `trailhead:dashboard` index issue exists** (see **The dashboard**): create and pin it if missing (the fixed 3rd pinned slot, alongside codebase + conventions), then **write it to list this map** (charting a map is a structural event); if it already exists, refresh it.
12
- 4. **Create the tickets you can specify now** (the **Ticket or fog** test below) as child issues (each with `trailhead:ticket` + its type + this map's `trailhead:map-<n>` label, **plus `trailhead:blocked` when the mapped dependency graph already shows it has a blocker**, and set as a **native sub-issue** of the map, see **Child→map link** under Substrate), then wire the blocking in a **second pass** (issues need ids before they can reference each other): for each ticket **read its Question and wire a blocker for every still-open ticket whose output it consumes** (don't stop at the first/obvious one) by wiring its `## Blocked by` line + native dependency together (moves 1-2 of the three-move wiring; the `trailhead:blocked` label, move 3, already rode in at creation, see **Substrate**). **Set the blocked label at creation, not via a post-creation `--add-label` pass:** a bulk `--add-label` burst spawns one repo-side label-guard run per ticket and reddens the Actions history with sheddable no-op runs; riding the label in the ticket's `issues.opened` event avoids that entirely. Wiring sorts them into frontier (unblocked) and blocked; the rest stays fog. **Ticket or fog.** Run it on every area from step 2: "specify now" means you can write the area's `## Question` now, sized to one session, not that it can be answered now. An area whose Question you can write is a ticket, created already blocked (`trailhead:blocked` plus its wiring) on the tickets whose answers it needs: being blocked is not a reason to leave it in the fog, because a blocked ticket graduates onto the frontier by itself when its blockers close, while a fog entry waits for someone to dissolve it. An area stays fog only when you **cannot phrase** its Question yet (you don't know what to ask: "Monetization" before the audience is known), or when an open decision could **reshape** the question itself (not just its answer), in which case the fog entry names that decision so it is re-examined when that decision closes. An area too big for one session is several tickets, or one ticket plus fog for the rest. Volume is not a reason to keep an area in the fog: the only brake is GitHub's 100 sub-issue cap per map, and near it, say so and keep the furthest-out areas as fog.
12
+ 4. **Create the tickets you can specify now** (the **Ticket or fog** test below) as child issues (each with `trailhead:ticket` + its type + this map's `trailhead:map-<n>` label, **plus `trailhead:blocked` when the mapped dependency graph already shows it has a blocker**, and set as a **native sub-issue** of the map, see **Child→map link** under Substrate), then wire the blocking in a **second pass** (issues need ids before they can reference each other): for each ticket **read its Question and wire a blocker for every still-open ticket whose output it consumes** (don't stop at the first/obvious one) by wiring its `## Blocked by` line + native dependency together (moves 1-2 of the three-move wiring; the `trailhead:blocked` label, move 3, already rode in at creation, see **Substrate**). Every other cross-reference between these tickets is written in this same pass, from the ids just created, never a guessed number (**Cross-references in a bulk create** in `../../_shared/substrate-commands.md`). **Set the blocked label at creation, not via a post-creation `--add-label` pass:** a bulk `--add-label` burst spawns one repo-side label-guard run per ticket and reddens the Actions history with sheddable no-op runs; riding the label in the ticket's `issues.opened` event avoids that entirely. Wiring sorts them into frontier (unblocked) and blocked; the rest stays fog. **Ticket or fog.** Run it on every area from step 2: "specify now" means you can write the area's `## Question` now, sized to one session, not that it can be answered now. An area whose Question you can write is a ticket, created already blocked (`trailhead:blocked` plus its wiring) on the tickets whose answers it needs: being blocked is not a reason to leave it in the fog, because a blocked ticket graduates onto the frontier by itself when its blockers close, while a fog entry waits for someone to dissolve it. An area stays fog only when you **cannot phrase** its Question yet (you don't know what to ask: "Monetization" before the audience is known), or when an open decision could **reshape** the question itself (not just its answer), in which case the fog entry names that decision so it is re-examined when that decision closes. An area too big for one session is several tickets, or one ticket plus fog for the rest. Volume is not a reason to keep an area in the fog: the only brake is GitHub's 100 sub-issue cap per map, and near it, say so and keep the furthest-out areas as fog.
13
13
  5. **Fire the research subagents.** For each `research` ticket created, run the **Research** technique in parallel; each agent returns its report, and the orchestrator posts it on the ticket as a `RESEARCH` comment (no branch: the report is the artifact; see the technique for the `found` / `inconclusive` / `needs_input` branches).
14
14
  6. **Offer config, then stop.** Charting is one session's work; it hand-resolves nothing. Before ending, make the **first-time config offer** (see **First-time config offer** in `../SKILL.md`): the project now has a fresh map and no `.trailhead/config.json`, so put the **two-way choice** to the user, **accept as-is** (writes `{}`) or **run `/trailhead:config`** for the guided menu, and let them pick. Name both paths; don't pre-fill the config keys and ask for an inline edit, and don't interview them key-by-key here (that's `/trailhead:config`'s job). Once the file exists it's never offered again. **No `trailhead:codebase` issue is created here**: greenfield has no code to map yet. It's created later, once the repo has substantial code (see Mode 2 step 1 and the **Codebase** row under Substrate), so at chart two of the three permanent pins (the dashboard + conventions) are filled and the codebase slot fills in later. Maps are never pinned.
15
15
 
@@ -27,8 +27,8 @@ Same rules as the handoff block: **name the ticket, put its number (never the ti
27
27
  When the code already exists (project in progress). Like Mode 1, but start from **reality**, not a blank page. One heavy step at entry, then all lean.
28
28
 
29
29
  1. **Map the codebase (once per repo).** Heavy step, *only* at adoption: it does not repeat per ticket, and **not per map**. First check for an existing **`trailhead:codebase`** issue (singleton lookup: see **Singletons** in `../../_shared/substrate.md`): if one exists (an earlier map on this repo created it) and the code hasn't materially drifted, just **link it** from this map's Notes and skip the fan-out. Otherwise run the **Codebase map** technique (a parallel fan-out of reader subagents) for a structured understanding (architecture, stack, decisions already embodied, risk areas) and distil the essence into the repo's **`trailhead:codebase` issue** (create it, label `trailhead:codebase`), then link that issue from the map's Notes. Discard the raw. Because it's a standalone repo-scoped issue, the next map reuses it instead of re-deriving or losing it.
30
- 2. **Name the destination of the remaining stretch.** **Grilling** + **Domain vocabulary**, **seeded by the map you just built**: not "what is the project" but "what's left to reach working / the next milestone"; close with the **Holistic review before the final confirmation** of the Grilling technique.
30
+ 2. **Name the destination of the remaining stretch.** **Grilling** + **Domain vocabulary** (first run the routing step at the top of `../../_shared/techniques/grilling.md`, then the rounds as **Who runs a round** there says), **seeded by the map you just built**: not "what is the project" but "what's left to reach working / the next milestone"; close with the **Holistic review before the final confirmation** of the Grilling technique.
31
31
  3. **Backfill the decisions already made.** Choices already embodied in the code (now visible from the map) or stated by the user go into `Decisions so far` as **ticket-less** lines (they're already closed), so the map reflects reality and doesn't pretend greenfield. Link to code/commits where useful.
32
- 4. **Map the frontier of the remainder**, **breadth-first as in Mode 1 step 2**, closing with the **Holistic review before the final confirmation** of the Grilling technique over this step's choices and step 2's destination together: enumerate every functional area still standing between the code and the destination and **land each on the map (fog in *Not yet specified* at minimum) before deep-diving any one**, so the map makes every remaining section visible; the map, not the transcript, is the deliverable to check for that coverage. This yields → tickets specifiable now + fog in *Not yet specified*, then wire the blocking and fire the research. **Harvest parked work** (todos *and* ideas) from earlier maps on this repo (their `→ future map` *Out of scope* lines and their *Not yet specified* fog), asking the user which belong here, as in Mode 1 step 2. As Mode 1, steps 4–6.
32
+ 4. **Map the frontier of the remainder**, **breadth-first as in Mode 1 step 2** (first run the routing step at the top of `../../_shared/techniques/grilling.md`, then the rounds as **Who runs a round** there says), closing with the **Holistic review before the final confirmation** of the Grilling technique over this step's choices and step 2's destination together: enumerate every functional area still standing between the code and the destination and **land each on the map (fog in *Not yet specified* at minimum) before deep-diving any one**, so the map makes every remaining section visible; the map, not the transcript, is the deliverable to check for that coverage. This yields → tickets specifiable now + fog in *Not yet specified*, then wire the blocking and fire the research. **Harvest parked work** (todos *and* ideas) from earlier maps on this repo (their `→ future map` *Out of scope* lines and their *Not yet specified* fog), asking the user which belong here, as in Mode 1 step 2. As Mode 1, steps 4–6.
33
33
 
34
34
  The tracker is the existing repo's (`gh` in its directory); run the **first-use repo setup** (see `../../_shared/substrate-commands.md`) on first use (missing `trailhead:*` labels **and** the label-guard check/install (adopting an existing repo is exactly when the guard tends to be missing)) and set up the `trailhead:conventions` issue via a **brief conventions brainstorming** (see Mode 1 step 3), but here, **seed the questions from what's discoverable** so you confirm rather than ask blind: an existing `CLAUDE.md`/`AGENTS.md`, CI workflows, the PR-vs-`main` git history, package scripts, deploy config. **Detect a monorepo** (a `pnpm-workspace.yaml`, a `workspaces` field, an `nx.json`/`turbo.json`, or several packages under `packages/`|`apps/`) and, if found, keep the default `isolation: auto` (it picks `worktree` for such a plain/monorepo checkout at work-start) and note that tickets should carry a `Scope:` line, **unless the buildable units are path-bound** (see below). **Detect git submodules** (a `.gitmodules` file) and, if found, add the prose rule that submodule work is committed **inside the submodule (on a branch), published, then a gitlink bump in the parent**, and that a **submodule-bearing superrepo resolves to `clone`** under the default `auto` (a superproject `worktree` cannot safely carry submodule state, so worktree is escalated to `clone`); a submodule path is a natural `Scope:` (see **Working as a team**). **For path-bound tooling** (a React Native/Expo app, native toolchains, a package whose build depends on a local `node_modules`/bundler cache/absolute-path config): those don't build from a worktree, so for them prefer an explicit **`isolation: clone`** (a per-ticket clone that installs its own deps and builds), or `isolation: none` + **serialize** if the user doesn't want per-ticket clones. Worktree is for units that build from any path. Propose the values you inferred and let the human correct/complete them; create the issue if absent, link it from the map's Notes. Also **ensure the repo's `trailhead:dashboard` index issue exists** (see **The dashboard**): create and pin it if missing (the fixed 3rd pinned slot, alongside codebase + conventions) and write it to list this map.
@@ -2,14 +2,16 @@
2
2
 
3
3
  `/trailhead:inbox [issue]` triages issues that aren't trailhead tickets (bug reports, feature requests, questions) and **integrates the worthwhile ones into the map**, keeping the reporter's authorship.
4
4
 
5
- `/trailhead:inbox` presents **three sections**, each named for what it holds. **Present them to the user by these descriptive names, never as a bare letter or "section A/B/C"** (result-oriented output: no internal labels). The three:
5
+ `/trailhead:inbox` presents **four sections**, each named for what it holds. **Present them to the user by these descriptive names, never as a bare letter or "section A/B/C"** (result-oriented output: no internal labels). The four:
6
6
 
7
- **Incoming issues to triage.** Open issues carrying **no** `trailhead:*` label (plus any `trailhead:unverified` from the trust guard):
7
+ **Incoming issues to triage.** Open issues carrying **no** `trailhead:*` label (plus any `trailhead:unverified`, applied by a session under the Trust rule in `../../_shared/teamwork.md`; the guard Action strips labels instead):
8
8
  ```bash
9
9
  gh issue list --state open --json number,title,author,labels \
10
- | jq '[.[] | select([.labels[].name] | any(startswith("trailhead:")) | not)]'
10
+ | jq '[.[] | select(([.labels[].name] | any(startswith("trailhead:")) | not) or ([.labels[].name] | any(. == "trailhead:unverified")))]'
11
11
  ```
12
12
 
13
+ **Waiting on the reporter.** The open `trailhead:needs-info` issues: ones the maintainer asked a question and parked. They are absent from the incoming query (the `trailhead:*` label drops them), and they come back only when the reporter answers. Read them with the read-only helper, never by hand (a per-issue loop is what goes wrong): `node <templates>/trailhead-inbox-needs-info.js --repo <owner/name> --config-root <project root>`, with `<templates>` found by the same three-dir lookup as `trailhead-work-state.js` (see `../../_shared/teamwork.md`, Work state file). It prints one JSON line `{status, days, answered, timedOut, waiting, unknown, warnings}`: `answered` is an issue whose reporter commented after the label (it wins over the timeout), `timedOut` one with no answer after `days` whole days (`inbox.needs_info_days`, default 14, see `../../_shared/configuration-reference.md`), `waiting` the rest, `unknown` one the helper could not classify (a failed read or a reporter with no usable identity), named with its reason and never offered for closing. Surface `warnings` in one line. Show the answered ones, each proposed for triage by the moves below; the timed-out ones, each offered for closing (see **Needs info**); the still-waiting ones as one count line, no list; and any unknown ones by number and reason. If the helper fails (a missing script, a node error, `status: error`), say the waiting section could not be read and go on with the other sections; never guess which are answered or timed out.
14
+
13
15
  **Parked fog ready to graduate?** The open `trailhead:fog` issues, **most-recently-active first**: recent discussion is exactly where the fog has likely cleared, so you review those, not all of them:
14
16
  ```bash
15
17
  gh issue list --label trailhead:fog --state open --search "sort:updated-desc"
@@ -18,12 +20,15 @@ This is **how you notice a fog has cleared**: you don't watch each thread; you r
18
20
 
19
21
  **Out-of-scope lines actually deferred?** Scan the map's *Out of scope* section for lines that are **deferred**, not truly ruled out: ones that **name a gate or trigger** (*gated by X*, *quando X*, *dipende da X*), carry a **this-only scope qualifier** (*per questo slice*, *for now*, *in this milestone*, not for the product), describe a **feature wanted later** (already annotated `→ future map`, or plainly still on the roadmap), or **are the gate of an existing `trailhead:seed`**. That last one is a **near-deterministic check**: cross-reference every open `trailhead:seed`'s trigger / `## Blocked by` against the *Out of scope* lines; a line some seed waits on is a **live gate**, so it cannot be "ruled out" and the cluster is **mis-wired** (the gate was left behind), re-route it. A line **decided against for the product** (won't be built, wrong approach, no "for this slice" qualifier) stays put; and a line **already properly parked** (annotated `→ future map` with its dependent seeds wired to a live trigger) is settled, skip it. Only **un-parked** deferred lines surface here. For each deferred line, **ask the user** (never decide silently) how to re-route it, `seed` / `idea` / `todo`, and **which map** it belongs to (this one, or a future / other map), then apply the **out-of-scope-vs-deferred** rules (see `../../_shared/out-of-scope.md`): **re-route the gate first**, tie its dependents to the gate's new form with a **live trigger** (never a dead "ruled out" line), and **move the whole cluster together**. Update or remove the *Out of scope* line to match where each item went (a future-map item stays as an annotated `→ future map` line; an item pulled into this map leaves *Out of scope* entirely).
20
22
 
21
- For each issue in the first two sections (incoming issues, parked fog), propose a disposition (one of the moves below, with a one-line why) and put them to the user as one list, which they confirm, or amend item by item, before anything is written: every move labels, comments on or closes an issue someone else opened, and an out-of-scope ruling is the human's (`../../_shared/choices.md`). Act only on the user's explicit yes; never close or comment on an issue on your own judgement, and silence or an ambiguous reply leaves it untouched. The moves:
23
+ For each issue in the first three sections (incoming issues, waiting on the reporter, parked fog; for the waiting ones, the answered issues and the timed-out offers), propose a disposition (one of the moves below, with a one-line why) and put them to the user as one list, which they confirm, or amend item by item, before anything is written: every move labels, comments on or closes an issue someone else opened, and an out-of-scope ruling is the human's (`../../_shared/choices.md`). Act only on the user's explicit yes; never close or comment on an issue on your own judgement, and silence or an ambiguous reply leaves it untouched. The moves:
22
24
 
23
25
  - **Adopt → a ticket.** Integrate it *in place* (same issue number, so the reporter stays credited): append a trailhead block to the body: `Parent: <map name+link>`, `Adopted-from: opened by @<author>`, a reframed `## Question` (the decision/build the *map* will act on), and `## Blocked by`, **without erasing the reporter's original text above it**. Apply `trailhead:ticket` + the right type label (`bug`/`decision`/`build`/`research`/`task`) as a write-access maintainer (which also satisfies Trust & provenance), plus the map's `trailhead:map-<n>` label + native sub-issue edge (like any ticket, so it joins the right map's frontier); add `trailhead:blocked` if it has an open blocker. It lands on the frontier. Comment to the reporter that it's been picked up.
24
26
  - **Fog.** In scope but not sharp enough to ticket → **keep the issue open and label it `trailhead:fog`** (parked: still visible and tracked, but the `trailhead:*` label drops it out of the inbound query, so it won't re-surface at every triage). Then use the open issue as a **clarification space**: post a comment with the questions that would make it concrete, in the spirit of **Grilling**, but async ("what would *done* look like? which users? where does it show?"), and invite the reporter and anyone interested to refine it in the thread. The fog dissipates through that conversation as much as through other work landing; others can carry the discussion with the reporter without you in the loop. **You don't lose it**: `gh issue list --label trailhead:fog` is the durable list, and the reporter follows their own open issue. When the thread (or the wider work) has made the question sharp, **adopt it in place**: swap `trailhead:fog` for `trailhead:ticket` + the type label, add `Parent:` and the now-answerable `## Question`, and it lands on the frontier: same issue, same reporter, the clarifying discussion preserved above.
25
27
  - **Out of scope.** Beyond the destination → label `trailhead:out-of-scope`, **drop its native sub-issue edge** to reclaim a slot (the eager drop in `../../_shared/substrate-commands.md`, 404-tolerant, so an un-adopted inbound issue with no edge is a no-op), then close with the why.
26
28
  - **Duplicate.** In the proposal, name what each one covers (the issue and the existing ticket) and where they differ; only when the existing ticket covers all of it, link it and close. A partial overlap is not a duplicate: link the two and triage the issue by the other moves.
27
- - **Needs info.** Ask the reporter for a repro or specifics; leave it open.
29
+ - **Needs info.** Ask the reporter for a repro or specifics; leave it open and park it. Ensure the label exists (`gh label create trailhead:needs-info --color FBCA04 --description "Waiting on the reporter: back in the inbox when they answer" 2>/dev/null || true`), post the question **first**, then add `trailhead:needs-info`. The order matters: when the maintainer is also the reporter, a question posted after the label would count as the answer. The label parks the issue (it drops out of the incoming query) and starts the clock.
30
+ - **Resurfacing.** The issue returns, in the **Waiting on the reporter** section, only when the reporter comments after the label. On an answered issue the moves are the same as for a new issue, each **dropping `trailhead:needs-info` first**. **Ask again** posts the new question, then removes and re-adds the label (two calls: adding a label that is already present makes no event, so the clock would not restart and the issue would resurface on every run). A failed removal leaves the old clock, so it resurfaces next run; a failed re-add leaves it in the incoming queue: both stay visible, and both are reported.
31
+ - **Timeout.** After `days` with no answer the issue is only **offered** for closing, as one more item in the same confirmed list as every other move; it is never closed automatically. On a yes: remove the label, **then** `gh issue close <n> --reason "not planned" --comment "<standard message>"`, in that order (a failed close leaves an open issue back in the incoming queue, visible; a failed removal after the close would leave a reopened issue parked on a stale clock, so a reopened issue is back in the incoming query). The standard message, written in `config.ticket.language`, states when the question was asked and how many days passed, and asks for a new issue linking this one (a reporter without write access cannot reopen an issue a maintainer closed, and a comment on a closed issue would never resurface). **Keep waiting** removes and re-adds the label (two calls, as for Ask again: the label is still present on a timed-out issue, and adding a present label makes no event, so the clock would not restart and the offer would return on every run). A failed removal keeps the old clock, so the offer returns next run; a failed re-add leaves the issue in the incoming queue: both stay visible, and both are reported. Any failed step of a move is reported naming the issue and the state it was left in, with no retry on the user's behalf.
32
+ - **Direct path.** `/trailhead:inbox <n>` on a needs-info issue triages it directly, whatever its state (it is absent from the incoming query), and every move drops the label first.
28
33
 
29
- A rogue issue wearing a `trailhead:*` label it shouldn't (now `trailhead:unverified`) is triaged the same way: **adopt** (a maintainer applies the real labels + `Parent:`) or **reject** (strip the `trailhead:*` labels, leave it a normal inbound issue). Never adopt by trusting a label that was already there; you apply the labels on adoption.
34
+ A rogue issue wearing a `trailhead:*` label it shouldn't (one whose label came from outside the trusted labellers of the Trust rule in `../../_shared/teamwork.md#trust--provenance`, so a label from the bot, an allowlisted login or a write+ collaborator is not rogue; now `trailhead:unverified`) is triaged the same way: **adopt** (a maintainer applies the real labels + `Parent:`) or **reject** (strip the `trailhead:*` labels, leave it a normal inbound issue). Never adopt by trusting a label that was already there; you apply the labels on adoption.
@@ -1,6 +1,6 @@
1
1
  # Auditing closed work and open overlap
2
2
 
3
- `/trailhead:audit [map] [--all]` answers two questions about a repo's tickets: is each closed one **still true against today's code** (it hunts **regressions over time**, not just close-time delivery), and are any open ones duplicates or small enough to land together? Unchanged code is checked cheaply and deterministically, with no agent; the full agent verification is kept for the tickets whose code actually moved. The first run (no marker) covers the whole closed history; later runs keep the marker window. It is **read-only until you confirm**: the verification and the comparison only read, the result is one consolidated report, and every change that follows is a separate proposal applied only on an explicit yes. Nothing is written to GitHub before that.
3
+ `/trailhead:audit [map] [--all]` answers two questions about a repo's tickets: is each closed one **still true against today's code** (it hunts **regressions over time**, not just close-time delivery), and are any open ones duplicates or small enough to land together? A whole-repo run also flags codebase facets whose cited files changed after their stamp (step 7b). Unchanged code is checked cheaply and deterministically, with no agent; the full agent verification is kept for the tickets whose code actually moved. The first run (no marker) covers the whole closed history; later runs keep the marker window. It is **read-only until you confirm**: the verification and the comparison only read, the result is one consolidated report, and every change that follows is a separate proposal applied only on an explicit yes. Nothing is written to GitHub before that.
4
4
 
5
5
  All `gh` mechanics are in `../../_shared/substrate-commands.md`; this protocol only says how to use them. Only the purely process choices (the >20 cost-guard ask, the re-run offer for unverifiable tickets) are advisory, so offer the delegate option on those per `../../_shared/choices.md`. The reopen, duplicate-close and merge proposals reshape the map, so they are **human-owned**: do not offer the delegate option on them. The marker advance stays an explicit yes too.
6
6
 
@@ -52,7 +52,7 @@ Run in this order, so the cost guard counts only what the cheap passes leave ove
52
52
 
53
53
  ## 5. Verify the closed set
54
54
 
55
- Dispatch each agent-bound ticket to a **`trailhead-verify`** agent (see `../../_shared/techniques.md` for dispatch, the `config.models.verify` model override, and the inline fallback), in parallel **batches of 5**, background and polled: reviews never block the session. The verification protocol is `../../_shared/techniques/verify.md`; this audit states its own brief on top of it, and `verify.md` itself is unchanged.
55
+ The audit routes `verify` once per run through `model-route.js` (the **Route every step through model-route.js** paragraph in `../../_shared/techniques.md`) and reuses that route for every batch. Dispatch each agent-bound ticket to a **`trailhead-verify`** agent (see `../../_shared/techniques.md` for dispatch, the `config.models.verify` model override, and the inline fallback), in parallel **batches of 5**, background and polled: reviews never block the session. The verification protocol is `../../_shared/techniques/verify.md`; this audit states its own brief on top of it, and `verify.md` itself is unchanged.
56
56
 
57
57
  **Audit-mode brief**, handed to each agent:
58
58
  - Work goal-backward against the **current HEAD code** (read it with `git show HEAD:<file>`, never the working tree, so every range it returns is a HEAD range) and the ticket's own Question / Outcome / PLAN criteria.
@@ -100,9 +100,23 @@ Findings:
100
100
 
101
101
  Over 60 open tickets in scope, run stage (a) per map plus one cross-map pass over titles and Questions only, and say so.
102
102
 
103
+ ## 7b. Codebase facets
104
+
105
+ A whole-repo run also reports which facets of the `trailhead:codebase` issue are stale. It is repo-scoped, so a `[map]` run skips it, and so does a repo with no `trailhead:codebase` issue. Look the issue up with the fail-closed singleton rule (**Find a singleton issue** in `../../_shared/substrate-commands.md`): more than one open issue means skip the step and name the duplicates in the report.
106
+
107
+ Deterministic, no agent: read the body with `gh issue view <n> --json body -q .body` into a file inside a private `mktemp -d` directory, run `node <templates>/trailhead-audit-walk.js facets --repo <root> --body-file <file>` (same `<templates>` lookup as above), and remove the directory on every path, failure included. The script prints `{head, facets:[{name, sha, status, paths, changed}]}`. For each facet it compares the `_as of <sha>_` stamp with HEAD over the files the facet cites in backticks (those that exist at the stamp), and reports one status:
108
+
109
+ - `current`: none of the cited files changed since the stamp.
110
+ - `stale`: at least one cited file changed, was deleted or renamed since the stamp; `changed` lists them.
111
+ - `unstamped`: the facet carries no stamp yet (it gains one on its next patch).
112
+ - `unresolved`: the stamp is unknown or not an ancestor of HEAD (a shallow clone, a rewritten commit).
113
+ - `no-paths`: no cited file exists at the stamp, so there is nothing to compare.
114
+
115
+ A script failure is one line in the report and never blocks the audit or the marker. The flag is a hint, not a verdict: a facet citing a frequently edited file reads stale after any edit to it. `current` is not proof either: patching one line moves the stamp over the whole facet, so an earlier edit to another file it cites stops being flagged.
116
+
103
117
  ## 8. Report
104
118
 
105
- One consolidated report: counts per class, which must sum to the audited set (each ticket is counted **once by its primary class**, the step 6 precedence, and plain unverifiable only when no unmet claim exists): implemented (by the agent or by the light check, giving the light-check share), **unchanged**, **evolved**, implemented wrong, not implemented, unverifiable, then each evolved ticket by name and number with the evolving ticket(s), then each other non-implemented or unverifiable ticket by name and number with a one-line reason, the tickets **also unverifiable in part** listed separately, **outside the sum**, then the decisions flagged **no build** (step 4), also outside the sum, each by name and number, then the duplicate and groupable findings, and any dirty-file note or shallow-clone note from step 4. Nothing has changed yet.
119
+ One consolidated report: counts per class, which must sum to the audited set (each ticket is counted **once by its primary class**, the step 6 precedence, and plain unverifiable only when no unmet claim exists): implemented (by the agent or by the light check, giving the light-check share), **unchanged**, **evolved**, implemented wrong, not implemented, unverifiable, then each evolved ticket by name and number with the evolving ticket(s), then each other non-implemented or unverifiable ticket by name and number with a one-line reason, the tickets **also unverifiable in part** listed separately, **outside the sum**, then the decisions flagged **no build** (step 4), also outside the sum, each by name and number, then the duplicate and groupable findings, and any dirty-file note or shallow-clone note from step 4. On a whole-repo run, also the **stale facets** of step 7b, each by name with its changed files, and the counts of the other statuses, all outside the class sum. Nothing has changed yet.
106
120
 
107
121
  ## 9. Proposals, each on an explicit yes
108
122
 
@@ -111,6 +125,7 @@ One proposal per finding, shown one at a time, applied only on an explicit yes.
111
125
  - **Implemented wrong** → a new `bug` carrying `Regression of: <ticket>`, via the capture cluster's `bug --of <n>` (a bug in already-closed work is always a new ticket; see the capture cluster's `bug` engine in `../../trailhead-capture/`). Before proposing it for ticket <n>, search exhaustively (`is:issue`, open, or closed after <n>'s `closedAt`) for an existing ticket whose body carries `Regression of:` pointing at <n> (match the number or its URL). If one exists, report <n> as already tracked, naming that ticket, instead of proposing a new bug; this prevents duplicates when a rewound marker re-audits tickets.
112
126
  - **Not implemented** → `gh issue reopen <n>` plus a comment stating the gap (the premature-close branch of new-bug-versus-reopen).
113
127
  - **Evolved** → no proposal: listed in the report only, because a later ticket changed the code on purpose.
128
+ - **Stale facets** → no proposal: listed in the report only (step 7b), because the flag is report-only; the facet is patched at the Resolve of the next ticket that changes it.
114
129
  - **No build** → open the missing build tickets, per the decision engine's **Carry it into build** step (`../../trailhead-work/references/ticket-engines.md`): read the decision's resolution, propose the split (titles, one line of scope each, blockers, any existing open ticket that already covers a piece), and on a yes create them create-then-wire on the decision's map (the whiteboard for a whiteboard decision), each with a `Decided in: <name+link>` line, then post a comment on the decision carrying the `Builds:` line. If the human says the decision builds nothing, post `Builds: none (<why>)` with their reason instead. The ticket split is human-owned: ask it as plain yes/no, never with the delegate option.
115
130
  - **Duplicate** → first **carry the duplicate's wiring onto the kept ticket**, in all three views (the `## Blocked by` line, the native `blocked_by` edge, the `trailhead:blocked` label): each open dependent of the duplicate is re-pointed to the kept ticket unless already wired there, and the union of blockers is built by adding each open blocker of the duplicate that the kept ticket lacks, both **excluding the pair itself** so the kept ticket is never re-pointed onto itself or made to block on itself. Then close the newer one natively as a duplicate of the kept one, `gh issue close <n> --duplicate-of <k>` (this sets the `DUPLICATE` state reason the window filter reads), plus a comment naming the kept ticket. If the duplicate was one of the kept ticket's blockers, that edge is removed (the native edge auto-reflects the close; also drop it from the `## Blocked by` line) instead of transferred, and the kept ticket's `trailhead:blocked` label is re-evaluated only after the duplicate's blockers have been added to it: drop `trailhead:blocked` if that was its last open blocker. Its native sub-issue edge stays: the eager drop is reserved to superseded and out-of-scope closes, and `/trailhead:prune` reclaims closed edges under cap pressure.
116
131
  - **Groupable** → **supersede-and-merge**, the reverse of the split in `../../_shared/teamwork.md`. Open one merged ticket on the same map or whiteboard (create-then-wire). Its blockers are the union of the originals' open blockers **minus the group's own members**, wired in all three views (the `## Blocked by` line, the native `blocked_by` edge, and the `trailhead:blocked` label at creation). Every dependent of an original **outside the group** is re-pointed onto the merged ticket in all three views (body line edited, native edge added to the merged ticket; the original's edge reflects its close on its own). Intra-group edges are dropped, never re-pointed, so the merged ticket never blocks on, or depends on, itself. The merged ticket's body carries a `Merged from: <name+link>, ...` provenance line (mirroring split's `Split from:`). Then close each original as `trailhead:superseded` with the eager edge drop and a closing comment linking the merged ticket.
@@ -9,14 +9,16 @@ argument-hint: "[work|quick|pause|resume|split|auto] [ticket | \"text\" | note]"
9
9
 
10
10
  ## Move into the handed checkout (`--in=`)
11
11
 
12
- The panel starts `work` and `resume` from wherever the session stands and cannot move it, so it appends the pinned checkout to the args as a last word `--in=<token>` (base64url of the checkout root). When the args of `work` or `resume` end with such a word, this section runs first, before the load, the Preflight and anything else; with no such word it does not apply. `quick` never gets one.
12
+ The panel starts `work` and `resume` from wherever the session stands and cannot move it, so it appends the pinned checkout to the args as a last word `--in=<token>` (base64url of the checkout root, plus the repo identity when the panel knows it). When the args of `work` or `resume` end with such a word, this section runs first, before the load, the Preflight and anything else; with no such word it does not apply. `quick` never gets one.
13
13
 
14
14
  1. Drop the `--in=<token>` word from the args: the verb, and its ticket number if any, are what is left.
15
15
  2. Run `node <templates>/trailhead-checkout-in.js --in <token> --cwd "$PWD"`. `<templates>` is the first of `${CLAUDE_PLUGIN_ROOT}/templates/`, `${CLAUDE_CONFIG_DIR:-~/.claude}/trailhead/templates/`, `~/.codex/skills/trailhead/templates/` that contains the script (the same lookup as `trailhead-work-state.js`: try all three in order, skipping the first when `CLAUDE_PLUGIN_ROOT` is unset or empty). It prints one JSON line with a `status`. If the script is missing (none of the three has it: an outdated install), stop and report that the checkout could not be reached and the install needs an update: nothing is claimed, and never move by hand.
16
- 3. `invalid` -> stop and report its `reason` in one line; never move, never look for another checkout.
16
+ 3. `invalid` -> stop and report its `reason` in one line; never move, never look for another checkout by hand.
17
17
  4. `here` -> the session is already in the checkout: no move, carry on with the args from step 1.
18
18
  5. `move` -> run the helper's `cd` field verbatim as its own Bash call (a lone `cd` is what moves the session; never join it to a read or another command, and never edit the quoting). A failed `cd` stops the verb with a report. Then re-run the helper with the new `$PWD` and require `here`; anything else means the session could not move: stop and report that, with nothing claimed.
19
- 6. After a move, say in one result-oriented line which checkout the session is now working in, then continue with **Load first** and, for an explicit ticket number, the Preflight.
19
+ 6. `choose` -> the handed path is gone and several checkouts of the repo match its remote (`candidates`, each with a `kind` of `main` or `linked`). Ask with AskUserQuestion, one option per candidate path (say which is a linked worktree), never pick one by hand. Then run `cd -- '<chosen path>'` (single-quote-escaped like the helper's own `cd` field) as its own Bash call and re-run the helper with the new `$PWD` and the original `--in` token; if it does not answer `here`, stop and report that the session could not move. A failed `cd` stops the verb.
20
+ 7. When the result carries `relocatedFrom` (the checkout was found again by its remote after it moved), say in one result-oriented line that the checkout moved and which one the session now works in; that line replaces the generic one of step 8.
21
+ 8. After a move, say in one result-oriented line which checkout the session is now working in, then continue with **Load first** and, for an explicit ticket number, the Preflight.
20
22
 
21
23
  ## Load first, in order
22
24
 
@@ -55,12 +57,13 @@ The user invokes with a map (URL or number). A ticket is optional: without one,
55
57
  - **Write the session-ticket marker**: at the **working root** (the checkout, worktree, or clone you'll edit in), write `.trailhead/session-ticket` as **one line** `#<n> <ticket title>`. **Keep it gitignored** (add `.trailhead/session-ticket` to that repo's `.gitignore` if absent): it's per-session local state, **never committed**. It lets an external tool show "what am I on" offline (the tracker stays the source of truth), but it is not merely that cheap hint: while it is present, the git commit-msg hook also requires every commit to carry a `Refs: #<n>` trailer matching it, so a stale marker left behind would wrongly demand that trailer on unrelated commits. Remove it at Resolve/handoff (see `../_shared/session-handoff.md`); on **Pause** it may stay (you'll return) or be cleared if you release the claim. The **`.trailhead/active-map`** marker (which map this session works) follows the same rules: gitignored, per-session, never committed; see **Multiple maps on one repo** in `../_shared/multi-map.md`.
56
58
  - **Sync the commit-msg hook**: at the working root, run `node <templates>/trailhead-commit-msg-sync.js --repo <working-root>` (where `<templates>` lives and what each status means: the commit-msg hook paragraph in `../_shared/teamwork.md`). Stay silent when it reports installed or current. On **upgraded**, give the user one result-oriented line: this clone's commit-msg hook was updated to the current rules. On **foreign**, one line that the user's own hook was left untouched and git does not enforce trailhead's rules here. On **skipped**, one line that nothing was set up here (a `core.hooksPath` without a trailhead hook, or a working root that is not a git repo) and git does not enforce trailhead's rules. On a missing script or a node error (missing means none of the three template dirs has it: try them all before concluding that), inspect the hooks dir (`git rev-parse --git-path hooks`) and report per the fallback in that same paragraph of `../_shared/teamwork.md` (trailhead hook present and executable: enforced but freshness unchecked, and only while node is on the PATH git runs with, update the install; present but not executable: git skips it, not enforced; foreign hook: untouched, not enforced; no hook: not enforced, update the install), never a blind "not enforced"; it never blocks the work.
57
59
  - **Backfill the UAT label once per clone**: at the working root, run `node <templates>/trailhead-uat-backfill.js --repo <owner/name> --marker <working-root>/.trailhead/uat-backfill` (same `<templates>` lookup as the commit-msg sync; pass the marker as an absolute path, since the shell may not sit at the working root). It tags any open ticket with a `UAT of:` line with `trailhead:uat` (a pending UAT follow-up opened before the label existed), and writes the `.trailhead/uat-backfill` marker itself only on a clean run, so later work-starts exit `skipped` with no `gh` call. **Keep the marker gitignored** (add `.trailhead/uat-backfill` to that repo's `.gitignore` if absent): per-clone local state, never committed. Stay silent when it tagged nothing or skipped; when it tagged tickets, one result-oriented line saying how many pending UAT follow-ups now carry the label; on `failed` entries or an `error` status, one line saying the backfill did not finish and will retry at the next work-start.
60
+ - **Sweep stale needs-you marks** at every work-start: `node <templates>/trailhead-needs-you.js sweep --repo <owner/name>` drops `trailhead:needs-you` from every closed ticket still carrying it (a ticket closed by hand or in the web UI keeps it otherwise). Stay silent when `cleared` is empty; otherwise one result-oriented line naming the tickets cleaned up; an `error` is one line and never blocks the work.
58
61
  - **When the resolved mode is `worktree` or `clone` (explicit, or `auto` answered yes), set up the isolated workspace NOW, before any file edit or diagnostic probe** (not later at Execute/Fix): **if the repo has submodules (a `.gitmodules`), `isolation: worktree` is escalated to `clone`** (a superproject worktree cannot safely carry submodule state; see `../_shared/teamwork.md`). For `worktree` (no submodules), create/enter this ticket's `git worktree` on a `trailhead/t<n>` branch for the package/path the ticket's `Scope:` names (a submodule-bearing repo would have escalated to `clone` above), e.g. `git worktree add ../<repo>-t<n> -b trailhead/t<n>`; for `clone`, make an independent working copy at `../<repo>-t<n>` (branch `trailhead/t<n>`) by whichever is faster: `git clone` (add `--recurse-submodules` when the repo has submodules) + the install step, or a **folder copy that brings `node_modules`** (and the submodule working trees) to skip the reinstall (then verify the copy's git is independent, `git -C <copy> rev-parse --absolute-git-dir` resolves inside it, and the same inside every submodule, else commits leak back to the original); a clone with submodules is provisioned via `git submodule update --init --recursive` (nested included), each scoped submodule pinned at the superproject's recorded commit. **If this `clone` was reached via `auto`'s work-start ask (answered yes), that ask already served as the clone gate: do not re-ask, proceed to set up the clone. Under an explicit `isolation: clone`, run the gate as follows.** **Because `clone` is heavy (a full clone + an install), gate it, every time, before cloning: ask the user whether they expect to work more than one ticket at once on this machine this session.** If **no**, **skip the clone** and work this ticket in the current checkout (isolation only exists to stop concurrent workspaces from colliding; a lone ticket has nothing to collide with, so the shared-checkout caveats apply and that is fine, and `git:` still decides the branch as usual); if **yes**, clone as above. The gate is **`clone`-only**: `worktree` is cheap, so set it up without asking. Do every subsequent step in whatever workspace you ended up in. This is the whole point of isolation: if you diagnose and edit in the shared checkout "just to start" while a concurrent workspace exists, you are already colliding. Right after creating the workspace (before any install or build step), copy the declared gitignored files into it with `node <templates>/trailhead-isolation-copy.js --from <main-checkout> --to <working-root>`, per **Gitignored files in a new workspace** in `../_shared/teamwork.md`. Set the workspace up silently (the `clone` gate-question is the one exception you voice); it's undone at Resolve when the branch integrates and the worktree/clone is removed. See `../_shared/teamwork.md`.
59
62
  - **Write the work state, last** (the working root is final only now): `node <templates>/trailhead-work-state.js set --repo <working-root> --map <n> --ticket <n> --title "<title>" --step discuss`, and add `.trailhead/work-state.json` to that repo's `.gitignore` if absent. It is updated at every step change and cleared at Resolve; the canonical description, the `<templates>` lookup, and the silent-on-failure rule are the **Work state file** paragraph in `../_shared/teamwork.md`, and the step mapping per type is in `references/ticket-engines.md`.
60
- 3. **Brief the ticket and confirm scope, before the engine runs.** With the ticket claimed, read it and give the user a **brief, result-oriented summary of what resolving it will do** (its goal and the shape of the work, not a plan yet), then **ask whether to proceed or change something as an `AskUserQuestion` menu, never as free text**: **Proceed** (Recommended) first, **Change something** second (the user types the change through the menu's built-in "Other" or in a follow-up message), plus the **delegate** option (this gate is a process choice, see `../_shared/choices.md`). On a host with no structured question tool (Codex), the same options are a plain-text numbered list, per its adapter. This is a HITL confirm gate: on **Proceed**, proceed to the engine. **If the user asks for a change, hold it against the ticket.** A change **consistent** with the ticket is folded in and the engine proceeds. A change that **diverges** from the ticket is surfaced as a mismatch, and the user is **asked, never forced**, whether to (a) work the ticket **as-is**, or (b) open a **separate new ticket** for the divergent request, as an `AskUserQuestion` menu **without the delegate option** (routing a scope change is the human's, per the one-line test in `../_shared/choices.md`). Offer both and decide neither: open the separate ticket **only if the user chooses it** (via **Scope that surfaces while working a ticket**, served by the capture cluster, the same capture-or-split path used for scope that surfaces mid-work), never automatically. **If the user defers the choice rather than settling it here, mark the divergent follow-up pending for the Pending follow-up guard (`../_shared/session-handoff.md`), so it is re-posed at the handoff rather than lost.** **Under an autonomous run (`auto`) this is a process confirm gate**, so it follows the advisory boundary in `references/auto.md`: `auto` proceeds (takes the delegate option) without stopping to brief.
63
+ 3. **Brief the ticket and confirm scope, before the engine runs.** With the ticket claimed, read it and give the user a **brief, result-oriented summary of what resolving it will do** (its goal and the shape of the work, not a plan yet), then **ask whether to proceed or change something as an `AskUserQuestion` menu, never as free text**: **Proceed** (Recommended) first, **Change something** second (the user types the change through the menu's built-in "Other" or in a follow-up message), plus the **delegate** option (this gate is a process choice, see `../_shared/choices.md`). On a host with no structured question tool (Codex), the same options are a plain-text numbered list, per its adapter. This is a HITL confirm gate: on **Proceed**, proceed to the engine. **If the user asks for a change, hold it against the ticket.** A change **consistent** with the ticket is folded in and the engine proceeds. A change that **diverges** from the ticket is surfaced as a mismatch, and the user is **asked, never forced**, whether to (a) work the ticket **as-is**, or (b) open a **separate new ticket** for the divergent request, as an `AskUserQuestion` menu **without the delegate option** (routing a scope change is the human's, per the one-line test in `../_shared/choices.md`). Offer both and decide neither: open the separate ticket **only if the user chooses it** (via **Scope that surfaces while working a ticket**, `../_shared/surfaced-scope.md`, the same capture-or-split path used for scope that surfaces mid-work), never automatically. **If the user defers the choice rather than settling it here, mark the divergent follow-up pending for the Pending follow-up guard (`../_shared/session-handoff.md`), so it is re-posed at the handoff rather than lost.** **Under an autonomous run (`auto`) this is a process confirm gate**, so it follows the advisory boundary in `references/auto.md`: `auto` proceeds (takes the delegate option) without stopping to brief.
61
64
  - **Skip the confirm on a ticket this session already shaped.** Skip the brief menu when, in this same session (before any `/clear`), (a) the agent created this ticket from the user's own request for it, one ticket at a time (a capture `todo`/`bug`/`idea`/`seed`, `ticket`, `quick "<text>"`, or a `split` child the user approved), or (b) a Grilling round on this ticket concluded (`grill <ticket>`): the user just stated or settled its scope, so asking again is redundant. Not a trigger: tickets created in bulk as a side effect (`new`/`adopt` charting, `inbox` triage, follow-ons spawned by a grill on another topic), where no one confirmed this ticket's own scope. Skipping replaces the menu with a **one-line**, result-oriented note (e.g. "working <name> (#n), scope as just agreed"), so the user can still interrupt with a change; a change typed then follows the same consistent/divergent handling as the menu's **Change something**. Still asked: a ticket created or grilled in an earlier session, by someone else, or before a `/clear` (a fresh session has no memory of it), a `resume`d ticket, a ticket only read or mentioned this session. `auto` is unchanged (it already takes the confirm as delegate). The evidence is the session's own context, never inferred from tracker metadata (an issue's author or creation time does not prove this session made it).
62
65
  4. Resolve it with its type's engine in **`references/ticket-engines.md`**, **zoom as needed**: fetch the full body of related/closed tickets on demand. If in doubt on a `decision` ticket, run **Grilling** + **Domain vocabulary**. If in doubt on a `build`, **stop and ask** (see the Discuss step): never auto-grill.
63
- 5. Record the resolution: a comment with the answer (for a `decision`, carrying the `Builds:` line of its engine's **Carry it into build** step, with the build tickets created before the close), `gh issue close`, add the pointer to *Decisions so far* (default path: `node <templates>/trailhead-issue-body.js append --issue <map> --section "Decisions so far" --line "<pointer>"`, which does the fail-safe read-modify-write, the concurrent-edit checks and the secret scan in one call; when the helper is missing, do it by hand per the Fail-safe body rewrites rule in `../_shared/substrate.md`). Then **unblock dependents**: for every ticket this one was blocking, if it was the last open blocker, remove its `trailhead:blocked` label so it graduates onto the frontier.
66
+ 5. Record the resolution: a comment with the answer (for a `decision`, carrying the `Builds:` line of its engine's **Carry it into build** step, with the build tickets created before the close), `gh issue close`, add the pointer to *Decisions so far* (default path: `node <templates>/trailhead-issue-body.js append --issue <map> --section "Decisions so far" --line "<pointer>"`, which does the fail-safe read-modify-write, the concurrent-edit checks and the secret scan in one call; when the helper is missing, do it by hand per the Fail-safe body rewrites rule in `../_shared/substrate.md`). Unblocking is a separate step that does not depend on the map or on `Decisions so far`, and it runs for **every** ticket, map or whiteboard, via `work` or `quick`: **unblock dependents** (default path: `node <templates>/trailhead-unblock.js --issue <n> --repo <owner/name>`; by hand when the helper is missing or errors, see the Resolve step in `references/ticket-engines.md`): for every ticket this one was blocking, if it was the last open blocker, remove its `trailhead:blocked` label so it graduates onto the frontier.
64
67
  6. Add newly-surfaced tickets (create-then-wire, labelling blocked ones `trailhead:blocked`); graduate the fog that became specifiable: re-read **every entry** of *Not yet specified* (not just the ones this ticket names) against the answer, apply the **Ticket or fog** test of charting (`../trailhead-chart/references/charting.md`, Mode 1 step 4) to each, and propose all the entries that pass as **one batch** (title, type, blockers per ticket), created on a yes, so the user never has to dissolve ripe fog one entry per session. For each graduated entry, clear the patch from *Not yet specified* (default path: `node <templates>/trailhead-issue-body.js remove --issue <map> --section "Not yet specified" --match "<unique text of the entry>"`, which drops exactly one list entry (no match is a reported no-op, several matches abort); when the helper is missing, do the fail-safe read-modify-write by hand). If the answer reveals a ticket sits beyond the destination, apply the **out-of-scope vs deferred** test (`../_shared/out-of-scope.md`): if it's really deferred on something outside the map, ask the user to route it to a `seed`/`idea`/`todo`; only if it's truly beyond the destination rule it **out of scope** (label, then **drop its native sub-issue edge** to reclaim a slot, then close: the eager drop in `../_shared/substrate-commands.md`, 404-tolerant, references intact) instead of resolving it. If the decision invalidates other parts of the map, update or delete them.
65
68
  7. **Hand off.** Close the session with the **Session handoff** ritual (`../_shared/session-handoff.md`): confirm the ticket is resolved by name, then the scannable **next-step block with `/clear` first** (never the next command without it), naming the next frontier ticket and giving its number (or, when several mutually independent tickets sit on the frontier, presenting them as a **set to choose from** rather than crowning the first-listed), or `/trailhead:map` if the frontier is empty/ambiguous. This holds in any later follow-up too, not only at the moment of resolution. **If this resolution leaves the map exhausted** (no open tickets and no fog left: the destination is reached), say so; but **before closing it out, if *Out of scope* holds any deferred line** (apply the out-of-scope-vs-deferred tells in `../_shared/out-of-scope.md`), **stop and ask the user what to do with each** (a `seed` gated on its trigger, an `idea`, or a `todo`, in this or another map), so nothing wanted-later is lost when the map goes quiet. Only once the deferred items are routed (or the user says leave them), **ask the user whether to close the map issue** now that the destination is reached: `gh issue close` it on a yes (it stays the record), leave it open on a no; never close a map unprompted. Either way **refresh the dashboard** so an exhausted/closed map drops off (a structural event): `node <templates>/trailhead-dashboard.js` regenerates the whole body from the live tracker in one call (`--dry-run` previews it); when the helper is missing, regenerate it by hand per `../_shared/substrate-commands.md`. The `clear` of the work state (`node <templates>/trailhead-work-state.js clear --repo <working-root>`) also removes the session-ticket marker, so the handoff needs just that one call. Maps aren't pinned, so there's no pin to free; the dashboard, codebase, and conventions stay pinned regardless.
66
69
 
@@ -69,10 +72,10 @@ The user invokes with a map (URL or number). A ticket is optional: without one,
69
72
  A streamlined way to just get one ticket done, without map ceremony. `quick "<text>"` opens a **whiteboard** ticket (a `build`, or a `bug` when the text is clearly a defect) and works it end to end now; `quick <n>` works an **existing** ticket (whiteboard or map) the same way. For `quick <n>`, the **Preflight** above runs first: a closed, out-of-scope, superseded, or nonexistent ticket stops immediately, before config, claim, or isolation setup. Claim it first; still one ticket per session. **Bare `/trailhead:quick`** (no argument, e.g. offered in a Session handoff) asks for the piece to work, like a capture missing its text, then proceeds as `quick "<text>"`.
70
73
 
71
74
  It runs the **full engine** (the `build`/`bug` cycle in `references/ticket-engines.md`: Discuss → Plan → Execute → Verify → Resolve), differing from `work` on these points:
72
- - **Briefs and confirms scope first, exactly as Work the map step 3.** Whether the menu is posed at all follows **Skip the confirm on a ticket this session already shaped** (Work the map step 3). When it is, with the ticket claimed, summarise what resolving it will do and pose the same scope-confirm menu (`AskUserQuestion`: **Proceed** (Recommended), **Change something**, delegate; numbered list on Codex), applying the same consistency rule (a divergent request is offered as work-as-is vs a separate new ticket, as the same no-delegate menu, opened only if the user chooses it, never forced; via **Scope that surfaces while working a ticket**), and, exactly as Work the map step 3, if the user defers that choice rather than settling it here, mark the divergent follow-up pending for the Pending follow-up guard (`../_shared/session-handoff.md`) so it is re-posed at the handoff. Whether the menu is skipped follows **Skip the confirm on a ticket this session already shaped**: a `quick "<text>"` ticket gets the one-line note, no menu; `quick <n>` follows the same rule as `work`.
73
- - **Grill only if needed** (Discuss): never auto-grill; start from Plan and stop to ask only if a blocking ambiguity surfaces, exactly as the `build` Discuss. The user may ask to grill at any time.
74
- - **Never splits.** If the ticket turns out large, work it whole anyway: that is the point of `quick`. Do not `split` it and do not spin children. (New scope that surfaces mid-work is still captured out, never folded in, see **Scope that surfaces while working a ticket**, served by the capture cluster.)
75
- - **No map narrative book-keeping.** A whiteboard ticket has no map, so there is no `Decisions so far` update, no fog to graduate, no frontier re-scan. Everything else holds: atomic commits with `Refs: #<n>`, TDD / Code review / Acceptance testing per the cycle, the resolution comment + `gh issue close`, and the **Session handoff** (`../_shared/session-handoff.md`). **Exception, `quick <n>` on a *map* ticket: dependent-unblocking still runs** (remove `trailhead:blocked` from any dependent whose last blocker just closed, **Work the map** step 5 above); skipping it would strand those dependents off the frontier for good. Only the *narrative* book-keeping above and the split are skipped, so prefer `work <n>` for a map ticket that has dependents or belongs in the map record.
75
+ - **Briefs and confirms scope first, exactly as Work the map step 3.** Whether the menu is posed at all follows **Skip the confirm on a ticket this session already shaped** (Work the map step 3). When it is, with the ticket claimed, summarise what resolving it will do and pose the same scope-confirm menu (`AskUserQuestion`: **Proceed** (Recommended), **Change something**, delegate; numbered list on Codex), applying the same consistency rule (a divergent request is offered as work-as-is vs a separate new ticket, as the same no-delegate menu, opened only if the user chooses it, never forced; via **Scope that surfaces while working a ticket**, `../_shared/surfaced-scope.md`), and, exactly as Work the map step 3, if the user defers that choice rather than settling it here, mark the divergent follow-up pending for the Pending follow-up guard (`../_shared/session-handoff.md`) so it is re-posed at the handoff. Whether the menu is skipped follows **Skip the confirm on a ticket this session already shaped**: a `quick "<text>"` ticket gets the one-line note, no menu; `quick <n>` follows the same rule as `work`.
76
+ - **Grill only if needed** (Discuss): never auto-grill; start from Plan and stop to ask only if a blocking ambiguity surfaces, exactly as the `build` Discuss (first run the routing step at the top of `../_shared/techniques/grilling.md`, then the rounds as **Who runs a round** there says). The user may ask to grill at any time.
77
+ - **Never splits.** If the ticket turns out large, work it whole anyway: that is the point of `quick`. Do not `split` it and do not spin children. (New scope that surfaces mid-work is still captured out, never folded in, see **Scope that surfaces while working a ticket** (`../_shared/surfaced-scope.md`).)
78
+ - **No map narrative book-keeping.** A whiteboard ticket has no map, so there is no `Decisions so far` update, no fog to graduate, no frontier re-scan. Everything else holds: atomic commits with `Refs: #<n>`, TDD / Code review / Acceptance testing per the cycle, the resolution comment + `gh issue close`, unblocking dependents (`trailhead-unblock.js`, map or whiteboard ticket alike), and the **Session handoff** (`../_shared/session-handoff.md`). Dependent-unblocking always runs (see **Work the map** step 5 above): skipping it would strand those dependents off the frontier for good. Only the *narrative* book-keeping above and the split are skipped, so prefer `work <n>` for a map ticket that has dependents or belongs in the map record.
76
79
  - **Same isolation setup as `work`.** A claimed ticket that carries `trailhead:paused` drops it at the claim, exactly as in **Work the map** step 2. It clears `trailhead:needs-you` the same way (`node <templates>/trailhead-needs-you.js clear --issue <n> --repo <owner/name>`); a failed `clear` is reported to the user in one line but never blocks the claim. `quick` **writes the `.trailhead/session-ticket` marker at the working root in every isolation mode** (exactly as **Work the map** step 2, so tooling like the statusline shows what's in play; the active-map marker follows the same rule as `work`: a named ticket's map becomes the active map, a whiteboard ticket leaves it alone, see **Active map** in `../_shared/multi-map.md`). Under `auto` it runs the same work-start concurrency ask (with the same `check`/`record` answer reuse) **before any edit** and resolves to current-checkout / worktree / clone by the same rule; under an explicit `isolation: worktree`/`clone` it sets up the ticket's `trailhead/t<n>` workspace before any edit (including the submodule-forces-`clone` escalation and submodule provisioning) and re-checks the claim; under `isolation: none` (or `auto` answered no), the scope-free check applies too. Under `worktree`/`clone` it also runs the same gitignored-file copy (`trailhead-isolation-copy.js --from <main-checkout> --to <working-root>`) right after creating the workspace. `quick` also runs the same commit-msg hook sync at the working root, exactly as **Work the map** step 2. It also writes the work state last, exactly as **Work the map** step 2, with `--map none` for a whiteboard ticket.
77
80
  - **Refreshes the dashboard at the handoff, not mid-work.** A `quick "<text>"` whiteboard ticket is born and resolved in the **same** session, so its dashboard refresh rides with the session's end, not with ticket creation: **do not refresh right after the ticket exists** (that lands a structural pinned-issue write ahead of the work `quick` exists to just-get-done, and only duplicates the resolution refresh a moment later). Refresh at **Resolve** (the handoff), as a **full regeneration from the live tracker** (`node <templates>/trailhead-dashboard.js`, never a partial patch that leaves other sections knowingly stale), so the whiteboard ticket lands on the dashboard as the session closes. **If the session pauses before Resolve** (`/trailhead:pause`), refresh there instead, so a deliberately-unfinished quick still shows (the read-only renders never rewrite the dashboard body, so nothing else would surface it). `quick <n>` on a *map* ticket does **not** refresh (map ticket progress shows via the native bar). See **The dashboard** and its freshness rule in `../_shared/substrate.md`.
78
81
  - **Always size-triages, independent of `config.effort`.** `quick` is the home of the cost-aware path: reaching for `quick` is itself the signal the piece is small, so it runs the **same size triage and ask-to-skip offer defined canonically under effort in `../_shared/configuration-reference.md`** (the same one the `build`/`bug` engine runs, `references/ticket-engines.md`) **every time, at the start before planning, whatever `config.effort` is set to**. `quick` does not wait for `effort: lean`, and an explicit `effort: standard` does not suppress it: `effort` governs `work` / the map cycle, not `quick`. On a trivial/small ticket it surfaces the one consolidated offer (the plan pass, TDD, cross-AI review, the full code review, the goal-backward verify) and goes lean **only on an explicit yes**; on a large ticket there is no offer and the full cycle runs. The correctness spine (atomic `Refs:` commits, tests/criterion, post-commit self-check, Resolve) is never in the offer, whatever the size or the answer.
@@ -58,7 +58,7 @@ The run ends only when the queue finds **nothing auto-workable** (the auto-worka
58
58
  ## Parallel slots
59
59
  With more than one slot the run keeps up to that many tickets in flight at once, each in its own workspace on a `trailhead/t<n>` branch. The run stays **one orchestrating session**: the only one that talks to the tracker for the run, claims, and fills slots.
60
60
 
61
- - **Background dispatch, never inline.** Every engine step that has a technique agent (plan, execute, fix, debug, code review, goal-backward verify) is dispatched as a **background subagent** with the slot's **absolute workspace path as its working root**, even when its model equals the session model: with more than one slot nothing runs inline, or one slot would freeze the others. The orchestrator advances whichever slot's step finished: that slot's tracker moves (comments, labels, close), its preflight and its acceptance steps, then it dispatches the slot's next step.
61
+ - **Background dispatch, never inline.** Every engine step that has a technique agent (plan, execute, fix, debug, code review, goal-backward verify) is dispatched as a **background subagent** with the slot's **absolute workspace path as its working root**, even when its model equals the session model: with more than one slot nothing runs inline, or one slot would freeze the others. The router still runs per step (`model-route.js <key> --session <your full model id> <project root>`, see **Route every step through model-route.js** in `../../_shared/techniques.md`): on `route: dispatch` spawn its `agent` with `alias` as usual; on `route: inline` spawn the same agent with no model override, so it inherits the session model, which is the resolved one. The orchestrator advances whichever slot's step finished: that slot's tracker moves (comments, labels, close), its preflight and its acceptance steps, then it dispatches the slot's next step.
62
62
  - **Filling a free slot.** A slot frees when its ticket resolves, is parked needs-you, or is paused by the failure budget. The orchestrator then runs `queue --scope <scope> --skip <csv>` with the run's skip list **plus every in-flight ticket**, and takes `next`. Tickets of different maps can therefore run together while oldest-first holds. Under the plan-time coupling check (advisory), worktrees isolate the tickets and a conflict surfaces at integration, not before.
63
63
  - **Opening a slot.** At claim, capture the trunk SHA (the local trunk HEAD at that moment), then run `node <templates>/trailhead-auto-slot.js open --repo <main-checkout> --ticket <n> --base <sha>`. The workspace is `<parent of the main checkout>/<repo>-t<n>` on `trailhead/t<n>`, created at exactly `--base`. The output carries a `kind` (`worktree` or `clone`). The status is `created`, `reentered` (the branch already has a workspace: a worktree, or an existing clone at the slot path; a `reentered` clone is used as is, nothing is cloned over it) or `reattached` (the branch existed without a worktree). `error` is a helper error for that ticket: the slot path exists and is not a checkout of the branch, the base does not resolve, or the branch's worktree is a stale worktree (git still lists it but its directory was deleted by hand; the reason names it and says to run `git worktree prune`, which the helper never does itself). The ticket is not worked: park it needs-you with `trailhead-needs-you.js park` naming the reason, and it joins the skip list and the run-end summary. `list` reports a stale worktree as a warning, not as a slot. On `clone-required` (a superrepo, and only when the slot path does not exist yet) nothing was created: set the clone up at the same sibling path per the `clone` setup in `../../_shared/teamwork.md` (`git clone --recurse-submodules`, or the folder copy, git-independence checked), then `git -C <clone> checkout -b trailhead/t<n> <base>` and `git -C <clone> submodule update --init --recursive`, so a clone slot carries the same path, branch and base as a worktree slot (and `list` finds it).
64
64
  - **Per-workspace state.** Right after `open`, write the slot's work state: `env -u CLAUDE_CODE_SESSION_ID node <templates>/trailhead-work-state.js set --repo <workspace> --map <map|none> ... --auto true --auto-done <n>`, and the `.trailhead/session-ticket` marker at the workspace root; only then the `trailhead-isolation-copy.js` copy and the commit-msg sync (so a crash after setup still leaves the `auto: true` resume signal; the gap between the claim and `open` stays the known limit). The commit-msg hook (shared `.git/hooks`) reads the marker of the workspace it commits in. **The slot work states are sessionless on every write**: every `set` on a slot workspace (the first one, each per-step update with `--auto true --auto-done <n>`, and the run-end reset) uses the same `env -u CLAUDE_CODE_SESSION_ID` form (`CLAUDE_CODE_SESSION_ID` unset for that call), because one session-bearing write would let the mod adopt that slot as the ticket in play: the mod's prompt hint adopts only a state naming this session, so several slot states of one session would be ambiguous and never pick one slot as "the ticket in play"; crash detection keys on `auto: true`, not on the session.
@@ -79,6 +79,8 @@ Every path above that hands a ticket to the human parks it **at once** with `nod
79
79
 
80
80
  A needs-you ticket is a mark for autonomous runs only: it stays on the frontier and the map for the human, and `/trailhead:work` still offers it. The queue skips it, and a re-invocation of `/trailhead:auto` does not reconsider it; it comes back to a run only after the human clears the label (a `work`/`quick` claim clears it, or `gh issue edit <n> --remove-label trailhead:needs-you`). A helper error from `park` is reported in chat and the ticket still joins the skip list for this run; never read it as a success.
81
81
 
82
+ A closed ticket never keeps the mark: every Resolve runs `trailhead-needs-you.js clear` right after the close, and the run starts with `node <templates>/trailhead-needs-you.js sweep --repo <owner/name>` (before the queue is read), which drops the label from every closed ticket still carrying it, however it was closed. A sweep error is reported in one line and never stops the run.
83
+
82
84
  ## Per-ticket failure budget (a ticket that will not verify)
83
85
  On the green path a ticket resolves and the loop continues. On a **Verify failure**, auto does not hand back at the would-be checkpoint. Instead it **extends past the engine's normal bounded fix-and-re-review rounds**, taking the advisory "which fix" sub-choice as **delegate** (the accept / resolve-anyway decision still stays the human's, so at true exhaustion it does not silently resolve a red ticket). The per-ticket **failure budget** is: a **no-progress rule** (stop iterating once a round makes no measurable progress) **plus a hardcoded ceiling of 3 extra rounds** (tunable only in code; there is no config surface). At budget exhaustion, still red, auto **pauses the stuck ticket keeping its claim** (a `PAUSED` checkpoint) and **skips to the next frontier ticket** (skip-and-continue, not stop-the-run). A single stuck ticket never ends the run; its dependents stay blocked naturally. Skipped / stuck tickets are reported at run end. The accepted cost is stranded dependents and accumulated paused tickets.
84
86
 
@@ -14,6 +14,8 @@ The orchestrator passes: the ticket number `<n>`, the PLAN (build) or the confir
14
14
 
15
15
  **TDD per the `config.tdd` value passed.** `seams`: test-first at the seams the PLAN names; `on`: test-first throughout; `off`: no test-first requirement. For a fix, where sensible, a test that fails before and passes after.
16
16
 
17
+ **Test runs.** In Execute and Fix, run only the focused tests (those on the files you touched or that exercise them), never the whole suite: the one full run belongs to Verify. When `testing.targeted` is set, run them through `node <templates>/trailhead-test-run.js run --repo <working-root> --config-root <project root> --kind targeted --files <a,b>`, which queues behind other heavy runs on the machine; otherwise use a scoped invocation of the project's own runner (a file or pattern argument) that demonstrably does not run everything. When no scoped invocation exists, write `focused testing unavailable` in `tests:` and run none. Put the helper's JSON (`source`, `tree`, exit) in the `tests:` line of your report; `status: unconfigured` means run the project's own focused command as usual. The single full run, its record and the cache reuse apply only when `testing.full` is set (see Verify in the engine); the focused-tests rule here holds either way.
18
+
17
19
  ## New dependencies
18
20
  **Vet before installing any new dependency** (slopsquatting barrier): name and typosquat similarity to popular packages, age / first-seen date, downloads (counts), repository metadata present and matching. On a host with a hook bus the `trailhead-install-guard` `PreToolUse` hook blocks a named public-registry install (npm / PyPI / crates.io / Go / RubyGems) until it is vetted.
19
21
  - **Clean result, or the orchestrator passed that exact package as user-approved:** install by re-running the exact install command prefixed with `TRAILHEAD_VERIFIED_INSTALL=1`.