@marcomigozzi/trailhead 0.16.1 → 0.17.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 (76) hide show
  1. package/README.it.md +57 -10
  2. package/README.md +57 -10
  3. package/bin/capture-action.test.js +167 -0
  4. package/bin/capture-docs.test.js +32 -0
  5. package/bin/engine-route-docs.test.js +148 -0
  6. package/bin/grill-model-docs.test.js +155 -0
  7. package/bin/grilling-holistic-review-docs.test.js +7 -3
  8. package/bin/lib/codex-projection.js +22 -13
  9. package/bin/lib/codex-projection.test.js +58 -17
  10. package/bin/lib/host-descriptor.js +1 -1
  11. package/bin/lib/host-descriptor.test.js +6 -0
  12. package/bin/report-status-docs.test.js +10 -2
  13. package/bin/session-trust-docs.test.js +37 -0
  14. package/bin/surfaced-scope-docs.test.js +57 -0
  15. package/bin/trailhead.js +7 -5
  16. package/bin/trailhead.test.js +53 -11
  17. package/bin/unblock-docs.test.js +56 -0
  18. package/package.json +1 -1
  19. package/plugins/trailhead/.claude-plugin/plugin.json +1 -1
  20. package/plugins/trailhead/agents/trailhead-codebase-map.md +1 -1
  21. package/plugins/trailhead/agents/trailhead-grill.md +41 -0
  22. package/plugins/trailhead/hooks/lib/capture.test.js +439 -0
  23. package/plugins/trailhead/hooks/lib/checkout-find.test.js +195 -0
  24. package/plugins/trailhead/hooks/lib/checkout-in.test.js +83 -7
  25. package/plugins/trailhead/hooks/lib/dashboard.test.js +19 -0
  26. package/plugins/trailhead/hooks/lib/gh-auth-preflight.js +89 -0
  27. package/plugins/trailhead/hooks/lib/gh-auth-preflight.test.js +132 -0
  28. package/plugins/trailhead/hooks/lib/inherit-session.js +17 -0
  29. package/plugins/trailhead/hooks/lib/issue-body.test.js +84 -0
  30. package/plugins/trailhead/hooks/lib/model-defaults-review.js +22 -1
  31. package/plugins/trailhead/hooks/lib/model-defaults-review.test.js +64 -1
  32. package/plugins/trailhead/hooks/lib/model-route.js +207 -0
  33. package/plugins/trailhead/hooks/lib/model-route.test.js +231 -0
  34. package/plugins/trailhead/hooks/lib/report-status.test.js +56 -1
  35. package/plugins/trailhead/hooks/mod/README.md +16 -7
  36. package/plugins/trailhead/hooks/mod/map-pane.tsx +72 -16
  37. package/plugins/trailhead/hooks/mod/my-tickets.ts +76 -0
  38. package/plugins/trailhead/hooks/mod/repo-pin.ts +8 -5
  39. package/plugins/trailhead/hooks/trailhead-check-update.js +17 -1
  40. package/plugins/trailhead/hooks/trailhead-check-update.test.js +60 -0
  41. package/plugins/trailhead/skills/_shared/capture-core.md +118 -0
  42. package/plugins/trailhead/skills/_shared/choices.md +2 -2
  43. package/plugins/trailhead/skills/_shared/configuration-reference.md +18 -16
  44. package/plugins/trailhead/skills/_shared/configuration.md +1 -1
  45. package/plugins/trailhead/skills/_shared/session-handoff.md +1 -1
  46. package/plugins/trailhead/skills/_shared/substrate-commands.md +8 -4
  47. package/plugins/trailhead/skills/_shared/substrate.md +1 -1
  48. package/plugins/trailhead/skills/_shared/surfaced-scope.md +23 -0
  49. package/plugins/trailhead/skills/_shared/teamwork.md +6 -2
  50. package/plugins/trailhead/skills/_shared/techniques/acceptance-testing.md +1 -1
  51. package/plugins/trailhead/skills/_shared/techniques/code-review.md +5 -2
  52. package/plugins/trailhead/skills/_shared/techniques/codebase-map.md +4 -1
  53. package/plugins/trailhead/skills/_shared/techniques/debug.md +4 -1
  54. package/plugins/trailhead/skills/_shared/techniques/grilling.md +26 -1
  55. package/plugins/trailhead/skills/_shared/techniques/research.md +4 -1
  56. package/plugins/trailhead/skills/_shared/techniques/verify.md +3 -1
  57. package/plugins/trailhead/skills/_shared/techniques.md +6 -4
  58. package/plugins/trailhead/skills/trailhead-capture/SKILL.md +2 -4
  59. package/plugins/trailhead/skills/trailhead-capture/references/capture.md +29 -4
  60. package/plugins/trailhead/skills/trailhead-chart/SKILL.md +1 -1
  61. package/plugins/trailhead/skills/trailhead-chart/references/charting.md +4 -4
  62. package/plugins/trailhead/skills/trailhead-chart/references/inbox.md +3 -3
  63. package/plugins/trailhead/skills/trailhead-manage/references/auditing.md +1 -1
  64. package/plugins/trailhead/skills/trailhead-work/SKILL.md +11 -9
  65. package/plugins/trailhead/skills/trailhead-work/references/auto.md +1 -1
  66. package/plugins/trailhead/skills/trailhead-work/references/ticket-engines.md +10 -10
  67. package/plugins/trailhead/templates/trailhead-capture.js +418 -0
  68. package/plugins/trailhead/templates/trailhead-checkout-find.js +256 -0
  69. package/plugins/trailhead/templates/trailhead-checkout-in.js +76 -20
  70. package/plugins/trailhead/templates/trailhead-dashboard.js +15 -2
  71. package/plugins/trailhead/templates/trailhead-issue-body.js +69 -6
  72. package/plugins/trailhead/templates/trailhead-report-status.js +57 -9
  73. package/plugins/trailhead/tests/map-pane-ui.test.ts +122 -37
  74. package/plugins/trailhead/tests/my-tickets.test.ts +66 -0
  75. package/plugins/trailhead/tests/repo-pin.test.ts +20 -5
  76. package/plugins/trailhead/types/index.d.ts +7 -0
@@ -1,12 +1,12 @@
1
1
  # Capture: the verbs in detail
2
2
 
3
- Capture without breaking flow, but with the **tracker** as destination, not scattered files. The verbs are named in this cluster's `../SKILL.md`; here is the detail of each. One capture = one action + one confirmation line; it resolves nothing.
3
+ Capture without breaking flow, but with the **tracker** as destination, not scattered files. Here is the detail of each verb. One capture = one action + one confirmation line; it resolves nothing.
4
4
 
5
- **Confirmation line.** Confirm the capture by the ticket's name and its number, both visible (e.g. *Add login rate limit* (#45), never the name alone nor a bare `#number`), and where it landed. **If you point at working it later, lead with `/clear`**: the confirmation is a place you point at the next ticket, so the [Session handoff](../../_shared/session-handoff.md)'s `/clear`-first rule applies: a compact, future-conditional one-liner is fine (a capture is not a resolution, so no full labelled block), along the lines of *"to work it later: `/clear`, then `/trailhead:work 45`; or, for the lighter path, `/trailhead:quick 45`"* (in the user's conversation language). Never a bare `/trailhead:work 45` with no `/clear`, and (the same post-`/clear` invariant the [Session handoff](../../_shared/session-handoff.md) states) **never place after `/clear` an action the agent must perform that depends on context `/clear` erases**: a capture confirmation points only at user-runnable commands, which satisfies this by construction, so keep it that way. **Whenever the confirmation does point at working the ticket (a `seed` parked on its trigger, or a fog `note`/`idea` that is not a ticket yet, is the case where it does not), anchor the next step to `/trailhead:work <n>`, always:** it is the recommendation (the full engine, which can split and keeps the map record), and the confirmation names it first. `/trailhead:quick <n>` is the **lighter alternative** (a single alternative, not two): it works that same ticket whole right now, always size-triaged so on a small change it offers to skip the heavy steps (plan pass, TDD, full code review, goal-backward verify) on an explicit yes, grills only if needed, skips the map narrative book-keeping, and never splits; a map ticket stays on its map, so never call it "off the map". **Describe it by that purpose (the lighter, faster path), never as merely "without splitting"**: not splitting is a side effect, not what `quick` is for. It is offered *alongside* `work`, **never in its place** and **never rendered twice on one line**. This holds for a **whiteboard** capture too, where both apply (`quick` is the no-split variant of `work`): the anchor stays `/trailhead:work <n>` for consistency. Same spirit as chart's codified close (#147). Render the command in this host's command forms (the **Host command forms** rule in `../../_shared/load-first.md`).
5
+ **Confirmation line.** Confirm the capture by the ticket's name and its number, both visible (e.g. *Add login rate limit* (#45), never the name alone nor a bare `#number`), and where it landed. **If you point at working it later, lead with `/clear`**: the confirmation is a place you point at the next ticket, so the `/clear`-first rule in `capture-core.md` applies: a compact, future-conditional one-liner is fine (a capture is not a resolution, so no full labelled block), along the lines of *"to work it later: `/clear`, then `/trailhead:work 45`; or, for the lighter path, `/trailhead:quick 45`"* (in the user's conversation language). Never a bare `/trailhead:work 45` with no `/clear`, and (the same post-`/clear` invariant: nothing the agent must do after a `/clear`) **never place after `/clear` an action the agent must perform that depends on context `/clear` erases**: a capture confirmation points only at user-runnable commands, which satisfies this by construction, so keep it that way. **Whenever the confirmation does point at working the ticket (a `seed` parked on its trigger, or a fog `note`/`idea` that is not a ticket yet, is the case where it does not), anchor the next step to `/trailhead:work <n>`, always:** it is the recommendation (the full engine, which can split and keeps the map record), and the confirmation names it first. `/trailhead:quick <n>` is the **lighter alternative** (a single alternative, not two): it works that same ticket whole right now, always size-triaged so on a small change it offers to skip the heavy steps (plan pass, TDD, full code review, goal-backward verify) on an explicit yes, grills only if needed, skips the map narrative book-keeping, and never splits; a map ticket stays on its map, so never call it "off the map". **Describe it by that purpose (the lighter, faster path), never as merely "without splitting"**: not splitting is a side effect, not what `quick` is for. It is offered *alongside* `work`, **never in its place** and **never rendered twice on one line**. This holds for a **whiteboard** capture too, where both apply (`quick` is the no-split variant of `work`): the anchor stays `/trailhead:work <n>` for consistency. Same spirit as chart's codified close (#147). Render the command in this host's command forms (the host command forms rule in `capture-core.md`).
6
6
 
7
- **Language (do not skip):** the captured ticket (title + body) is written in `config.ticket.language` (default `en`), **regardless of the language you are chatting in**. A capture fired in Italian still lands as an English ticket. This is the easiest place to slip, because a capture echoes the user's one-line phrasing; translate it, do not mirror it. See the *Ticket language* rule in `../../_shared/ticket-language.md`.
7
+ **Language (do not skip):** the captured ticket (title + body) is written in `config.ticket.language` (default `en`), **regardless of the language you are chatting in**. A capture fired in Italian still lands as an English ticket. This is the easiest place to slip, because a capture echoes the user's one-line phrasing; translate it, do not mirror it. See the ticket language rule in `capture-core.md`.
8
8
 
9
- **Map or whiteboard:** a capture that produces a **ticket** (`todo`, `bug`, `seed`, and `idea` when it is sharp enough) must land on a **map** or on the **whiteboard** (map-less). **When a map is open, ask the user which** before creating it (active map vs whiteboard; several maps open -> the ask also picks the map). **Map eligibility is "is the map open", not "is it still in progress".** An **exhausted** map (destination reached) that is **still open** stays a valid capture target: a capture thematically tied to it is offered that map on the which-map ask, never silently dropped to the whiteboard just because the destination was reached (that would sever the thematic link). The exhausted state is a reason to *offer to close the map* (the dashboard's `exhausted · closeable` flag, see the dashboard freshness rule in `../../_shared/substrate.md`), never a reason to drop it as a capture target. Only a **closed** map is no longer eligible: then the whiteboard is the right fallback. Whiteboard tickets get the `trailhead:whiteboard` label and **no** map label or `Parent:`; map tickets get the map's `trailhead:map-<n>` label + native sub-issue edge. **No map open at all -> whiteboard, no ask** (nowhere else to go). The ask is also skipped for a `bug --of` whose origin resolves to an open map or the whiteboard (see the `bug` bullet in the capture forms). `note` and a non-sharp `idea` stay map-fog and skip this; with no open map they fall back to the global notes location (`$HOME/.claude/notes/`), or a now-sharp `idea` becomes a whiteboard ticket. **When a capture creates a whiteboard ticket, refresh the pinned dashboard** afterwards (a structural event: the whiteboard surface the dashboard indexes grew, and a whiteboard ticket has no native progress bar, so the dashboard is the only place it shows); a capture landing on a *map* does not (native sub-issue progress bars track map tickets). See *The whiteboard* and the dashboard freshness rule in `../../_shared/substrate.md`.
9
+ **Map or whiteboard:** a capture that produces a **ticket** (`todo`, `bug`, `seed`, and `idea` when it is sharp enough) must land on a **map** or on the **whiteboard** (map-less). **When a map is open, ask the user which** before creating it (active map vs whiteboard; several maps open -> the ask also picks the map). **Map eligibility is "is the map open", not "is it still in progress".** An **exhausted** map (destination reached) that is **still open** stays a valid capture target: a capture thematically tied to it is offered that map on the which-map ask, never silently dropped to the whiteboard just because the destination was reached (that would sever the thematic link). The exhausted state is a reason to *offer to close the map* (the dashboard's `exhausted · closeable` flag, see the dashboard freshness rule: a structural event refreshes it, a map ticket's resolve does not), never a reason to drop it as a capture target. Only a **closed** map is no longer eligible: then the whiteboard is the right fallback. Whiteboard tickets get the `trailhead:whiteboard` label and **no** map label or `Parent:`; map tickets get the map's `trailhead:map-<n>` label + native sub-issue edge. **No map open at all -> whiteboard, no ask** (nowhere else to go). **A capture fired mid-ticket** (scope that surfaced while working another ticket) follows this routing instead: the current map when the new work serves its destination and is not a line its *Out of scope* rules out, or another open map when the topic plainly is that map's (read the maps' titles and destinations, not their tickets), no ask; otherwise the which-map ask above, listing every open map by name and number (the current one first) and then the whiteboard; never the whiteboard silently while a map is open. Under an autonomous run a case that needs the ask is a human decision: record it as a pending follow-up, never guess. The ticket carries a `Surfaced from: <ticket name+link>` line, and you say in one line what you opened and where it landed. The ask is also skipped for a `bug --of` whose origin resolves to an open map or the whiteboard (see the `bug` bullet in the capture forms). `note` and a non-sharp `idea` stay map-fog and skip this; with no open map they fall back to the global notes location (`$HOME/.claude/notes/`), or a now-sharp `idea` becomes a whiteboard ticket. **When a capture creates a whiteboard ticket, refresh the pinned dashboard** afterwards with `node <templates>/trailhead-dashboard.js --update-only` (it never opens a dashboard of its own, see `capture-core.md`) (a structural event: the whiteboard surface the dashboard indexes grew, and a whiteboard ticket has no native progress bar, so the dashboard is the only place it shows); a capture landing on a *map* does not (native sub-issue progress bars track map tickets). The whiteboard has no native progress bar, which is why its births refresh the dashboard.
10
10
 
11
11
  - **`note`** → a verbatim line in `## Not yet specified` (outside a project → `$HOME/.claude/notes/`). No questions, no rewriting.
12
12
  - **`idea`** → fog by default in `## Not yet specified`; becomes a ticket only if already phrasable as a sharp question. An idea in the fog is **not executable**: to work it, it must first **graduate** into a ticket (which happens when the frontier reaches it, or when you can now phrase it sharply). A `todo`, by contrast, is born a `build` ticket and is workable immediately.
@@ -24,3 +24,28 @@ Capture without breaking flow, but with the **tracker** as destination, not scat
24
24
  | **todo** | a frontier ticket | **now** | *I'm doing it* |
25
25
 
26
26
  Discriminator: not even work → `note`; maybe, once it's clear → `idea`; yes, when X happens → `seed`; yes, now → `todo`. The close pairs: `note` vs `idea`: raw text vs a work-candidate meant to graduate; `idea` vs `seed`: waiting on *clarity* vs waiting on a *condition*; `seed` vs `todo`: committed *later* vs committed *now*.
27
+
28
+ ## Scripted capture for automations
29
+
30
+ For a run with no chat session (CI, cron, a bot), capture through the helper instead of hand-building labels: `node <templates>/trailhead-capture.js --type bug|todo|idea|seed --title <t> (--body <b> | --body-file <path|->) --repo <owner/name> [--map <n> | --of <n>] [--trigger <text>] [--unverified] [--dedupe-key <key>] [--no-dashboard]`. `--trigger` is required for `seed` and rejected otherwise; `--of` is only for `bug` and excludes `--map`; `--unverified` is rejected for `idea` (fog is already off the frontier); `--dedupe-key` matches `^[A-Za-z0-9._:-]{1,100}$`. Nothing is created when a pre-check fails.
31
+
32
+ For GitHub workflows, the composite Action `actions/capture` (`uses: ToRvaLDz/trailhead/actions/capture@<tag>`) is the ready-made wrapper: it maps its inputs 1:1 to these flags, runs this helper from the action checkout and exposes `issue`, `url` and `status` as outputs. It adds no logic of its own and needs `permissions: issues: write`.
33
+
34
+ **Output and exit codes.** One JSON line on stdout, always:
35
+ - `{"status":"created",...}`, exit 0: `issue`, `url`, `type`, `destination` (`map-<n>` or `whiteboard`; `none` for an `idea` without `--map`), `labels`, `trust` (`ok`, `allowlisted` or `actions-token`), `edge` (map tickets), `dashboard`, `warnings`. Printed only once the labels are read back and the trust is verified.
36
+ - `{"status":"existing",...}`, exit 0: the dedupe key matched an open issue, nothing was created.
37
+ - `{"status":"partial",...}`, exit 3: the issue is created and labelled but the native sub-issue edge failed after one retry (for example the 100 sub-issue cap). The `trailhead:map-<n>` label keeps it on the map frontier; the warning points at the prune command (`/trailhead:prune`). The exit is non-zero so a caller checking only the status never mistakes it for success.
38
+ - `{"status":"error","reason":...}`, exit 1 (`"trust":"insufficient"` on a trust failure, `issue` and `url` when an issue was created before the failure).
39
+ - `{"status":"usage","reason":...}`, exit 2, the message also on stderr.
40
+
41
+ **Trust.** The identity running the helper must be one the label guard Action never strips: write, maintain or admin access, or a login in the repository's own `TRAILHEAD_LABEL_ALLOWLIST` variable (the helper never consults its local environment). An identity `gh api user` cannot read (an Actions `GITHUB_TOKEN` or a GitHub App token) is verified after the create from the server-side record of who applied the labels: `github-actions[bot]` (its events trigger no workflow), an allowlisted or a write actor pass; anyone else, or unreadable events, exits 1 with `trust: insufficient` and no edge, leaving a plain inbox issue.
42
+
43
+ **Shapes.** `bug` is `trailhead:ticket` + `trailhead:bug`, `todo` is `trailhead:ticket` + `trailhead:build`, `seed` adds `trailhead:seed` + `trailhead:blocked` and a `Trigger:` line with a `## Blocked by` section. `--map <n>` adds `trailhead:map-<n>`, the `Parent:` first line and the native edge; otherwise `trailhead:whiteboard`. `--unverified` adds `trailhead:unverified`. `--of <n>` inherits the origin's map or the whiteboard (else exit 1, `origin destination unresolved`) and adds a `Regression of:` line.
44
+
45
+ **`idea` is parked fog, not a ticket.** It becomes a `trailhead:fog` issue (off the frontier, a clarification space) with no type label, no map label, no edge and no dashboard refresh; with `--map` its body opens with a `Parent:` line. It shows in the inbox's parked-fog list and the map render's fog count, but not in the map body's own `Not yet specified` list.
46
+
47
+ **Dedupe.** With `--dedupe-key` the body carries `<!-- trailhead-dedupe: <key> -->` and a rerun returns `existing`. The search is the fast path; when it returns its 1000-result cap the helper scans every open issue instead. The one residual is search-index lag: a near-simultaneous run may not yet see an issue created seconds earlier.
48
+
49
+ **Dashboard.** Only a whiteboard ticket refreshes the dashboard (`--update-only`, never opening one), unless `--no-dashboard`. A refresh failure leaves the status unchanged and reports `dashboard: "failed"`. Map tickets and fog report `not-applicable`.
50
+
51
+ **Settle residual.** There is no settle window: the labelling actor is verified as one the guard never acts on before any edge and before `created` is printed, so no asynchronous strip can follow a `created`.
@@ -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. 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,8 +6,8 @@ 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
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.
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).
@@ -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.
@@ -4,10 +4,10 @@
4
4
 
5
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:
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
13
  **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:
@@ -26,4 +26,4 @@ For each issue in the first two sections (incoming issues, parked fog), propose
26
26
  - **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
27
  - **Needs info.** Ask the reporter for a repro or specifics; leave it open.
28
28
 
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.
29
+ 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.
@@ -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.
@@ -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
 
@@ -57,10 +59,10 @@ The user invokes with a map (URL or number). A ticket is optional: without one,
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.
58
60
  - **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
61
  - **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.
62
+ 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
63
  - **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
64
  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.
65
+ 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
66
  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
67
  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
68
 
@@ -69,10 +71,10 @@ The user invokes with a map (URL or number). A ticket is optional: without one,
69
71
  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
72
 
71
73
  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.
74
+ - **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`.
75
+ - **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.
76
+ - **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`).)
77
+ - **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
78
  - **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
79
  - **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
80
  - **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.
@@ -15,7 +15,7 @@ A ticket whose body carries a `UAT of: [<parent name>](<parent url>)` line is a
15
15
  Close a choice, but widen before you narrow:
16
16
 
17
17
  1. **Diverge (only if the option space isn't already clear).** Mapping the frontier usually framed the options already; when it did, check that the framed set is still current and complete before grilling: re-read it against the ticket and against what has landed since (the map's `Decisions so far`, tickets closed after it was framed), add any missing candidate (the status quo, "leave it as it is", included when it is a real option), post the additions as an `OPTIONS` comment, then grill. When it didn't (the ticket names a question but the candidate answers aren't laid out) open the space *first*: brainstorm the plausible options, name each concretely, and post them as an `OPTIONS` comment. Divergence has no judgement yet: surface possibilities, don't rank them. If a missing **fact** is what's blocking clarity, spin a `research` ticket and block on it; if the question is "how could it look/behave", spin a `prototype`. Don't grill a space you haven't opened.
18
- 2. **Converge.** Run the **Grilling** + **Domain vocabulary** techniques over the options to pick one. Before the final confirmation, run the **Holistic review before the final confirmation** (Grilling technique). Record the decision and the why. The delegate option (agent picks) is never offered here, the choice stays the human's, but **defer is offered** (in prose, per the recipe in `../../_shared/choices.md`) when the choice is genuinely blocked on a prerequisite.
18
+ 2. **Converge.** 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) over the options to pick one. Before the final confirmation, run the **Holistic review before the final confirmation** (Grilling technique). Record the decision and the why. The delegate option (agent picks) is never offered here, the choice stays the human's, but **defer is offered** (in prose, per the recipe in `../../_shared/choices.md`) when the choice is genuinely blocked on a prerequisite.
19
19
  3. **Carry it into build.** A decision that only gets recorded builds nothing: when a fog area graduated into this ticket, its entry left *Not yet specified*, so once the ticket closes the work it implies lives nowhere unless a ticket carries it. So, before the close, list the work the answer makes buildable (the code, the content, the setup it commits the project to) and give every piece a home: an **existing** open ticket that already covers it (a `build`, `bug` or `task` on the map, its body extended with a `Decided in: <name+link>` line when it lacks one), or a **new `build` ticket** on the same map (on the whiteboard for a whiteboard decision), create-then-wire per `../../_shared/substrate.md`, its `## Question` naming the deliverable and its body a `Decided in: <name+link>` line back to this ticket, labelled `trailhead:blocked` and wired to the open tickets it really waits on (another unresolved decision, the build it extends). Size each one to a session, as at charting; a sprawling answer gets several builds, not one. Put the proposed split (titles, one line of scope each, blockers) in front of the human inside the **final confirmation** of step 2, so the scope of what gets built is theirs too, then create the tickets. The resolution comment carries one `Builds: <name+link>, <name+link>` line listing every ticket (new or existing) that carries the work. Only a decision whose answer builds nothing (a pure policy, a naming, a ruling that a feature is dropped) writes `Builds: none (<why>)` instead, with the reason. **Never `gh issue close` a decision whose resolution comment lacks the `Builds:` line**: the line is what lets the map, and `/trailhead:audit`, tell a carried decision from a stranded one.
20
20
 
21
21
  The two phases are distinct on purpose: divergence generates, grilling chooses. Never let the agent invent the options *and* pick for the human: the choice stays theirs.
@@ -31,21 +31,21 @@ The ticket that *builds*. Lean cycle, all in **ticket comments**:
31
31
 
32
32
  **Size triage (`effort`-gated, before Discuss).** When `config.effort` is `lean`, run the **size triage and ask-to-skip offer defined canonically under effort in `../../_shared/configuration-reference.md`** before Discuss: classify the change, and on trivial/small surface the one consolidated offer (its skippable steps here: the plan subagent -> a short inline plan; TDD; Cross-AI plan review; the full Code-review subagent -> a lighter inline pass; the Goal-backward verification subagent -> a quick inline goal check), going lean **only on an explicit yes**. On `normal`, on a no/silence/ambiguity, and always under `effort: standard` (the default), run the full cycle below unchanged. The correctness spine (atomic `Refs:` commits, the tests / the plan's criterion, the post-commit self-check, Resolve) is never in the offer.
33
33
 
34
- 1. **Discuss**: **never auto-grill.** Start from Plan. If reading the ticket surfaces blocking ambiguity, **stop and ask the user** whether they want a **Grilling** round: don't launch it on your own initiative. Only on their assent run the short round (ending with the **Holistic review before the final confirmation** of the Grilling technique) and post a `DISCUSS` comment with the closed choices, recorded after the review. The user may also ask for it themselves at any time. Advisory choices raised here **offer** the delegate/defer options, per the injection recipe in `../../_shared/choices.md`.
35
- 2. **Plan**: a `PLAN` comment: the steps, the seams, the files touched, the verification criteria. Apply **TDD** per `config.tdd` (`seams`/`on`/`off`). Produce this plan via a *planner subagent* on `config.models.plan` (dispatch the **`trailhead-plan`** agent, read-only, passing `config.models.plan` as the model override; inline when the model is unset or equals the session model; Execute then runs as its own subagent, see below). **Report (orchestrator).** Run the report-status check on the planner's report (`--technique plan`, the *Malformed review report* protocol in `../../_shared/techniques.md`) and branch on the checker's `verdict`: `status: ready` -> post the checker's `body` verbatim as the `PLAN` comment; `status: needs_input` -> ask the user the `questions:` (the checker's `fields`), then re-spawn the planner with the answers; never answer them yourself or guess. If `config.plan_review` is on, run **Cross-AI plan review** before Execute: send the plan to external AI CLIs and converge on their concerns. Now that the files/seam are known, run the **undeclared-coupling check** (`../../_shared/teamwork.md`) against in-progress tickets. **Plan to sufficiency:** stop once you can name the steps, seams, and files with confidence, and never re-read a file already read in this planning pass; act when you have enough to plan, rather than re-deriving what the pass already established. **Coverage check before `ready`.** Before the PLAN is returned (the short inline plan too), map every `Done when` criterion of the ticket (its `Question` when it has none) to the step(s) and the verification criterion that deliver it, and keep that mapping in the PLAN's verification criteria: a criterion no step delivers is a gap, so add the step, or return `needs_input` when closing it needs a decision. Where the PLAN picks between approaches, the pick is stress-tested per **Stress-test the pick before offering it** in `../../_shared/choices.md`, its `Weak point:` line kept in the PLAN. **The single exception:** under an accepted `lean` skip (see the Size triage above), produce a short inline plan here instead of the planner subagent, and skip Cross-AI plan review; absent that accepted skip, this step runs in full as described.
34
+ 1. **Discuss**: **never auto-grill.** Start from Plan. If reading the ticket surfaces blocking ambiguity, **stop and ask the user** whether they want a **Grilling** round: don't launch it on your own initiative. Only on their assent run the short round (first run the routing step at the top of `../../_shared/techniques/grilling.md`, then the rounds as **Who runs a round** there says) (ending with the **Holistic review before the final confirmation** of the Grilling technique) and post a `DISCUSS` comment with the closed choices, recorded after the review. The user may also ask for it themselves at any time. Advisory choices raised here **offer** the delegate/defer options, per the injection recipe in `../../_shared/choices.md`.
35
+ 2. **Plan**: **First, route it.** Run `node <hooks>/lib/model-route.js plan --session <your full model id> <project root>` and obey its `route`. Your full model id is in your system prompt or environment ("The exact model ID is ..."); pass `unknown` only if it is truly absent. Obey it exactly: never decide inline versus dispatch by hand, never read the config yourself to decide. On `route: dispatch` spawn `agent` with model `alias` (`agent_type=<agent>` on Codex) and check its report as below; on `route: inline` write the plan in the session. Finding `<hooks>`, the flags and the only two exceptions are in **Route every step through model-route.js** in `../../_shared/techniques.md`. The step produces a `PLAN` comment: the steps, the seams, the files touched, the verification criteria. Apply **TDD** per `config.tdd` (`seams`/`on`/`off`). Produce this plan via the route: a *planner subagent* on `config.models.plan` (the read-only **`trailhead-plan`** agent) on dispatch, the session on inline; Execute then runs as its own subagent, see below. **Report (orchestrator).** Run the report-status check on the planner's report (`--technique plan`, the *Malformed review report* protocol in `../../_shared/techniques.md`) and branch on the checker's `verdict`: `status: ready` -> post the checker's `body` verbatim as the `PLAN` comment; `status: needs_input` -> ask the user the `questions:` (the checker's `fields`; when the user opts to grill them, first run the routing step at the top of `../../_shared/techniques/grilling.md`, then the rounds as **Who runs a round** there says), then re-spawn the planner with the answers; never answer them yourself or guess. If `config.plan_review` is on, run **Cross-AI plan review** before Execute: send the plan to external AI CLIs and converge on their concerns. Now that the files/seam are known, run the **undeclared-coupling check** (`../../_shared/teamwork.md`) against in-progress tickets. **Plan to sufficiency:** stop once you can name the steps, seams, and files with confidence, and never re-read a file already read in this planning pass; act when you have enough to plan, rather than re-deriving what the pass already established. **Coverage check before `ready`.** Before the PLAN is returned (the short inline plan too), map every `Done when` criterion of the ticket (its `Question` when it has none) to the step(s) and the verification criterion that deliver it, and keep that mapping in the PLAN's verification criteria: a criterion no step delivers is a gap, so add the step, or return `needs_input` when closing it needs a decision. Where the PLAN picks between approaches, the pick is stress-tested per **Stress-test the pick before offering it** in `../../_shared/choices.md`, its `Weak point:` line kept in the PLAN. **The single exception:** under an accepted `lean` skip (see the Size triage above), produce a short inline plan here instead of the planner subagent, and skip Cross-AI plan review; absent that accepted skip, this step runs in full as described.
36
36
  3. **Execute**: the orchestrator runs the HITL and tracker moves around an **executor subagent on `config.models.execute`**; the implementing protocol itself lives in `executor-brief.md` (this directory), the only protocol text the agent receives from this step. The agent cannot ask the user and never writes to the tracker, pushes or creates a branch.
37
37
  - **Prototype FIRST (orchestrator).** **If this ticket changes user-facing UI (a new screen, a redesign, a changed layout), run the Prototype technique FIRST, before any real UI code**: surface a mockup to react to (its clickable link always in the same message as the approval question, per the Prototype technique; the guard also accepts a link inside the approval question itself, the prose stays preferred), routed by `config.design` (disk / artifacts / stitch) and gated by `config.design.approval` (`explicit` waits for a go-ahead, `auto` proceeds after surfacing). **Route by `config.design` and never shortcut a hosted mode to a local disk mockup**: when `config.design` is `artifacts` (or the legacy `claude.ai/design`) or `stitch`, the mockup is built there (reusing a cached `design.project`); `disk` is only for `config.design: disk` or the technique's explicit, announced fallback, never a convenience default. Read `config.design` before producing anything. For a redesign or new screen the default is to offer mockups; don't jump straight to code on a UI ticket (full statement in the **Prototype** technique, `../../_shared/techniques/prototype.md`).
38
38
  - **Branch and worktree (orchestrator).** Following the repo's `trailhead:conventions` `git:`, straight to `main` by default, or a feature branch + PR when `git: pr` (the orchestrator creates the branch before the spawn and opens the PR; the executor only commits, never pushes, switches branch or writes to the tracker). **Under `isolation: worktree`, the work happens in this ticket's `git worktree`** (`trailhead/t<n>` branch), integrating to the trunk at Resolve (see `../../_shared/teamwork.md`).
39
- - **Dispatch.** Spawn the **`trailhead-executor`** agent (full tools that can Edit/Write/commit), passing `config.models.execute` as the model override and `executor-brief.md` as the only protocol text, plus the runtime inputs: the ticket number, the PLAN, the base SHA, the `config.tdd` value, the approved mockup link if any, the accepted mockup deviations, and, for a resumed spawn, the packages the user approved. Inline when the model is unset or equals the session model: the session then does both parts. Inline, the session reads `executor-brief.md` and applies it, asking the user directly at the points the brief says to report. The agent implements with **atomic commits** (conventional commits, each with a `Refs: #<n>` trailer for this ticket, see **Principles** in `../../_shared/principles.md`; the trailer is also what scopes Verify's review and goal check, per **Ticket diff** in `../../_shared/techniques/code-review.md`; one commit = one verifiable step); the commits land on `main`, visible.
39
+ - **Dispatch.** Spawn the **`trailhead-executor`** agent (full tools that can Edit/Write/commit) only as the route says. **First, route it.** Run `node <hooks>/lib/model-route.js execute --session <your full model id> <project root>` and obey its `route`. Your full model id is in your system prompt or environment ("The exact model ID is ..."); pass `unknown` only if it is truly absent. Obey it exactly: never decide inline versus dispatch by hand, never read the config yourself to decide (flags, `<hooks>` lookup and the two exceptions: **Route every step through model-route.js** in `../../_shared/techniques.md`). On `route: dispatch` spawn it with the route's `alias` as the model override (`agent_type=<agent>` on Codex) and `executor-brief.md` as the only protocol text, plus the runtime inputs: the ticket number, the PLAN, the base SHA, the `config.tdd` value, the approved mockup link if any, the accepted mockup deviations, and, for a resumed spawn, the packages the user approved. On `route: inline` the session does both parts: it reads `executor-brief.md` and applies it, asking the user directly at the points the brief says to report. The agent implements with **atomic commits** (conventional commits, each with a `Refs: #<n>` trailer for this ticket, see **Principles** in `../../_shared/principles.md`; the trailer is also what scopes Verify's review and goal check, per **Ticket diff** in `../../_shared/techniques/code-review.md`; one commit = one verifiable step); the commits land on `main`, visible.
40
40
  - **Fidelity (orchestrator).** The approved mockup is reproduced with **100% fidelity** (layout, spacing, sizing, colour, type, copy, and every state), never a loose approximation. **Make fidelity verifiable, not a hope:** the executor returns the enumerated fidelity list in its report and the orchestrator posts it as a ticket comment (noting any element raised as infeasible with its resolution), so Verify works from a durable checklist instead of reconstructing intent. At Verify, **Acceptance testing** checks the built screen against the approved mockup (element by element, state by state) and **Goal-backward verification** carries fidelity as an explicit goal claim (see `../../_shared/techniques/acceptance-testing.md` and `../../_shared/techniques/verify.md`).
41
41
  - **Infeasible element (orchestrator).** When the report marks an element infeasible, raise it with the user and settle it: revise and re-approve the mockup, or the user accepts a named deviation. Then re-spawn the executor with the settled mockup and the accepted deviations; never let it deviate silently.
42
42
  - **Flagged package (orchestrator).** The **verify-before-install gate** (slopsquatting barrier) runs in the executor (legitimacy, typosquat similarity, age, downloads, repository metadata). On a flagged package in the report, block-and-ask: stop and ask the user, never installing on your own judgement. Only on an explicit yes, re-spawn the executor with that package passed as user-approved (it then re-runs the exact install command prefixed with `TRAILHEAD_VERIFIED_INSTALL=1`); on a no it is not installed and the plan is revisited with the user. On a host with a hook bus the `trailhead-install-guard` `PreToolUse` hook enforces the gate, blocking a named public-registry install (npm / PyPI / crates.io / Go / RubyGems) until it is vetted.
43
43
  - **Surfaced scope (orchestrator).** New scope surfacing mid-execute, reported by the executor in `surfaced scope:`, is captured/split, not worked here.
44
44
  - **Report (orchestrator).** Run the report-status check on the agent's report (`--technique executor`, the *Malformed review report* protocol in `../../_shared/techniques.md`) and branch on the checker's `verdict`, reading its `fields`: never reconstruct a result from the prose. `status: done` -> Verify. `status: partial` or `blocked` -> act on each `blockers:` entry with the moves above (flagged package, infeasible element; a foreign change in the way is raised with the user), then re-spawn. Post `fidelity:` as the fidelity comment and route `surfaced scope:` as below. Check `commits:` against the repo: it must equal the `Refs: #<n>` commit set since the base SHA, in both directions, and every SHA must resolve (`git cat-file -e <sha>^{commit}`); a mismatch is surfaced to the user, never patched over.
45
45
  4. **Verify**: a `VERIFY` comment: **start with the Stale-base preflight** (`trailhead-base-preflight.js --sync`, **Stale-base preflight** in `../../_shared/teamwork.md`): preflight, then sync, then re-run the tests, all before anything is reviewed (a rebase rewrites SHAs the review ledger cites, and the Code review and goal check must see the synced tree); then run the tests / the plan's criterion; then the **Code review** technique, scoped to the ticket's own commits per its **Ticket diff** rule (`../../_shared/techniques/code-review.md`: the `Refs: #<n>` set, read per commit, recomputed each round, an empty set surfaced as an error, never a `<base>...HEAD` range) (its subagent reviews *adversarially*: it doesn't defer to a `CLAUDE.md` convention or a "settled decision" memory), and carries the review's counts line and findings ledger, per code-review.md, **run in the background and collected by polling, never a blind blocking wait** (a hard engine default on every host, see *Reviews never block the session* in `../../_shared/techniques.md`); run the report-status check before branching on status (each report, from the subagent or inline, goes through the *Malformed review report* protocol in `../../_shared/techniques.md`, which also fixes the layout of the round's single `VERIFY` comment); **read the review's leading `status:` tag to branch deterministically: `status: clean` -> proceed to Resolve, `status: issues_found` -> iterate the fix-and-re-review loop (or run the HITL checkpoint) before resolving**; then the **Goal-backward verification** technique on `config.models.verify` (does the change deliver what the ticket promised, backward from its `Question` and the `PLAN`'s criteria; the read-only `trailhead-verify` subagent, on the same **Ticket diff** set), **run in the background and collected by polling** like Code review, branching on its leading `status:` tag (`status: met` -> proceed, `status: gaps_found` -> loop back to Execute for the gap or run the HITL checkpoint before resolving, `status: inconclusive` as the leading tag (all gaps cleared) -> a per-reason-class HITL checkpoint with no auto-retry, never auto-resolving over the unevaluated goal); then **Acceptance testing** if the change is user-facing, opening with the UAT setup (`trailhead-uat-setup.js`, after the preflight) and then the drive-mode ask (`../../_shared/techniques/acceptance-testing.md`): AI-driven, or **walk the user through a guided UAT conversationally, one step at a time**, never a checklist dumped in a comment (or skip (waive acceptance), recorded as waived, if the user so chooses): interactive by design, so it runs inline, not backgrounded. Report the outcome honestly (if a test fails, say so). **The single exception:** under an accepted `lean` skip, run a lighter inline review pass instead of the full Code-review subagent and a quick inline goal check instead of the full Goal-backward verification subagent (or skip either only when the change is truly cosmetic); the lighter inline review still uses the same finding IDs, counts line, and ledger, and the same no-`open` gate, per code-review.md; the tests / criterion and the honest outcome report still run regardless. At a HITL checkpoint here, an advisory sub-choice (which fix, how to proceed) **offers** the delegate option, per the injection recipe in `../../_shared/choices.md`; the accept/resolve decision stays the human's.
46
- 5. **Resolve**: a resolution comment with what was done → `gh issue close` → update `Decisions so far` on the map (default path: `node <templates>/trailhead-issue-body.js append --issue <map> --section "Decisions so far" --line "<pointer>"`, a body read-modify-write that obeys the **Fail-safe body rewrites** rule in `../../_shared/substrate.md` for you; `<templates>` is the same three-dir lookup as for `trailhead-work-state.js` (**Work state file** in `../../_shared/teamwork.md`); when the helper is missing, do the read-modify-write by hand per that rule). **Disposition gate (before the close)**: the latest `VERIFY` comment's findings ledger covers every finding the counts line counts and none of them is `open`; the resolution comment links that ledger rather than duplicating it; otherwise do not `gh issue close` (per code-review.md's Disposition gate). **Stale-base preflight (before integrating).** Run the preflight per **Stale-base preflight** in `../../_shared/teamwork.md` (`git: main`: right before the trunk push; `git: pr` without isolation: on the ticket branch, immediately before the push and PR; `worktree`/`clone`: on the main checkout, then on the ticket branch) and integrate only on `current`/`synced` with the tests green and `checksOk`. **Pending-UAT guard (before the close).** If Verify left acceptance testing **not yet run** (it would otherwise be deferred), **pose the two-way choice to the user and wait for an explicit answer, never open a ticket unilaterally**: (a) run the UAT now, in this session, with no ticket (the stale-base preflight first, then the UAT setup, then the drive-mode ask, `../../_shared/techniques/acceptance-testing.md`), or (b) open a UAT follow-up ticket to run it later. (Under an autonomous run the guard takes option (b) itself for the human-only steps, reusing an open follow-up instead of opening a second one: see `auto.md`.) On (a), run the guided walkthrough now and resolve on a confirmed pass, or on an explicit waiver if the user skips at the ask (recorded as unconfirmed; the resolution comment states it) (a failing step becomes a fix or a `bug` follow-up and an incomplete/blocked step is marked pending and tracked, per the guided-UAT rules in `../../_shared/techniques/acceptance-testing.md`, not a silent close); on (b), **open the follow-up ticket carrying the owed UAT first**, linked back to this parent (a `task` on the frontier or a `trailhead:seed` gated on the run, per *Pending UAT must stay tracked* in `../../_shared/techniques/acceptance-testing.md`). **Never open a UAT ticket without asking, never `gh issue close` on a chosen-deferred UAT with nothing opened, and never let silence or an ambiguous reply default to either path.** When acceptance was already completed at Verify, or was **explicitly waived** by the user at the drive-mode ask (a waiver is not a deferral: nothing is owed, so nothing is opened; the resolution comment states the waiver), this guard doesn't fire. **Post-commit self-check (before the comment goes out).** For each commit the comment cites, confirm the hash resolves (`git cat-file -e <hash>^{commit}`, or `git show --stat <hash>`) and that every file the comment names as touched actually appears in that commit's diff (`git -c core.quotepath=false show --name-only --format= <hash>`, with `core.quotepath=false` so a non-ASCII filename compares plainly instead of in git's C-quoted form); correct or drop any citation that fails the check, so a hallucinated or mis-stated commit never lands in the tracker record. **If this change materially altered the codebase** (a new module/layer, dependency, seam, or build/test command), **patch the affected facet of the `trailhead:codebase` issue** (see **Codebase** in `../../_shared/substrate.md`; look it up per its singleton fail-closed rule) in place (a line or two, not a re-map). When a structural event is due (a whiteboard ticket resolved, a map exhausted), refresh the dashboard with `node <templates>/trailhead-dashboard.js` (one call, whole body from the live tracker; by hand per `../../_shared/substrate-commands.md` when the helper is missing). Then close the session with the **Session handoff** ritual (`../../_shared/session-handoff.md`): one `node <templates>/trailhead-work-state.js clear --repo <working-root>` removes the work state and the session-ticket marker, then `/clear` + next command.
46
+ 5. **Resolve**: a resolution comment with what was done → `gh issue close` → update `Decisions so far` on the map (default path: `node <templates>/trailhead-issue-body.js append --issue <map> --section "Decisions so far" --line "<pointer>"`, a body read-modify-write that obeys the **Fail-safe body rewrites** rule in `../../_shared/substrate.md` for you; `<templates>` is the same three-dir lookup as for `trailhead-work-state.js` (**Work state file** in `../../_shared/teamwork.md`); when the helper is missing, do the read-modify-write by hand per that rule). **Disposition gate (before the close)**: the latest `VERIFY` comment's findings ledger covers every finding the counts line counts and none of them is `open`; the resolution comment links that ledger rather than duplicating it; otherwise do not `gh issue close` (per code-review.md's Disposition gate). **Stale-base preflight (before integrating).** Run the preflight per **Stale-base preflight** in `../../_shared/teamwork.md` (`git: main`: right before the trunk push; `git: pr` without isolation: on the ticket branch, immediately before the push and PR; `worktree`/`clone`: on the main checkout, then on the ticket branch) and integrate only on `current`/`synced` with the tests green and `checksOk`. **Pending-UAT guard (before the close).** If Verify left acceptance testing **not yet run** (it would otherwise be deferred), **pose the two-way choice to the user and wait for an explicit answer, never open a ticket unilaterally**: (a) run the UAT now, in this session, with no ticket (the stale-base preflight first, then the UAT setup, then the drive-mode ask, `../../_shared/techniques/acceptance-testing.md`), or (b) open a UAT follow-up ticket to run it later. (Under an autonomous run the guard takes option (b) itself for the human-only steps, reusing an open follow-up instead of opening a second one: see `auto.md`.) On (a), run the guided walkthrough now and resolve on a confirmed pass, or on an explicit waiver if the user skips at the ask (recorded as unconfirmed; the resolution comment states it) (a failing step becomes a fix or a `bug` follow-up and an incomplete/blocked step is marked pending and tracked, per the guided-UAT rules in `../../_shared/techniques/acceptance-testing.md`, not a silent close); on (b), **open the follow-up ticket carrying the owed UAT first**, linked back to this parent (a `task` on the frontier or a `trailhead:seed` gated on the run, per *Pending UAT must stay tracked* in `../../_shared/techniques/acceptance-testing.md`). **Never open a UAT ticket without asking, never `gh issue close` on a chosen-deferred UAT with nothing opened, and never let silence or an ambiguous reply default to either path.** When acceptance was already completed at Verify, or was **explicitly waived** by the user at the drive-mode ask (a waiver is not a deferral: nothing is owed, so nothing is opened; the resolution comment states the waiver), this guard doesn't fire. **Post-commit self-check (before the comment goes out).** For each commit the comment cites, confirm the hash resolves (`git cat-file -e <hash>^{commit}`, or `git show --stat <hash>`) and that every file the comment names as touched actually appears in that commit's diff (`git -c core.quotepath=false show --name-only --format= <hash>`, with `core.quotepath=false` so a non-ASCII filename compares plainly instead of in git's C-quoted form); correct or drop any citation that fails the check, so a hallucinated or mis-stated commit never lands in the tracker record. **If this change materially altered the codebase** (a new module/layer, dependency, seam, or build/test command), **patch the affected facet of the `trailhead:codebase` issue** (see **Codebase** in `../../_shared/substrate.md`; look it up per its singleton fail-closed rule) in place (a line or two, not a re-map). When a structural event is due (a whiteboard ticket resolved, a map exhausted), refresh the dashboard with `node <templates>/trailhead-dashboard.js` (one call, whole body from the live tracker; by hand per `../../_shared/substrate-commands.md` when the helper is missing). **Unblock dependents (every ticket, map or whiteboard).** Right after the close, whatever the ticket type and whether or not there is a map (independent of the map-only `Decisions so far` update above): run `node <templates>/trailhead-unblock.js --issue <n> --repo <owner/name>` (same `<templates>` lookup), which removes `trailhead:blocked` from every open dependent whose last open blocker just closed and prints one JSON line (`{"status":"ok","unblocked":[...],"stillBlocked":[...]}` or `{"status":"error","reason":...}`). When the helper is missing or returns `error`, do it by hand: for each open dependent carrying `trailhead:blocked` in `gh api repos/{owner}/{repo}/issues/<n>/dependencies/blocking`, read `gh api repos/{owner}/{repo}/issues/<dep>/dependencies/blocked_by` and, if every blocker there is closed, `gh issue edit <dep> --remove-label "trailhead:blocked"`; report a failure in one line. A ticket unblocked here joins the frontier, so the next-step handoff can name it. Then close the session with the **Session handoff** ritual (`../../_shared/session-handoff.md`): one `node <templates>/trailhead-work-state.js clear --repo <working-root>` removes the work state and the session-ticket marker, then `/clear` + next command.
47
47
 
48
- New scope that surfaces mid-build (while planning, executing, or testing) → **capture or split, never expand this ticket in flight** (see **Scope that surfaces while working a ticket**, served by the capture cluster). If the user defers that in-the-moment choice rather than settling it, mark the follow-up **pending** so the **Pending follow-up guard** re-poses it at handoff (`../../_shared/session-handoff.md`), instead of losing it to scrollback.
48
+ New scope that surfaces mid-build (while planning, executing, or testing) → **capture or split, never expand this ticket in flight** (see **Scope that surfaces while working a ticket**, `../../_shared/surfaced-scope.md`). If the user defers that in-the-moment choice rather than settling it, mark the follow-up **pending** so the **Pending follow-up guard** re-poses it at handoff (`../../_shared/session-handoff.md`), instead of losing it to scrollback.
49
49
 
50
50
  ## `bug`: the fix with diagnosis first
51
51
  A defect to correct. Unlike `build`, you don't start from implementation: first you **understand**.
@@ -59,9 +59,9 @@ Then run the cycle, all in ticket comments:
59
59
  **Size triage (`effort`-gated).** Under `config.effort: lean`, run the **size triage and ask-to-skip offer defined canonically under effort in `../../_shared/configuration-reference.md`**, classifying the fix as in the `build` engine. On trivial/small, offer to skip only the *heavy verification* a tiny fix does not warrant: lighten or skip **TDD**, run a **lighter inline Code review** instead of the full subagent, and a **quick inline goal check** instead of the full **Goal-backward verification** subagent, going lean **only on an explicit yes**. **Diagnose is never in the offer**: a bug always needs a confirmed root cause before Fix, whatever its size. On a no/silence/ambiguity, and under `effort: standard` (default), the full cycle runs.
60
60
 
61
61
  1. **Repro**: a `REPRO` comment: how to reproduce, expected vs observed behaviour.
62
- 2. **Diagnose**: run the **Debug** technique. Dispatch `trailhead-debug` and run the report-status check on its report (`--technique debug`, the *Malformed review report* protocol in `../../_shared/techniques.md`); branch on the checker's `verdict`, reading its `fields`: `status: found` -> its `answer:` (the cause) and `evidence:` (the falsification test and its result) feed the `DIAGNOSIS`; `status: inconclusive` -> the ticket stays in diagnosis, next hypotheses, never on to Fix; `status: needs_input` -> ask the user the `open questions:`, then re-spawn with the answers. A `DIAGNOSIS` comment with the root cause **stated as a falsifiable hypothesis plus the falsification test that confirmed it**. **Pre-fix checkpoint: only a confirmed cause proceeds to Fix** (a cause that has not survived its falsification test stays in diagnosis, looping the hypothesis steps); the full gate lives in the **Debug** technique (`../../_shared/techniques/debug.md`).
62
+ 2. **Diagnose**: **First, route it.** Run `node <hooks>/lib/model-route.js debug --session <your full model id> <project root>` and obey its `route`. Your full model id is in your system prompt or environment ("The exact model ID is ..."); pass `unknown` only if it is truly absent. Obey it exactly: never decide inline versus dispatch by hand, never read the config yourself to decide (flags, `<hooks>` lookup and the two exceptions: **Route every step through model-route.js** in `../../_shared/techniques.md`). On `route: dispatch` spawn `agent` with model `alias` (`agent_type=<agent>` on Codex); on `route: inline` run the **Debug** technique in the session. Then run the report-status check on the report (`--technique debug`, the *Malformed review report* protocol in `../../_shared/techniques.md`); branch on the checker's `verdict`, reading its `fields`: `status: found` -> its `answer:` (the cause) and `evidence:` (the falsification test and its result) feed the `DIAGNOSIS`; `status: inconclusive` -> the ticket stays in diagnosis, next hypotheses, never on to Fix; `status: needs_input` -> ask the user the `open questions:`, then re-spawn with the answers. A `DIAGNOSIS` comment with the root cause **stated as a falsifiable hypothesis plus the falsification test that confirmed it**. **Pre-fix checkpoint: only a confirmed cause proceeds to Fix** (a cause that has not survived its falsification test stays in diagnosis, looping the hypothesis steps); the full gate lives in the **Debug** technique (`../../_shared/techniques/debug.md`).
63
63
  3. **Fix**: the orchestrator runs the HITL and tracker moves around an **executor subagent on `config.models.execute`**; the implementing protocol itself lives in `executor-brief.md` (this directory), the only protocol text the agent receives from this step. The agent cannot ask the user and never writes to the tracker, pushes or creates a branch.
64
- - **Dispatch.** Spawn the **`trailhead-fix`** agent (full tools), passing `config.models.execute` as the model override and `executor-brief.md` as the only protocol text, plus the runtime inputs: the ticket number, the base SHA, the confirmed `DIAGNOSIS`, the `config.tdd` value, the approved mockup link if any, the accepted mockup deviations, and, for a resumed spawn, the packages the user approved. Inline when unset/same as session: the session then does both parts. Inline, the session reads `executor-brief.md` and applies it, asking the user directly at the points the brief says to report. The agent implements the correction with **atomic commits** (each with a `Refs: #<n>` trailer for this ticket, see **Principles** in `../../_shared/principles.md`; the trailer is also what scopes Verify's review and goal check, per **Ticket diff** in `../../_shared/techniques/code-review.md`), and where sensible a test that fails before and passes after (**TDD**).
64
+ - **Dispatch.** Spawn the **`trailhead-fix`** agent (full tools) only as the route says. **First, route it.** Run `node <hooks>/lib/model-route.js execute --session <your full model id> <project root>` and obey its `route`. Your full model id is in your system prompt or environment ("The exact model ID is ..."); pass `unknown` only if it is truly absent. Obey it exactly: never decide inline versus dispatch by hand, never read the config yourself to decide (flags, `<hooks>` lookup and the two exceptions: **Route every step through model-route.js** in `../../_shared/techniques.md`). Fix routes on the `execute` key: on `route: dispatch` spawn `trailhead-fix` in place of the returned `agent`, with the route's `alias` as the model override (`agent_type=trailhead-fix` on Codex) and `executor-brief.md` as the only protocol text, plus the runtime inputs: the ticket number, the base SHA, the confirmed `DIAGNOSIS`, the `config.tdd` value, the approved mockup link if any, the accepted mockup deviations, and, for a resumed spawn, the packages the user approved. On `route: inline` the session does both parts: it reads `executor-brief.md` and applies it, asking the user directly at the points the brief says to report. The agent implements the correction with **atomic commits** (each with a `Refs: #<n>` trailer for this ticket, see **Principles** in `../../_shared/principles.md`; the trailer is also what scopes Verify's review and goal check, per **Ticket diff** in `../../_shared/techniques/code-review.md`), and where sensible a test that fails before and passes after (**TDD**).
65
65
  - **Branch and worktree (orchestrator).** Per the repo's `trailhead:conventions` `git:`, `main` by default, else a branch + PR: the orchestrator creates the branch before the spawn and opens the PR under `git: pr`; the agent only commits. Under `isolation: worktree`, the work happens in this ticket's `git worktree`.
66
66
  - **UI fix: Prototype and approval (orchestrator).** Where the fix changes a screen (a user-facing UI fix), run the Prototype technique FIRST, before any real UI code: surface a mockup, routed by `config.design` (never shortcut a hosted mode to a local disk mockup) and gated by `config.design.approval`, per the technique (`../../_shared/techniques/prototype.md`). Once approved, the executor reproduces it with 100% fidelity.
67
67
  - **Fidelity (orchestrator).** The executor returns the enumerated fidelity list in its report and the orchestrator posts it as a ticket comment, so Verify (Acceptance testing and Goal-backward verification) checks against a durable list.
@@ -70,9 +70,9 @@ Then run the cycle, all in ticket comments:
70
70
  - **Surfaced scope (orchestrator).** New scope the executor reports in `surfaced scope:` is captured/split, never worked in this ticket.
71
71
  - **Report (orchestrator).** Run the report-status check on the agent's report (`--technique fix`, the *Malformed review report* protocol in `../../_shared/techniques.md`) and branch on the checker's `verdict`, reading its `fields`: never reconstruct a result from the prose. `status: done` -> Verify. `status: partial` or `blocked` -> act on each `blockers:` entry with the moves above (flagged package, infeasible element; a foreign change in the way is raised with the user), then re-spawn. Post `fidelity:` as the fidelity comment and route `surfaced scope:` as below. Check `commits:` against the repo: it must equal the `Refs: #<n>` commit set since the base SHA, in both directions, and every SHA must resolve (`git cat-file -e <sha>^{commit}`); a mismatch is surfaced to the user, never patched over.
72
72
  4. **Verify**: a `VERIFY` comment: **start with the Stale-base preflight** (`trailhead-base-preflight.js --sync`, **Stale-base preflight** in `../../_shared/teamwork.md`): preflight, then sync, then re-run the tests, all before anything is reviewed (a rebase rewrites SHAs the review ledger cites, and the Code review and goal check must see the synced tree); then the repro now passes, no regressions; **Code review** on the fix, scoped to the ticket's own commits per its **Ticket diff** rule (`../../_shared/techniques/code-review.md`: the `Refs: #<n>` set, read per commit, recomputed each round, an empty set surfaced as an error, never a `<base>...HEAD` range), and carries the review's counts line and findings ledger, per code-review.md, **run in the background and collected by polling, never a blind blocking wait** (a hard engine default on every host, see *Reviews never block the session* in `../../_shared/techniques.md`); run the report-status check before branching on status (each report, from the subagent or inline, goes through the *Malformed review report* protocol in `../../_shared/techniques.md`, which also fixes the layout of the round's single `VERIFY` comment); **read the review's leading `status:` tag to branch deterministically (`status: clean` -> Resolve, `status: issues_found` -> iterate the loop or run the checkpoint first)**; then the **Goal-backward verification** technique on `config.models.verify` (does the fix deliver what the ticket promised, backward from its `Question`; the read-only `trailhead-verify` subagent, on the same **Ticket diff** set), background and collected by polling, branching on its leading `status:` tag (`status: met` -> proceed, `status: gaps_found` -> loop back to Fix for the gap or run the checkpoint, `status: inconclusive` as the leading tag (all gaps cleared) -> a per-reason-class HITL checkpoint with no auto-retry, never auto-resolving over the unevaluated goal); **Acceptance testing** if the bug was user-facing, opening with the UAT setup (`trailhead-uat-setup.js`, after the preflight) and then the drive-mode ask (`../../_shared/techniques/acceptance-testing.md`): AI-driven, or **walk the user through a guided UAT conversationally, step by step**, not a checklist to self-serve (or skip (waive acceptance), recorded as waived, if the user so chooses): interactive by design, so it runs inline, not backgrounded. At a HITL checkpoint here, an advisory sub-choice (which fix, how to proceed) **offers** the delegate option, per the injection recipe in `../../_shared/choices.md`; the accept/resolve decision stays the human's.
73
- 5. **Resolve**: a resolution comment (cause + fix) → `gh issue close` → update `Decisions so far` (default path: `node <templates>/trailhead-issue-body.js append --issue <map> --section "Decisions so far" --line "<pointer>"`, same helper and fallback as the `build` Resolve step; a body read-modify-write that obeys the **Fail-safe body rewrites** rule in `../../_shared/substrate.md`). **Disposition gate (before the close)**: the latest `VERIFY` comment's findings ledger covers every finding the counts line counts and none of them is `open`; the resolution comment links that ledger rather than duplicating it; otherwise do not `gh issue close` (per code-review.md's Disposition gate). **Stale-base preflight (before integrating).** Run the preflight per **Stale-base preflight** in `../../_shared/teamwork.md` (`git: main`: right before the trunk push; `git: pr` without isolation: on the ticket branch, immediately before the push and PR; `worktree`/`clone`: on the main checkout, then on the ticket branch) and integrate only on `current`/`synced` with the tests green and `checksOk`. **Pending-UAT guard (before the close).** If Verify left acceptance testing **not yet run** (it would otherwise be deferred), **pose the two-way choice to the user and wait for an explicit answer, never open a ticket unilaterally**: (a) run the UAT now, in this session, with no ticket (the stale-base preflight first, then the UAT setup, then the drive-mode ask, `../../_shared/techniques/acceptance-testing.md`), or (b) open a UAT follow-up ticket to run it later. (Under an autonomous run the guard takes option (b) itself for the human-only steps, reusing an open follow-up instead of opening a second one: see `auto.md`.) On (a), run the guided walkthrough now and resolve on a confirmed pass, or on an explicit waiver if the user skips at the ask (recorded as unconfirmed; the resolution comment states it) (a failing step becomes a fix or a `bug` follow-up and an incomplete/blocked step is marked pending and tracked, per the guided-UAT rules in `../../_shared/techniques/acceptance-testing.md`, not a silent close); on (b), **open the follow-up ticket carrying the owed UAT first**, linked back to this parent (a `task` on the frontier or a `trailhead:seed` gated on the run, per *Pending UAT must stay tracked* in `../../_shared/techniques/acceptance-testing.md`). **Never open a UAT ticket without asking, never `gh issue close` on a chosen-deferred UAT with nothing opened, and never let silence or an ambiguous reply default to either path.** When acceptance was already completed at Verify, or was **explicitly waived** by the user at the drive-mode ask (a waiver is not a deferral: nothing is owed, so nothing is opened; the resolution comment states the waiver), this guard doesn't fire. **Run the post-commit self-check before posting, exactly as in the `build` Resolve step** (each cited hash resolves and the files it names appear in that commit's diff; correct or drop any citation that fails). **If the fix materially altered the codebase**, patch the affected facet of the `trailhead:codebase` issue (see **Codebase** in `../../_shared/substrate.md`, looked up per its singleton fail-closed rule; also a body read-modify-write, same **Fail-safe body rewrites** rule) in place (a line or two; the same `trailhead-issue-body.js` helper does it with `--section "<facet>"`). When a structural event is due, refresh the dashboard with `node <templates>/trailhead-dashboard.js`. Then close the session with the **Session handoff** ritual (`../../_shared/session-handoff.md`): one `node <templates>/trailhead-work-state.js clear --repo <working-root>` removes the work state and the session-ticket marker, then `/clear` + next command.
73
+ 5. **Resolve**: a resolution comment (cause + fix) → `gh issue close` → update `Decisions so far` (default path: `node <templates>/trailhead-issue-body.js append --issue <map> --section "Decisions so far" --line "<pointer>"`, same helper and fallback as the `build` Resolve step; a body read-modify-write that obeys the **Fail-safe body rewrites** rule in `../../_shared/substrate.md`). **Disposition gate (before the close)**: the latest `VERIFY` comment's findings ledger covers every finding the counts line counts and none of them is `open`; the resolution comment links that ledger rather than duplicating it; otherwise do not `gh issue close` (per code-review.md's Disposition gate). **Stale-base preflight (before integrating).** Run the preflight per **Stale-base preflight** in `../../_shared/teamwork.md` (`git: main`: right before the trunk push; `git: pr` without isolation: on the ticket branch, immediately before the push and PR; `worktree`/`clone`: on the main checkout, then on the ticket branch) and integrate only on `current`/`synced` with the tests green and `checksOk`. **Pending-UAT guard (before the close).** If Verify left acceptance testing **not yet run** (it would otherwise be deferred), **pose the two-way choice to the user and wait for an explicit answer, never open a ticket unilaterally**: (a) run the UAT now, in this session, with no ticket (the stale-base preflight first, then the UAT setup, then the drive-mode ask, `../../_shared/techniques/acceptance-testing.md`), or (b) open a UAT follow-up ticket to run it later. (Under an autonomous run the guard takes option (b) itself for the human-only steps, reusing an open follow-up instead of opening a second one: see `auto.md`.) On (a), run the guided walkthrough now and resolve on a confirmed pass, or on an explicit waiver if the user skips at the ask (recorded as unconfirmed; the resolution comment states it) (a failing step becomes a fix or a `bug` follow-up and an incomplete/blocked step is marked pending and tracked, per the guided-UAT rules in `../../_shared/techniques/acceptance-testing.md`, not a silent close); on (b), **open the follow-up ticket carrying the owed UAT first**, linked back to this parent (a `task` on the frontier or a `trailhead:seed` gated on the run, per *Pending UAT must stay tracked* in `../../_shared/techniques/acceptance-testing.md`). **Never open a UAT ticket without asking, never `gh issue close` on a chosen-deferred UAT with nothing opened, and never let silence or an ambiguous reply default to either path.** When acceptance was already completed at Verify, or was **explicitly waived** by the user at the drive-mode ask (a waiver is not a deferral: nothing is owed, so nothing is opened; the resolution comment states the waiver), this guard doesn't fire. **Run the post-commit self-check before posting, exactly as in the `build` Resolve step** (each cited hash resolves and the files it names appear in that commit's diff; correct or drop any citation that fails). **If the fix materially altered the codebase**, patch the affected facet of the `trailhead:codebase` issue (see **Codebase** in `../../_shared/substrate.md`, looked up per its singleton fail-closed rule; also a body read-modify-write, same **Fail-safe body rewrites** rule) in place (a line or two; the same `trailhead-issue-body.js` helper does it with `--section "<facet>"`). When a structural event is due, refresh the dashboard with `node <templates>/trailhead-dashboard.js`. **Unblock dependents (every ticket, map or whiteboard).** Right after the close, whatever the ticket type and whether or not there is a map (independent of the map-only `Decisions so far` update above): run `node <templates>/trailhead-unblock.js --issue <n> --repo <owner/name>` (same `<templates>` lookup), which removes `trailhead:blocked` from every open dependent whose last open blocker just closed and prints one JSON line (`{"status":"ok","unblocked":[...],"stillBlocked":[...]}` or `{"status":"error","reason":...}`). When the helper is missing or returns `error`, do it by hand: for each open dependent carrying `trailhead:blocked` in `gh api repos/{owner}/{repo}/issues/<n>/dependencies/blocking`, read `gh api repos/{owner}/{repo}/issues/<dep>/dependencies/blocked_by` and, if every blocker there is closed, `gh issue edit <dep> --remove-label "trailhead:blocked"`; report a failure in one line. A ticket unblocked here joins the frontier, so the next-step handoff can name it. Then close the session with the **Session handoff** ritual (`../../_shared/session-handoff.md`): one `node <templates>/trailhead-work-state.js clear --repo <working-root>` removes the work state and the session-ticket marker, then `/clear` + next command.
74
74
 
75
- A **blocking** bug that halts other work is worked immediately; an isolated one is a normal frontier ticket. New scope surfacing while fixing → capture or split, never expand this ticket in flight (see **Scope that surfaces while working a ticket**). A follow-up raised but left unopened is carried to the **Pending follow-up guard** at handoff (`../../_shared/session-handoff.md`), re-posed at Resolve rather than dropped.
75
+ A **blocking** bug that halts other work is worked immediately; an isolated one is a normal frontier ticket. New scope surfacing while fixing → capture or split, never expand this ticket in flight (see **Scope that surfaces while working a ticket**, `../../_shared/surfaced-scope.md`). A follow-up raised but left unopened is carried to the **Pending follow-up guard** at handoff (`../../_shared/session-handoff.md`), re-posed at Resolve rather than dropped.
76
76
 
77
77
  ## `task`: HITL or AFK
78
78
  Manual work that must happen before a *decision* can be made: signing up for a service to judge its API, provisioning access, moving data to see its shape. The agent drives it alone where it can (AFK); otherwise it hands the human a precise checklist (HITL). Resolved when the work is done; the answer records what was done and the resulting facts (where credentials live, new URLs, row counts) later tickets depend on.