@marcomigozzi/trailhead 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/README.it.md +45 -9
  2. package/README.md +46 -10
  3. package/bin/auto-intro-docs.test.js +72 -0
  4. package/bin/auto-merge-docs.test.js +33 -0
  5. package/bin/auto-merge-ticket-docs.test.js +32 -0
  6. package/bin/auto-review-rounds-docs.test.js +45 -0
  7. package/bin/auto-run-docs.test.js +60 -0
  8. package/bin/auto-scope-docs.test.js +33 -0
  9. package/bin/auto-slots-docs.test.js +45 -0
  10. package/bin/auto-uat-docs.test.js +47 -0
  11. package/bin/grilling-holistic-review-docs.test.js +90 -0
  12. package/bin/lib/codex-projection.js +5 -5
  13. package/bin/lib/codex-projection.test.js +2 -2
  14. package/bin/needs-you-docs.test.js +41 -0
  15. package/bin/pick-stress-test-docs.test.js +105 -0
  16. package/bin/trailhead.js +3 -3
  17. package/bin/trailhead.test.js +17 -18
  18. package/package.json +1 -1
  19. package/plugins/trailhead/.claude-plugin/plugin.json +1 -1
  20. package/plugins/trailhead/commands/auto.md +2 -2
  21. package/plugins/trailhead/hooks/lib/auto-merge.test.js +467 -0
  22. package/plugins/trailhead/hooks/lib/auto-queue.test.js +245 -0
  23. package/plugins/trailhead/hooks/lib/auto-run.test.js +318 -0
  24. package/plugins/trailhead/hooks/lib/auto-slot.test.js +278 -0
  25. package/plugins/trailhead/hooks/lib/checkout-in.test.js +173 -0
  26. package/plugins/trailhead/hooks/lib/dashboard.test.js +66 -3
  27. package/plugins/trailhead/hooks/lib/merge-ticket.test.js +394 -0
  28. package/plugins/trailhead/hooks/lib/needs-you.test.js +177 -0
  29. package/plugins/trailhead/hooks/lib/plan-review-rounds.test.js +319 -0
  30. package/plugins/trailhead/hooks/lib/statusline-context.test.js +140 -0
  31. package/plugins/trailhead/hooks/lib/uat-followup.test.js +169 -0
  32. package/plugins/trailhead/hooks/lib/unblock.test.js +207 -0
  33. package/plugins/trailhead/hooks/mod/README.md +180 -39
  34. package/plugins/trailhead/hooks/mod/auto-run-view.ts +93 -0
  35. package/plugins/trailhead/hooks/mod/auto-run.ts +165 -0
  36. package/plugins/trailhead/hooks/mod/map-pane-view.ts +150 -25
  37. package/plugins/trailhead/hooks/mod/map-pane.tsx +526 -92
  38. package/plugins/trailhead/hooks/mod/register.tsx +298 -39
  39. package/plugins/trailhead/hooks/mod/repo-pin.ts +167 -0
  40. package/plugins/trailhead/hooks/mod/session-registry.ts +65 -0
  41. package/plugins/trailhead/hooks/mod/turn-state.ts +14 -0
  42. package/plugins/trailhead/hooks/mod/whiteboard-follow.ts +98 -0
  43. package/plugins/trailhead/hooks/mod/work-state-locate.ts +191 -0
  44. package/plugins/trailhead/hooks/mod/work-state-prompt.ts +13 -15
  45. package/plugins/trailhead/hooks/mod/work-state-view.ts +21 -5
  46. package/plugins/trailhead/hooks/mod/worktree-porcelain.ts +17 -0
  47. package/plugins/trailhead/hooks/trailhead-bash-guards.js +8 -8
  48. package/plugins/trailhead/hooks/trailhead-bash-guards.test.js +22 -16
  49. package/plugins/trailhead/hooks/trailhead-labels-guard.js +231 -0
  50. package/plugins/trailhead/hooks/trailhead-labels-guard.test.js +212 -0
  51. package/plugins/trailhead/hooks/trailhead-secret-read-guard.js +296 -22
  52. package/plugins/trailhead/hooks/trailhead-secret-read-guard.test.js +253 -0
  53. package/plugins/trailhead/skills/_shared/choices.md +7 -2
  54. package/plugins/trailhead/skills/_shared/configuration-reference.md +3 -3
  55. package/plugins/trailhead/skills/_shared/substrate-commands.md +26 -4
  56. package/plugins/trailhead/skills/_shared/substrate.md +4 -1
  57. package/plugins/trailhead/skills/_shared/teamwork.md +2 -2
  58. package/plugins/trailhead/skills/_shared/techniques/acceptance-testing.md +2 -2
  59. package/plugins/trailhead/skills/_shared/techniques/grilling.md +4 -2
  60. package/plugins/trailhead/skills/_shared/techniques/plan-review.md +3 -1
  61. package/plugins/trailhead/skills/trailhead-capture/references/capture.md +1 -1
  62. package/plugins/trailhead/skills/trailhead-chart/SKILL.md +1 -1
  63. package/plugins/trailhead/skills/trailhead-chart/references/charting.md +4 -4
  64. package/plugins/trailhead/skills/trailhead-chart/references/inbox.md +2 -2
  65. package/plugins/trailhead/skills/trailhead-view/SKILL.md +1 -0
  66. package/plugins/trailhead/skills/trailhead-work/SKILL.md +21 -10
  67. package/plugins/trailhead/skills/trailhead-work/references/auto.md +70 -22
  68. package/plugins/trailhead/skills/trailhead-work/references/ticket-engines.md +6 -6
  69. package/plugins/trailhead/templates/statusline-trailhead.sh +77 -12
  70. package/plugins/trailhead/templates/trailhead-auto-merge.js +318 -0
  71. package/plugins/trailhead/templates/trailhead-auto-queue.js +186 -0
  72. package/plugins/trailhead/templates/trailhead-auto-run.js +294 -0
  73. package/plugins/trailhead/templates/trailhead-auto-slot.js +239 -0
  74. package/plugins/trailhead/templates/trailhead-checkout-in.js +88 -0
  75. package/plugins/trailhead/templates/trailhead-dashboard.js +32 -12
  76. package/plugins/trailhead/templates/trailhead-merge-ticket.js +366 -0
  77. package/plugins/trailhead/templates/trailhead-needs-you.js +193 -0
  78. package/plugins/trailhead/templates/trailhead-plan-review-rounds.js +289 -0
  79. package/plugins/trailhead/templates/trailhead-uat-followup.js +128 -0
  80. package/plugins/trailhead/templates/trailhead-unblock.js +111 -0
  81. package/plugins/trailhead/tests/auto-run-ui.test.ts +444 -0
  82. package/plugins/trailhead/tests/auto-run-view.test.ts +111 -0
  83. package/plugins/trailhead/tests/map-pane-ui.test.ts +1732 -142
  84. package/plugins/trailhead/tests/map-pane-view.test.ts +308 -14
  85. package/plugins/trailhead/tests/map-pane-write.test.ts +51 -0
  86. package/plugins/trailhead/tests/repo-pin-late-repo.test.ts +77 -0
  87. package/plugins/trailhead/tests/repo-pin.test.ts +123 -0
  88. package/plugins/trailhead/tests/session-registry.test.ts +50 -0
  89. package/plugins/trailhead/tests/whiteboard-follow.test.ts +90 -0
  90. package/plugins/trailhead/tests/work-state-locate.test.ts +33 -0
  91. package/plugins/trailhead/tests/work-state-prompt-hook.test.ts +276 -2
  92. package/plugins/trailhead/tests/work-state-prompt.test.ts +29 -35
  93. package/plugins/trailhead/tests/work-state-ui.test.ts +73 -1
  94. package/plugins/trailhead/tests/work-state-view.test.ts +19 -0
  95. package/plugins/trailhead/tests/work-state-worktree.test.ts +310 -0
  96. package/plugins/trailhead/types/index.d.ts +26 -1
@@ -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/`). Record the outcome where it belongs: a ticket's resolution, the map's `Decisions so far`, or a fresh `decision` ticket. **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 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'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
 
@@ -8,7 +8,7 @@ The user invokes with a loose idea.
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
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
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.
11
- 3. **Create the map** (`trailhead:map`): 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, 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.
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** 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.
13
13
  5. **Fire the research subagents.** For each `research` ticket created, run the **Research** technique in parallel; each agent returns its report, and the orchestrator posts it on the ticket as a `RESEARCH` comment (no branch: the report is the artifact; see the technique for the `found` / `inconclusive` / `needs_input` branches).
14
14
  6. **Offer config, then stop.** Charting is one session's work; it hand-resolves nothing. Before ending, make the **first-time config offer** (see **First-time config offer** in `../SKILL.md`): the project now has a fresh map and no `.trailhead/config.json`, so put the **two-way choice** to the user, **accept as-is** (writes `{}`) or **run `/trailhead:config`** for the guided menu, and let them pick. Name both paths; don't pre-fill the config keys and ask for an inline edit, and don't interview them key-by-key here (that's `/trailhead:config`'s job). Once the file exists it's never offered again. **No `trailhead:codebase` issue is created here**: greenfield has no code to map yet. It's created later, once the repo has substantial code (see Mode 2 step 1 and the **Codebase** row under Substrate), so at chart two of the three permanent pins (the dashboard + conventions) are filled and the codebase slot fills in later. Maps are never pinned.
@@ -26,9 +26,9 @@ Same rules as the handoff block: **name the ticket, put its number (never the ti
26
26
  ## Mode 1-bis: Adopt an existing project, `/trailhead:adopt`
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
- 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: 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".
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.
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**: 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**, 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.
@@ -18,12 +18,12 @@ This is **how you notice a fog has cleared**: you don't watch each thread; you r
18
18
 
19
19
  **Out-of-scope lines actually deferred?** Scan the map's *Out of scope* section for lines that are **deferred**, not truly ruled out: ones that **name a gate or trigger** (*gated by X*, *quando X*, *dipende da X*), carry a **this-only scope qualifier** (*per questo slice*, *for now*, *in this milestone*, not for the product), describe a **feature wanted later** (already annotated `→ future map`, or plainly still on the roadmap), or **are the gate of an existing `trailhead:seed`**. That last one is a **near-deterministic check**: cross-reference every open `trailhead:seed`'s trigger / `## Blocked by` against the *Out of scope* lines; a line some seed waits on is a **live gate**, so it cannot be "ruled out" and the cluster is **mis-wired** (the gate was left behind), re-route it. A line **decided against for the product** (won't be built, wrong approach, no "for this slice" qualifier) stays put; and a line **already properly parked** (annotated `→ future map` with its dependent seeds wired to a live trigger) is settled, skip it. Only **un-parked** deferred lines surface here. For each deferred line, **ask the user** (never decide silently) how to re-route it, `seed` / `idea` / `todo`, and **which map** it belongs to (this one, or a future / other map), then apply the **out-of-scope-vs-deferred** rules (see `../../_shared/out-of-scope.md`): **re-route the gate first**, tie its dependents to the gate's new form with a **live trigger** (never a dead "ruled out" line), and **move the whole cluster together**. Update or remove the *Out of scope* line to match where each item went (a future-map item stays as an annotated `→ future map` line; an item pulled into this map leaves *Out of scope* entirely).
20
20
 
21
- For each issue in the first two sections (incoming issues, parked fog), decide, with the user for judgement calls, on your own for clear ones:
21
+ For each issue in the first two sections (incoming issues, parked fog), propose a disposition (one of the moves below, with a one-line why) and put them to the user as one list, which they confirm, or amend item by item, before anything is written: every move labels, comments on or closes an issue someone else opened, and an out-of-scope ruling is the human's (`../../_shared/choices.md`). Act only on the user's explicit yes; never close or comment on an issue on your own judgement, and silence or an ambiguous reply leaves it untouched. The moves:
22
22
 
23
23
  - **Adopt → a ticket.** Integrate it *in place* (same issue number, so the reporter stays credited): append a trailhead block to the body: `Parent: <map name+link>`, `Adopted-from: opened by @<author>`, a reframed `## Question` (the decision/build the *map* will act on), and `## Blocked by`, **without erasing the reporter's original text above it**. Apply `trailhead:ticket` + the right type label (`bug`/`decision`/`build`/`research`/`task`) as a write-access maintainer (which also satisfies Trust & provenance), plus the map's `trailhead:map-<n>` label + native sub-issue edge (like any ticket, so it joins the right map's frontier); add `trailhead:blocked` if it has an open blocker. It lands on the frontier. Comment to the reporter that it's been picked up.
24
24
  - **Fog.** In scope but not sharp enough to ticket → **keep the issue open and label it `trailhead:fog`** (parked: still visible and tracked, but the `trailhead:*` label drops it out of the inbound query, so it won't re-surface at every triage). Then use the open issue as a **clarification space**: post a comment with the questions that would make it concrete, in the spirit of **Grilling**, but async ("what would *done* look like? which users? where does it show?"), and invite the reporter and anyone interested to refine it in the thread. The fog dissipates through that conversation as much as through other work landing; others can carry the discussion with the reporter without you in the loop. **You don't lose it**: `gh issue list --label trailhead:fog` is the durable list, and the reporter follows their own open issue. When the thread (or the wider work) has made the question sharp, **adopt it in place**: swap `trailhead:fog` for `trailhead:ticket` + the type label, add `Parent:` and the now-answerable `## Question`, and it lands on the frontier: same issue, same reporter, the clarifying discussion preserved above.
25
25
  - **Out of scope.** Beyond the destination → label `trailhead:out-of-scope`, **drop its native sub-issue edge** to reclaim a slot (the eager drop in `../../_shared/substrate-commands.md`, 404-tolerant, so an un-adopted inbound issue with no edge is a no-op), then close with the why.
26
- - **Duplicate.** Link the existing ticket and close.
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
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.
@@ -39,6 +39,7 @@ Render, by name:
39
39
  - **Not yet specified** + **parked fog**: the coarse fog with **each top-level entry numbered** (`1.`, `2.`, ...; one number per top-level bullet or line, numbered top-to-bottom in displayed order and restarting at 1 on every render, nested sub-points not numbered separately), and a count/link of open `trailhead:fog` issues. The numbers are a **render-time handle**, stable within a single render and never persisted into the map body: they exist so a specific fog point can be named by its number when offering to dissolve or graduate one. Resolve any number the user gives against the **current** render's list; if the map changed since it was rendered, re-render and confirm the point before graduating it.
40
40
  - **Out of scope**: what's been ruled out; **flag as an advisory** any line that's actually **deferred** rather than truly beyond the destination (the tells: a gate/trigger, a this-only qualifier, a feature wanted later, or the gate of a live `trailhead:seed`; see `../_shared/out-of-scope.md`), unless it's already parked (`→ future map` with dependent seeds on a live trigger, in which case say nothing). **Name any un-parked ones and suggest `/trailhead:inbox` to re-route them** (seed/idea/todo).
41
41
  - **Inbox**: a count of untriaged inbound issues, as a nudge.
42
+ - **Open across the repo**: one line, `N open tickets across all maps and the whiteboard`, the sum of the open tickets of every open map plus the whiteboard. It uses the dashboard's own count queries (the `trailhead:map-<n>` label per open map, the `trailhead:whiteboard` label), over the open maps listed with `gh issue list --label trailhead:map --state open --limit 200`; the exact recipe is in `../_shared/substrate-commands.md` (Count queries). A map or the whiteboard label is only ever applied to `trailhead:ticket` issues and a ticket carries one of them, never both, so the label scopes are the ticket count and nothing is counted twice; closed maps never count, whiteboard tickets always do. When the maps list reaches 200, or any count reaches its limit, the line reads `at least N open tickets across all maps and the whiteboard`, a lower bound worded as one.
42
43
  - **Exhaustion check**: if the map is exhausted (no open tickets and no fog left: the destination is reached), **say so and ask whether to close the map issue**, the same ask as the work handoff: on a yes, `gh issue close` it and refresh the dashboard so it drops off; on a no, leave it open. Maps aren't pinned, so there's no pin to free either way. Never close unprompted. Route any un-parked deferred *Out of scope* line first (above) so nothing wanted-later is lost. This user-confirmed close is the only change `map` makes to the map, and only on an explicit yes (the dashboard self-heal below touches only the pinned index, never the map).
43
44
 
44
45
  **Dashboard self-heal.** Before rendering, ensure the repo's pinned `trailhead:dashboard` index exists and is pinned: create + pin it only if missing, re-pin it if the pin was dropped, and otherwise do nothing (never rewrite its body on a render). This is the same self-heal the **Render the dashboard** engine documents below; the exact gh commands are in `../_shared/substrate-commands.md`.
@@ -1,15 +1,26 @@
1
1
  ---
2
2
  name: trailhead-work
3
3
  user-invocable: false
4
- description: "trailhead work cluster: the verbs that resolve tickets on the map and manage a ticket's lifecycle. Work the next frontier ticket or the one you name (work), work one ticket whole without splitting, a new whiteboard ticket or an existing one (quick), checkpoint the ticket in play (pause), resume it from its checkpoint (resume), split an oversized ticket into children (split), and run the whole frontier autonomously until a stop condition or exhaustion (auto). A cohesion cluster of the trailhead skill split, loading the shared `_shared/` core; reached through the `/trailhead:<verb>` command wrappers. Not auto-invoked: it runs only when one of these verbs is dispatched."
4
+ description: "trailhead work cluster: the verbs that resolve tickets on the map and manage a ticket's lifecycle. Work the next frontier ticket or the one you name (work), work one ticket whole without splitting, a new whiteboard ticket or an existing one (quick), checkpoint the ticket in play (pause), resume it from its checkpoint (resume), split an oversized ticket into children (split), and run a chosen scope (one map, the whiteboard, or everything) autonomously until a stop condition or exhaustion (auto). A cohesion cluster of the trailhead skill split, loading the shared `_shared/` core; reached through the `/trailhead:<verb>` command wrappers. Not auto-invoked: it runs only when one of these verbs is dispatched."
5
5
  argument-hint: "[work|quick|pause|resume|split|auto] [ticket | \"text\" | note]"
6
6
  ---
7
7
 
8
- `trailhead-work` is the **work cluster** of the trailhead skill: the verbs that resolve tickets and manage a ticket's lifecycle. `work` takes the next frontier ticket (or the one you name) and resolves it with its type's engine; `quick` works one ticket whole without splitting (a new whiteboard ticket from text, or an existing ticket, which stays where it is); `pause` checkpoints the ticket in play; `resume` picks it back up; `split` breaks an oversized ticket into children; `auto` runs the map's frontier autonomously, ticket by ticket, suspending the one-ticket-per-session rule until a stop condition or exhaustion. Everything lives on the GitHub Issues; the repo holds code only.
8
+ `trailhead-work` is the **work cluster** of the trailhead skill: the verbs that resolve tickets and manage a ticket's lifecycle. `work` takes the next frontier ticket (or the one you name) and resolves it with its type's engine; `quick` works one ticket whole without splitting (a new whiteboard ticket from text, or an existing ticket, which stays where it is); `pause` checkpoints the ticket in play; `resume` picks it back up; `split` breaks an oversized ticket into children; `auto` runs a chosen run scope autonomously, ticket by ticket, suspending the one-ticket-per-session rule until a stop condition or exhaustion. Everything lives on the GitHub Issues; the repo holds code only.
9
+
10
+ ## Move into the handed checkout (`--in=`)
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.
13
+
14
+ 1. Drop the `--in=<token>` word from the args: the verb, and its ticket number if any, are what is left.
15
+ 2. Run `node <templates>/trailhead-checkout-in.js --in <token> --cwd "$PWD"`. `<templates>` is the first of `${CLAUDE_PLUGIN_ROOT}/templates/`, `~/.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.
17
+ 4. `here` -> the session is already in the checkout: no move, carry on with the args from step 1.
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.
9
20
 
10
21
  ## Load first, in order
11
22
 
12
- Before doing anything, load `<skill base>/../_shared/load-first.md` and follow it: the shared-core load contract (the six core files, in order, then the effective config). `<skill base>` is this skill's **base directory** (Claude Code prints it as `Base directory for this skill:` when the skill loads; on another host, the directory holding this `SKILL.md`): resolve the path to an **absolute path** and open it with the **Read tool** on that absolute path. Never `cd` into a skills directory and never read a relative path from the shell (a `cd` compound followed by a read is denied by the search guard). The same rule covers every later `../_shared/...` or `references/...` path in this skill: resolve it against the base directory to an absolute path before reading. `_shared/` is a **sibling** of this cluster's own directory (at `../_shared/`), never a child of it; its absence from a listing of the cluster dir is expected, not a missing core.
23
+ Right after the move above (when there is one) and before anything else, load `<skill base>/../_shared/load-first.md` and follow it: the shared-core load contract (the six core files, in order, then the effective config). `<skill base>` is this skill's **base directory** (Claude Code prints it as `Base directory for this skill:` when the skill loads; on another host, the directory holding this `SKILL.md`): resolve the path to an **absolute path** and open it with the **Read tool** on that absolute path. Never `cd` into a skills directory and never read a relative path from the shell (a `cd` compound followed by a read is denied by the search guard). The same rule covers every later `../_shared/...` or `references/...` path in this skill: resolve it against the base directory to an absolute path before reading. `_shared/` is a **sibling** of this cluster's own directory (at `../_shared/`), never a child of it; its absence from a listing of the cluster dir is expected, not a missing core.
13
24
 
14
25
  Config is a property of the project/map, not the working directory: an isolation workspace inherits this config, never re-resolves it from the isolated copy's own root. **For an explicit `work <n>` / `quick <n>`, the named-ticket Preflight below runs *before* this config load:** never load config, or anything else, for a ticket that turns out closed.
15
26
 
@@ -17,16 +28,16 @@ Config is a property of the project/map, not the working directory: an isolation
17
28
 
18
29
  The **first word** of the arguments is the verb; the rest is the text (a ticket number, a `"text"` seed, or a pause note).
19
30
 
20
- - **`work [ticket]`** to the **Work the map** engine below (Mode 2). It dispatches the chosen ticket to its type's engine in **`references/ticket-engines.md`** (`decision` / `research` / `prototype` / `build` / `bug` / `task`). Read that file when you resolve the ticket.
31
+ - **`work [ticket]`** (after **Move into the handed checkout** when the args end with `--in=<token>`) to the **Work the map** engine below (Mode 2). It dispatches the chosen ticket to its type's engine in **`references/ticket-engines.md`** (`decision` / `research` / `prototype` / `build` / `bug` / `task`). Read that file when you resolve the ticket.
21
32
  - **`quick [ticket | "text"]`** to the **Work one ticket whole** engine below. It runs the full `build`/`bug` cycle from `references/ticket-engines.md`, without splitting (off the map only for a new `"text"` ticket; a map ticket stays on its map).
22
- - **`pause [note]`** / **`resume [ticket]`** / **`split [ticket]`** to the teamwork lifecycle: `../_shared/teamwork.md` (multi-cluster, so it lives in `_shared/`). Read it and follow it.
23
- - **`auto [map]`** to the **autonomous run** engine in **`references/auto.md`**: it runs the map's frontier ticket-by-ticket, suspending one-ticket-per-session, taking every advisory choice as delegate without the confirm gate and stopping only at the safety rail, fog, human-necessary decisions, or human interrupt. Read that file when you engage a run.
33
+ - **`pause [note]`** / **`resume [ticket]`** / **`split [ticket]`** to the teamwork lifecycle (`resume` first goes through **Move into the handed checkout** when its args end with `--in=<token>`): `../_shared/teamwork.md` (multi-cluster, so it lives in `_shared/`). Read it and follow it.
34
+ - **`auto [map]`** to the **autonomous run** engine in **`references/auto.md`**: it runs a chosen run scope (asked at start: one map, the whiteboard, or everything; `<n>` skips the ask) ticket-by-ticket, suspending one-ticket-per-session, taking every advisory choice as delegate without the confirm gate and stopping only at the safety rail, fog, human-necessary decisions, or human interrupt. Read that file when you engage a run.
24
35
 
25
36
  The cross-cluster situational references and the technique bodies these engines call live in `_shared/`; this cluster names each by its `../_shared/...` path where it needs one (teamwork, out-of-scope, the gh cookbook, the techniques).
26
37
 
27
38
  ## Preflight: reject a closed or missing named ticket (before anything else)
28
39
 
29
- **When the verb carries an explicit ticket number** (`work <n>` or `quick <n>`), the very first action, before config load, before the map load, and before any claim, scope, or isolation setup, is a single cheap state lookup of that ticket: `gh issue view <n> --json state,stateReason,title` (one call, near-free). Branch on the result:
40
+ **When the verb carries an explicit ticket number** (`work <n>` or `quick <n>`), the first action (after the move into the handed checkout, when there is one), before config load, before the map load, and before any claim, scope, or isolation setup, is a single cheap state lookup of that ticket: `gh issue view <n> --json state,stateReason,title` (one call, near-free). Branch on the result:
30
41
 
31
42
  - **Open** → proceed into the engine (config load, selection, claim, the rest).
32
43
  - **Closed, or the issue does not exist** → **stop immediately and report it by name**, with its close reason where useful (`stateReason`, and the `trailhead:out-of-scope` / `trailhead:superseded` label distinguishes resolved vs out-of-scope vs superseded). Suggest the sensible next step: for a defect in already-closed work open a **new** `bug` per the new-bug-vs-reopen rule (`references/ticket-engines.md`), otherwise pick a frontier ticket. Do **not** load config, load the map, claim, or set up any workspace: a stale or mistyped closed number must be a near-instant, near-free rejection, not a full ramp-up wasted.
@@ -38,8 +49,8 @@ This guard is the **explicit-number paths only**. Bare `work` selects from the l
38
49
  The user invokes with a map (URL or number). A ticket is optional: without one, you pick the next decision.
39
50
 
40
51
  1. Load the **map** (the low-res view, not every ticket body). **Pick which map** if the repo has several open: use the one the user named, else the active map (`.trailhead/active-map`); if neither and more than one is open, list them and ask. Set/refresh the active-map marker to the one you're working. **Named ticket:** its map becomes the active map, a whiteboard ticket leaves it alone (see **Active map** in `../_shared/multi-map.md`, under **Multiple maps on one repo**). **Codebase issue check (greenfield, offer once):** if the repo has **no `trailhead:codebase` issue** (a greenfield map never got one) and now holds **substantial code**, **don't interrupt the work to offer now**: mark the offer **pending** and surface it **at the handoff**, just before the `/clear`-first next-step block (not as a line inside it: see the **pending codebase offer** rule in `../_shared/session-handoff.md`), exactly once. On the user's yes, generate it via the **Codebase map** technique, then pin it (one of the three permanent repo pins: dashboard, codebase, conventions) and link it from the map's Notes; if the user declines, don't nag again this session. A repo that already has the issue, or is still too thin to map, skips this.
41
- 2. Choose the ticket. **If the user named a ticket, the Preflight above already confirmed it is open** (a closed, superseded, out-of-scope, or nonexistent number stopped there, before any setup ran). If the user names one, use it (a **named** ticket may be a `trailhead:whiteboard` one: `work <n>` works it whole, off the map, with the normal cycle and split allowed; use `quick <n>` for the no-split variant; a **named** ticket may also be a `trailhead:paused` one, which `work <n>` / `quick <n>` takes back). Otherwise take a frontier ticket to act on, **scoped to this map** (add its `trailhead:map-<n>` label to the frontier query when several maps are live), **never a `trailhead:paused` ticket** (it is off the frontier: `/trailhead:resume` takes it back) and **never a whiteboard ticket** (bare `work` stays on the map; the whiteboard frontier is reached via `/trailhead:whiteboard` and `quick`). Bare `work` may act on the first ticket the query returns, but that order is `gh`'s newest-first, not a priority: present several independent tickets as a **set to choose from**, not the graph's natural next step (see **Frontier order carries no priority** in `../_shared/substrate.md`). **Claim** it: assign it to yourself before any work, then re-read the assignee before starting; on a collision, stop and ask the user (see `../_shared/teamwork.md`). A claimed ticket that carries `trailhead:paused` drops it at the claim (`gh issue edit <n> --remove-label trailhead:paused`): working it is resuming it. If the ticket proves too big once you're in it, propose a **split** rather than grind, but never split on your own: present the children you would create and wait for the user's go-ahead before creating anything (same file).
42
- - **First, resolve the effective isolation mode.** The `isolation:` header defaults to **`auto`** (an absent key means `auto`; the engine never writes the key). Under **`auto`**, at work-start **before any file edit or diagnostic probe**, first run the read-only **assigned-ticket pre-check** (full rule in `../_shared/teamwork.md`): other open tickets already assigned to you **pre-bias** the ask toward `worktree`/`clone` (still asking, easy neutral), and the ask is **skipped** only when the session is already inside this ticket's own linked worktree (`trailhead/t<N>`, `N` the current ticket). Then run the remembered-answer reuse: `node <templates>/trailhead-isolation-answer.js check --repo <project root> --others <csv|none|unavailable>`; on `reuse` print the one-line note and resolve as that answer without asking, otherwise (`ask` or `skipped`) ask the user once whether they will work more than one ticket at once on this machine this session, then `node <templates>/trailhead-isolation-answer.js record --repo <project root> --answer yes|no --others <csv|none>` (sequence, 8-hour idle window and exclusions in `../_shared/teamwork.md`). **No** (a lone ticket): the mode resolves to the current checkout, exactly like `none` (the scope-free check below applies). **Yes**: resolve by repo shape, a plain repo -> `worktree`, a **superrepo** (a `.gitmodules` at the checkout root declaring at least one submodule) -> `clone`; that "yes" **is** the clone gate's answer, so the resolved `clone` path proceeds without re-asking. This asks at most once per ticket work-start (a within-session ticket switch re-asks at the new ticket's work-start), and a remembered answer may suppress even that. An autonomous run (`/trailhead:auto`) never asks and never runs the pre-check (sequential work, no concurrency) and resolves to the current checkout. If the header carries an **explicit** `none`/`worktree`/`clone`, honour it as-is (never subject it to auto's ask): explicit `clone` keeps its own gate, explicit `worktree` is eager. See `../_shared/teamwork.md`.
52
+ 2. Choose the ticket. **If the user named a ticket, the Preflight above already confirmed it is open** (a closed, superseded, out-of-scope, or nonexistent number stopped there, before any setup ran). If the user names one, use it (a **named** ticket may be a `trailhead:whiteboard` one: `work <n>` works it whole, off the map, with the normal cycle and split allowed; use `quick <n>` for the no-split variant; a **named** ticket may also be a `trailhead:paused` one, which `work <n>` / `quick <n>` takes back). Otherwise take a frontier ticket to act on, **scoped to this map** (add its `trailhead:map-<n>` label to the frontier query when several maps are live), **never a `trailhead:paused` ticket** (it is off the frontier: `/trailhead:resume` takes it back) and **never a whiteboard ticket** (bare `work` stays on the map; the whiteboard frontier is reached via `/trailhead:whiteboard` and `quick`). Bare `work` may act on the first ticket the query returns, but that order is `gh`'s newest-first, not a priority: present several independent tickets as a **set to choose from**, not the graph's natural next step (see **Frontier order carries no priority** in `../_shared/substrate.md`). **Claim** it: assign it to yourself before any work, then re-read the assignee before starting; on a collision, stop and ask the user (see `../_shared/teamwork.md`). A claimed ticket that carries `trailhead:paused` drops it at the claim (`gh issue edit <n> --remove-label trailhead:paused`): working it is resuming it. Likewise a claimed ticket that carries `trailhead:needs-you` (an autonomous run parked it for the human) has the mark cleared at the claim: `node <templates>/trailhead-needs-you.js clear --issue <n> --repo <owner/name>` (fallback by hand: `gh issue edit <n> --remove-label trailhead:needs-you`). A failed `clear` is reported to the user in one line but never blocks the claim. If the ticket proves too big once you're in it, propose a **split** rather than grind, but never split on your own: present the children you would create and wait for the user's go-ahead before creating anything (same file).
53
+ - **First, resolve the effective isolation mode.** The `isolation:` header defaults to **`auto`** (an absent key means `auto`; the engine never writes the key). Under **`auto`**, at work-start **before any file edit or diagnostic probe**, first run the read-only **assigned-ticket pre-check** (full rule in `../_shared/teamwork.md`): other open tickets already assigned to you **pre-bias** the ask toward `worktree`/`clone` (still asking, easy neutral), and the ask is **skipped** only when the session is already inside this ticket's own linked worktree (`trailhead/t<N>`, `N` the current ticket). Then run the remembered-answer reuse: `node <templates>/trailhead-isolation-answer.js check --repo <project root> --others <csv|none|unavailable>`; on `reuse` print the one-line note and resolve as that answer without asking, otherwise (`ask` or `skipped`) ask the user once whether they will work more than one ticket at once on this machine this session, then `node <templates>/trailhead-isolation-answer.js record --repo <project root> --answer yes|no --others <csv|none>` (sequence, 8-hour idle window and exclusions in `../_shared/teamwork.md`). **No** (a lone ticket): the mode resolves to the current checkout, exactly like `none` (the scope-free check below applies). **Yes**: resolve by repo shape, a plain repo -> `worktree`, a **superrepo** (a `.gitmodules` at the checkout root declaring at least one submodule) -> `clone`; that "yes" **is** the clone gate's answer, so the resolved `clone` path proceeds without re-asking. This asks at most once per ticket work-start (a within-session ticket switch re-asks at the new ticket's work-start), and a remembered answer may suppress even that. An autonomous run (`/trailhead:auto`) never asks and never runs the pre-check; it resolves to the current checkout with one parallel slot and to a worktree per ticket above one (see `references/auto.md`). If the header carries an **explicit** `none`/`worktree`/`clone`, honour it as-is (never subject it to auto's ask): explicit `clone` keeps its own gate, explicit `worktree` is eager. See `../_shared/teamwork.md`.
43
54
  - **When the resolved mode is the current checkout (`isolation: none`, or `auto` answered no), check the scope is free before starting** (this is how a submodule/package gets serialised when worktrees don't fit): scan the other in-progress tickets' `Scope:` lines, and if one **overlaps** this ticket's scope, **stop and tell the user** the scope is busy (name the ticket + holder) and offer a disjoint-scope frontier ticket or to wait. See `../_shared/teamwork.md`.
44
55
  - **Write the session-ticket marker**: at the **working root** (the checkout, worktree, or clone you'll edit in), write `.trailhead/session-ticket` as **one line** `#<n> <ticket title>`. **Keep it gitignored** (add `.trailhead/session-ticket` to that repo's `.gitignore` if absent): it's per-session local state, **never committed**. It lets an external tool show "what am I on" offline (the tracker stays the source of truth), but it is not merely that cheap hint: while it is present, the git commit-msg hook also requires every commit to carry a `Refs: #<n>` trailer matching it, so a stale marker left behind would wrongly demand that trailer on unrelated commits. Remove it at Resolve/handoff (see `../_shared/session-handoff.md`); on **Pause** it may stay (you'll return) or be cleared if you release the claim. The **`.trailhead/active-map`** marker (which map this session works) follows the same rules: gitignored, per-session, never committed; see **Multiple maps on one repo** in `../_shared/multi-map.md`.
45
56
  - **Sync the commit-msg hook**: at the working root, run `node <templates>/trailhead-commit-msg-sync.js --repo <working-root>` (where `<templates>` lives and what each status means: the commit-msg hook paragraph in `../_shared/teamwork.md`). Stay silent when it reports installed or current. On **upgraded**, give the user one result-oriented line: this clone's commit-msg hook was updated to the current rules. On **foreign**, one line that the user's own hook was left untouched and git does not enforce trailhead's rules here. On **skipped**, one line that nothing was set up here (a `core.hooksPath` without a trailhead hook, or a working root that is not a git repo) and git does not enforce trailhead's rules. On a missing script or a node error (missing means none of the three template dirs has it: try them all before concluding that), inspect the hooks dir (`git rev-parse --git-path hooks`) and report per the fallback in that same paragraph of `../_shared/teamwork.md` (trailhead hook present and executable: enforced but freshness unchecked, and only while node is on the PATH git runs with, update the install; present but not executable: git skips it, not enforced; foreign hook: untouched, not enforced; no hook: not enforced, update the install), never a blind "not enforced"; it never blocks the work.
@@ -62,7 +73,7 @@ It runs the **full engine** (the `build`/`bug` cycle in `references/ticket-engin
62
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.
63
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.)
64
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.
65
- - **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. `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.
76
+ - **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.
66
77
  - **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`.
67
78
  - **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.
68
79
 
@@ -1,13 +1,25 @@
1
1
  # The autonomous run: `auto`
2
2
 
3
- `auto` runs a map's **frontier autonomously**, ticket by ticket, until a stop condition fires or the frontier is exhausted. It is the one verb that **suspends the one-ticket-per-session rule**: while engaged it chains `build` / `bug` / `task` tickets in a single run. It is invoked explicitly, per run, as `/trailhead:auto [map]`; it **never re-engages on its own**. The run itself is an **autonomous run** (the noun); `auto` is the verb. Everything else about the engine (the substrate, the per-type engines, claiming, the session handoff) is unchanged: `auto` orchestrates the existing cycle, it does not replace it.
3
+ `auto` runs a chosen **run scope** autonomously (one map, the whiteboard, or everything), ticket by ticket, until a stop condition fires or the scope's frontier is exhausted. It is the one verb that **suspends the one-ticket-per-session rule**: while engaged it chains `build` / `bug` / `task` tickets in a single run. It is invoked explicitly, per run, as `/trailhead:auto [map]` (the optional `<n>` is the scope shortcut below); it **never re-engages on its own**. The run itself is an **autonomous run** (the noun); `auto` is the verb. Everything else about the engine (the substrate, the per-type engines, claiming, the session handoff) is unchanged: `auto` orchestrates the existing cycle, it does not replace it.
4
4
 
5
5
  Load the same shared core the cluster loads (`../../_shared/principles.md`, `substrate.md`, `session-handoff.md`, `configuration.md`, `techniques.md`, `ticket-language.md`), then this file.
6
6
 
7
7
  ## Preconditions and engagement
8
- 1. **Pick the map** exactly as bare `work` does: the map named in the argument, else the active map (`.trailhead/active-map`); if neither and more than one is open, list them and ask.
9
- 2. **Resume an interrupted ticket first.** Before scanning the frontier, check for **each** ticket you own (assigned to you) on this map that carries `trailhead:paused`, or whose latest checkpoint is a `PAUSED` comment (a ticket paused before the label existed), from a prior interrupted or stuck run. A paused ticket is off the frontier, so this step is the only way a run picks one back up, never the frontier scan. For each one, pick it back up via the **`resume`** engine (`../../_shared/teamwork.md`) and carry it to resolution first, then continue into the loop below. This is how an interrupted run is continued: the human re-invokes `/trailhead:auto`, and auto resumes the in-flight ticket before taking anything new.
10
- 3. **Load config once** from the map's project root, as every session does. `auto` reads no config of its own (there is none, per the decisions).
8
+ 1. **Pick the run scope.** The queue is built by `node <templates>/trailhead-auto-queue.js` (self-contained helper, one JSON line on stdout; `<templates>` uses the same three-dir lookup as `trailhead-work-state.js`).
9
+ - **Read the run record first.** Run `node <templates>/trailhead-auto-run.js read --repo <main-checkout>` before any ask (one JSON line). The record `<main-checkout>/.trailhead/auto-run.json` holds the run's start-up choices (scope, slots, review) and its skip and unmerged lists, so a compaction or a crash does not lose them (`sameSession` is always false on a host with no session id (Codex): there a re-invocation after a crash always gets the resume-or-fresh ask below, the accepted fallback, while a mid-run compaction never does, per the compaction rule in "The run loop"); it is written at the main checkout only and is gitignored (add `.trailhead/auto-run.json` to the repo's `.gitignore` if absent).
10
+ - `found` with `sameSession: true`: the record is this session's own run seen again after a compaction, so reuse it without asking: no scope, slot or review ask. The reuse is announced in one chat line naming the reused scope, slots and review choice, so the human sees what is being reused. An Esc/Stop interrupt does not clear the record (nothing is written at interrupt time, see "Reporting, interruption, resume"), so re-invoking `/trailhead:auto` in the same session after an Esc reuses the record with its choices, which is expected; to be asked again, re-invoke from a new session (the record then reads as another session's, which gets the resume-or-fresh ask below) or give an explicit `/trailhead:auto <n>` for a different map.
11
+ - `found` with `sameSession: false`: the record belongs to another session, either a crashed run or **a run still active elsewhere**, which the helper cannot tell apart, so ask once (`AskUserQuestion`, a numbered list without a structured question tool; no delegate option, the human's like the start-up asks) whether to resume that run with its recorded choices (only if no other session is still running it) or to start fresh (`start --replace` after the usual asks). This is the one start-up question a resumed run may pose, and it replaces the three asks. On resume, reload the record's skip and unmerged lists.
12
+ - An explicit `/trailhead:auto <n>` whose map differs from a found `map:<n>` scope (or a found non-map scope) starts a fresh run: ask as usual, then `start --replace`.
13
+ - `absent`: ask as below, then run `node <templates>/trailhead-auto-run.js start --repo <main-checkout> --scope <s> --slots <n> --review <r>` right after the last start-up ask and before anything is claimed (`<s>` is `map:<n>`, `whiteboard` or `everything`; `<r>` is the review choice token, or `skip` when the ask was skipped). `corrupt` (reported in one line) is treated like `absent`, but the write is `start --replace`, since a plain `start` would answer `exists` over the corrupt file.
14
+ - Any `skipped` result (an I/O error on any subcommand) is reported in one chat line and the run continues: a skipped `read` is treated as `absent` (ask, then `start --replace`), and a skipped `start`, `skip`, `unmerged` or `clear` leaves the record stale or missing, nothing else changes. The record is a safety net, not a gate.
15
+ - `/trailhead:auto <n>` runs map `<n>` **without asking** (the helper's `queue` validates it: a closed or non-map `<n>` is reported, not run).
16
+ - With no argument, ask **once** via `AskUserQuestion`: one map, the whiteboard, or everything, with the options and their frontier counts from `node <templates>/trailhead-auto-queue.js scopes`. When one map is chosen, then ask which map (skipped when only one map is open; the active map, if open, is the recommended option). This start-up ask is the human's: the scope is not advisory, so there is no delegate option, and it is asked before anything is claimed. On a host without a structured question tool, the same options go as a numbered list.
17
+ - **Parallel slots: 1, 2, 3 or 4.** Right after the run scope, ask **once** via `AskUserQuestion` how many tickets the run keeps in flight at once: 1, 2, 3 or 4, with **2 proposed** as the Recommended first option (a numbered list on a host without a structured question tool). `/trailhead:auto <n>` skips only the scope ask: it **still asks the slot count**. Like the scope, this ask is the human's: there is no delegate option, and it is asked before anything is claimed. With **1 slot** the run is the sequential run in the current checkout; above 1 it follows "Parallel slots" below.
18
+ - **External review rounds.** Right after the slot ask, run `node <templates>/trailhead-plan-review-rounds.js ask --repo <project-root>` (add `--host codex` on Codex; one JSON line). Any non-empty `warnings` (an `unknown` reviewer name, a bad `plan_review.rounds`) is printed in chat first, before the ask or the skip. On `skip` (plan review off, or no independent reviewer CLI installed, the host's own CLI not counting) there is no ask. On `ask`, pose one `AskUserQuestion` with the helper's `options` (at most four, **as in config** Recommended first; the full five as a numbered list, the helper's `fallback`, on a host without a structured question tool): as in config, 1, 2, 3, or until convergence (stop on a round with no progress, hard ceiling 5). On `error` (an unreadable or invalid config, a failed probe) the run reports it and hands back before any claim, like a queue helper error. Like the scope and the slot count, this ask is the human's: there is no delegate option, and it is asked before any claim, also under `/trailhead:auto <n>`. The choice holds for every `build` ticket of the run (each slot's plan step included) and overrides `plan_review.rounds` for that run; after each review round the plan step calls `node <templates>/trailhead-plan-review-rounds.js round --choice <choice> --configured <n> --history <file>` (see `../../_shared/techniques/plan-review.md`).
19
+ - **Order.** Under everything the run works one map at a time by ascending map number, the whiteboard last; inside a scope the oldest workable ticket first (lowest number), blockers respected through the frontier (`trailhead:blocked` tickets are never queued). The queue also skips every `trailhead:needs-you` ticket (one the run parked for the human, see "Parking a ticket for the human" below); the run only ever takes `next` and re-queries with `--skip` as it marks tickets needs-you or stops on them.
20
+ - **A helper error stops the run** (non-zero exit, `status: error`, unparseable output, including a truncated read): before any claim, the run reports it and hands back; mid-run, it stops taking new tickets and goes to the run-end summary. It never treats an error as a drained scope. Tickets the helper lists under `drift` (on more than one open map) are never worked and are listed in the run-end summary; `drift` can include a paused ticket you own, which is then neither resumed nor claimed, so the summary names it as your own paused work to fix by hand.
21
+ 2. **Resume an interrupted ticket first.** Before taking anything new, resume **each** ticket you own that carries `trailhead:paused` **within the scope** (the `resume` lists of `queue`'s groups), in queue order, plus any owned ticket in the scope whose latest checkpoint is a `PAUSED` comment (a ticket paused before the label existed), from a prior interrupted or stuck run. A paused ticket is off the frontier, so this step is the only way a run picks one back up, never the frontier scan. For each one, pick it back up via the **`resume`** engine (`../../_shared/teamwork.md`) and carry it to resolution first, then continue into the loop below. An owned, open, in-scope ticket named by a leftover work state file (`node <templates>/trailhead-work-state.js`) whose work state has `auto: true` (an interactive session writes `auto: false`, and a handback resets it, so `auto: true` left behind is the crash signature) and is not `trailhead:paused`, and whose latest checkpoint is not a `PAUSED` comment (no `PAUSED` at all, or engine comments after the last one, as for a ticket interrupted twice): the in-flight ticket of a crash or of a host interrupt, which leave the same trace, is resumed first too, from its latest engine comment. **Reconstruct the missing checkpoint first.** Before the normal resume, the run posts a `PAUSED` checkpoint comment on that ticket (the contents of the pause checkpoint in `../../_shared/teamwork.md`, "Pausing & resuming": what is done, the exact next step, the local state, naming the slot workspace path and branch when it has one) that notes it was reconstructed at resume after an interrupt or crash, then adds `trailhead:paused` (after the comment, so a failed comment never leaves a label without a checkpoint), and then resumes the ticket through the `resume` engine, which drops the label. It is idempotent: reconstruction is skipped only when the latest checkpoint is already a `PAUSED` comment or the label is present, so no second checkpoint is posted. Reconstruction happens when the ticket is actually resumed (when it takes a slot), so an extra waiting for a free slot is untouched until then. A slot ticket whose work state step is `resolve` is exempt from reconstruction: it is re-driven through Resolve directly (below). **Known limit:** a crash in the instant between the claim and the work-state write leaves the ticket claimed with no local trace; it stays visible as your own open ticket and is not resumed automatically (resuming every owned open ticket would grab work done by hand in another session). **Where it resumes.** Also run `node <templates>/trailhead-auto-slot.js list --repo <main-checkout>` (the slot workspaces, worktrees and sibling clones, each with its own work state). A ticket that meets the conditions above (the resume conditions are unchanged: owned, in scope, not needs-you, and paused / latest checkpoint `PAUSED`, or crashed with `auto: true` and a latest checkpoint that is not a `PAUSED` comment) and that has a slot workspace (its own work state found there, or the path its `PAUSED` checkpoint names) is resumed **in that workspace**, whatever the slot count chosen for this run. A workspace whose ticket fails the conditions (a manual branch, a needs-you ticket) is left alone. Resumed tickets take slots first; extras wait for a free slot. **An interrupted merge first.** Before resuming anything, run `node <templates>/trailhead-auto-merge.js recover --repo <main-checkout>`: `clean` means nothing was under way; `aborted` (it names the ticket) means a run's merge was cut short and has been aborted, so that ticket's merge is retried (its Resolve re-runs from the preflight); `foreign` (a human's merge, a rebase) hands the run back without touching it. **A slot ticket whose work state step is `resolve`** is re-driven through Resolve idempotently, whatever its branch state: `merge` answers `already-merged` when the merge landed before a crash, and every later Resolve move checks before acting (the resolution comment is posted only if none exists, the close only if the ticket is open, the `Decisions so far` pointer appended only if absent, and `trailhead-unblock.js` and `remove` are idempotent). This is how an interrupted run is continued: the human re-invokes `/trailhead:auto`, and auto resumes the in-flight ticket before taking anything new.
22
+ 3. **Load config once** from the project root, as every session does. `auto` reads no config of its own (there is none, per the decisions).
11
23
 
12
24
  ## The safety rail (always on, checked FIRST)
13
25
  Independent of and **above** the advisory boundary below, `auto` **never performs an irreversible or outward-facing action without an explicit human go-ahead**. Before any automatic choice handling, check the rail. The rail stops on:
@@ -17,42 +29,78 @@ Independent of and **above** the advisory boundary below, `auto` **never perform
17
29
  - a **delete / force** (`git push --force`, `rm -rf` of tracked work, a force-delete of a branch or remote ref);
18
30
  - any other **outward-facing** action (posting outside the tracker, calling an external service that mutates state).
19
31
 
20
- When the next step of a ticket would trip the rail, auto **sets that ticket aside and continues** (it does not perform the action and does not block the whole run): it hands the outward action back to the human, recorded in the run-end summary. **Atomic local commits to `main` are not rail-tripping**: they are the normal Execute and continue automatically. The rail is **hardcoded**, not a disableable toggle.
32
+ When the next step of a ticket would trip the rail, auto **marks that ticket needs-you and continues** (it does not perform the action and does not block the whole run): it hands the outward action back to the human with a `NEEDS YOU` comment naming it, recorded in the run-end summary. **Atomic local commits to `main` are not rail-tripping**: they are the normal Execute and continue automatically. **A local merge of a verified slot branch into the trunk is not rail-tripping either** (like atomic local commits, see "Integration under `git: main`"); the push stays the human's. The rail is **hardcoded**, not a disableable toggle.
21
33
 
22
- **Under a `git: pr` convention the rail bites at integration (by design).** When the repo's `trailhead:conventions` sets `git: pr` (or a ticket works in an `isolation: worktree`/`clone` branch), finishing a `build`/`bug` ticket ends in a **branch push + opening a PR**, both rail actions. So on such a repo auto drives each ticket through its **whole local cycle** (discuss / plan / execute / verify, atomic commits on the ticket's branch) and then **sets the push / PR / integration step aside for the human**, listing every ready-to-integrate branch in the run-end summary. The dependents of a ticket whose PR is not yet merged stay blocked until the human integrates, so a run on a `git: pr` repo drains less of the frontier per invocation than one on `git: main` (where a resolved ticket closes and unblocks immediately). This is the rail working as intended, not a failure; auto is simply most productive under `git: main`. **The stale-base preflight never auto-resolves.** A `conflict`, `trunk-diverged`, `unverified`, `dirty` or `sync-failed` status, or a failing preflight check, is a stop for that ticket (set aside for the human with the status named in the run-end summary); auto never resolves a merge conflict itself. **Isolation under a run:** `isolation: auto` (the default) resolves to the **current checkout** during an autonomous run without asking (the run is sequential, one ticket at a time, so there is no concurrency and no process question to put to the human); an explicit `worktree`/`clone` still applies as the conventions set it, and its branch push/PR is a rail action set aside for the human exactly as above.
34
+ **Under a `git: pr` convention the rail bites at integration (by design).** When the repo's `trailhead:conventions` sets `git: pr` (or a ticket works in an `isolation: worktree`/`clone` branch), finishing a `build`/`bug` ticket ends in a **branch push + opening a PR**, both rail actions. So on such a repo auto drives each ticket through its **whole local cycle** (discuss / plan / execute / verify, atomic commits on the ticket's branch) and then **marks the ticket needs-you for the push / PR / integration step**, listing every ready-to-integrate branch in the run-end summary. The dependents of a ticket whose PR is not yet merged stay blocked until the human integrates, so a run on a `git: pr` repo drains less of the frontier per invocation than one on `git: main` (where a resolved ticket closes and unblocks immediately). This is the rail working as intended, not a failure; auto is simply most productive under `git: main`. **The stale-base preflight never auto-resolves.** A `conflict`, `trunk-diverged`, `unverified`, `dirty` or `sync-failed` status, or a failing preflight check, is a stop for that ticket (marked needs-you with the status named in the comment and the run-end summary); auto never resolves a merge conflict itself. **Isolation under a run:** with **1 slot** (the sequential run), `isolation: auto` (the default) and `none` resolve to the **current checkout** during an autonomous run without asking (one ticket at a time, so there is no concurrency and no process question to put to the human); an explicit `worktree`/`clone` still applies as the conventions set it, and its branch push/PR is a rail action marked needs-you exactly as above. **Above 1 slot** the run gives each ticket **a worktree whatever `isolation:` says** (`none` and `auto` included), `clone` stays `clone`, and a superrepo (a `.gitmodules` declaring a submodule) escalates to `clone`; see "Parallel slots".
23
35
 
24
36
  ## The advisory boundary (the `choices.md` one-line test, no confirm gate)
25
37
  Wherever the engine would put a **forced choice** to the human, apply the one-line test in `../../_shared/choices.md`:
26
- - **Advisory / process choice** (the test's Positive side: the delegate option is offered): `auto` **takes the delegate option WITHOUT its confirm gate**. It picks the best option with the same honest rationale delegate would state, and proceeds. This is the whole of auto's autonomy: "take delegate, skip the confirm." No new confidence threshold, no separate allowlist, so the boundary can never drift from the tested convention.
27
- - **Human-necessary decision** (the test's Negative side: delegate is withheld): a `decision`-ticket convergence, a destination or scope change, an out-of-scope ruling, routing a deferred line to seed/idea/todo, closing an exhausted map, a claim collision. `auto` **sets that ticket aside and continues** (it does not decide). Likewise **fog** that needs dissolving into tickets (a human grilling act) is set aside.
38
+ - **Advisory / process choice** (the test's Positive side: the delegate option is offered): `auto` **takes the delegate option WITHOUT its confirm gate**. It picks the best option with the same honest rationale delegate would state, stress-tested first per **Stress-test the pick before offering it** in `../../_shared/choices.md` (no human checks it here, so the pass is never skipped on a non-trivial choice), records the pick with its `Weak point:` line in the ticket comment of the step the choice belongs to (the run-end summary when it belongs to no ticket), and proceeds. This is the whole of auto's autonomy: "take delegate, skip the confirm." No new confidence threshold, no separate allowlist, so the boundary can never drift from the tested convention.
39
+ - **Human-necessary decision** (the test's Negative side: delegate is withheld): a `decision`-ticket convergence, a destination or scope change, an out-of-scope ruling, routing a deferred line to seed/idea/todo, closing an exhausted map, a claim collision. `auto` **marks that ticket needs-you and continues** (it does not decide). Likewise **fog** that needs dissolving into tickets (a human grilling act) is marked needs-you.
28
40
 
29
- **The Acceptance testing drive-mode ask (AI-driven, human-driven, or skip) is advisory too, and is taken without the confirm gate**: `auto` picks AI-driven whenever any step is agent-performable; it never fakes a human-only step, so when the UAT includes one it leaves that step incomplete and sets the ticket aside, listing the human-only steps in the run-end summary. `auto` never picks skip (waiving acceptance is the human's call). **UAT setup in `auto`** runs the configured `uat.setup` only: no probe, no proposal, no save offer. A failing step (or an `invalid` config) sets the ticket aside and lists the failure in the run-end summary, like a human-only UAT step. Full protocol in `../../_shared/techniques/acceptance-testing.md`.
41
+ **The Acceptance testing drive-mode ask (AI-driven, human-driven, or skip) is advisory too, and is taken without the confirm gate**: `auto` picks AI-driven whenever any step is agent-performable, runs those steps and records them in the round's `VERIFY`, and never fakes a human-only step. A failing agent-performable step is still an acceptance finding (fix it or open a `bug`), never waved through. `auto` never picks skip (waiving acceptance is the human's call). **Human-only UAT steps no longer stall the ticket.** When human-only steps remain (or the whole UAT is human-only), the run takes the Pending-UAT guard's option (b) as its own choice: the run itself is the human's go-ahead, decided in the map's design ticket, and it is the one exception to "never open a UAT ticket without asking". First `node <templates>/trailhead-uat-followup.js find --parent <n> --repo <owner/name>`; when `reuse` names an open follow-up, reuse it and open nothing (a resume never opens a second one). Otherwise open one (a `task` on the same map, or on the whiteboard for a whiteboard ticket, or a `trailhead:seed` when the human steps are gated on an external trigger, per the routing in `acceptance-testing.md`), carrying `trailhead:uat`, the `UAT of: [<parent name>](<parent url>)` line and the human-only steps as the full numbered checklist. THEN Resolve the parent (its resolution comment names the follow-up), unblock dependents and continue. Only an open follow-up is reused (the lowest-numbered when several); closed ones never count; the run never closes duplicates. find-then-create is not atomic, which is safe because the run claims the parent before its UAT and one orchestrator makes every tracker call of the run, so two follow-ups are never opened at once. **A helper error is not a "none found"**: the run does not open a follow-up blind, it marks the parent needs-you (naming the helper error) and continues. **UAT setup in `auto`** runs the configured `uat.setup` only: no probe, no proposal, no save offer. A failing setup (or an `invalid` config) marks the ticket needs-you and lists the failure in the run-end summary. Full protocol in `../../_shared/techniques/acceptance-testing.md`.
30
42
 
31
- **A human-only step is also a stop, even though it is not a choice.** A **HITL `task`** whose next step is a checklist only the human can perform (manual plumbing the agent cannot do: signing up for a service, provisioning access, a physical action) is neither a forced choice nor fog nor a rail action, so the tests above do not catch it, but auto still **cannot execute it alone**. Auto **sets such a ticket aside and continues**, handing the precise checklist to the human in the run-end summary; it never fabricates the human's part. An **AFK `task`** the agent can drive alone chains normally.
43
+ **A human-only step is also a stop, even though it is not a choice.** A **HITL `task`** whose next step is a checklist only the human can perform (manual plumbing the agent cannot do: signing up for a service, provisioning access, a physical action) is neither a forced choice nor fog nor a rail action, so the tests above do not catch it, but auto still **cannot execute it alone**. Auto **marks such a ticket needs-you and continues**, handing the precise checklist to the human in the `NEEDS YOU` comment and the run-end summary; it never fabricates the human's part. A UAT follow-up (`trailhead:uat`) whose remaining steps are human-only is exactly such a HITL `task`: the run never works it and it joins the skip list. An **AFK `task`** the agent can drive alone chains normally.
32
44
 
33
45
  ## The run loop (a fixpoint, not one pass)
34
- Repeat until a full scan finds nothing auto-workable left:
35
- 1. **Scan the frontier** for this map (the normal frontier query, scoped to the map). Skip any ticket whose immediate next step is a human-necessary decision, fog, a HITL `task`'s human-only step, or a rail action (those are set aside for the human).
36
- 2. **Take the next auto-workable ticket**, claim it, and resolve it with its type's engine in `ticket-engines.md`. The types the agent can drive alone chain automatically: `build`, `bug`, an **AFK** `task`, and an AFK `research` (already exempt from one-ticket-per-session, so it needs no special suspension). Set aside a `decision` or `prototype` that needs the human, and a **HITL** `task` whose next step is a human-only checklist. The per-ticket engine runs its **normal configured cycle** (TDD, Code review, Goal-backward verification per config): auto is **orthogonal to `effort` and `models.*`** and changes neither. The run loop is orchestrated by the session model.
37
- 3. **On resolution**, do the normal Resolve book-keeping, then **unblock dependents** (remove `trailhead:blocked` from any dependent whose last blocker just closed) and **re-scan**. A ticket that graduates onto the frontier this way is picked up in a later pass: this is why the loop is a fixpoint, not a single sweep.
46
+ Repeat until the queue's `next` is null (nothing auto-workable left in the scope):
47
+ 1. **Query the queue**: `node <templates>/trailhead-auto-queue.js queue --scope <scope> --skip <csv>` with the run's skip list (the tickets already marked needs-you or stopped on this run), and take `next` (the lowest-numbered ticket of the first group that still has one). Do not take a ticket whose immediate next step is a human-necessary decision, fog, a HITL `task`'s human-only step, or a rail action (those are marked needs-you for the human).
48
+ 2. **Take `next`** if it is auto-workable, claim it, and resolve it with its type's engine in `ticket-engines.md`. The types the agent can drive alone chain automatically: `build`, `bug`, an **AFK** `task`, and an AFK `research` (already exempt from one-ticket-per-session, so it needs no special suspension). Mark needs-you a `decision` or `prototype` that needs the human, a **HITL** `task` whose next step is a human-only checklist, and a UAT follow-up (`trailhead:uat`) whose remaining steps are human-only: a ticket judged not auto-workable joins the skip list (and is marked needs-you for the human) and the loop re-queries, so the run leaves a map only once every frontier ticket of that map is resolved or skipped. With 1 slot the active-map marker follows the map in play (above 1 no slot writes it, see "Parallel slots"); a whiteboard ticket writes work state with `--map none` and leaves the marker alone. The per-ticket engine runs its **normal configured cycle** (TDD, Code review, Goal-backward verification per config): auto is **orthogonal to `effort` and `models.*`** and changes neither. The run loop is orchestrated by the session model.
49
+ 3. **On resolution**, do the normal Resolve book-keeping, then **unblock dependents** with `node <templates>/trailhead-unblock.js --issue <n> --repo <owner/name>` (it removes `trailhead:blocked` from any dependent whose last blocker just closed) and **re-query**. A ticket that graduates onto the frontier this way comes back through the next `queue`, so a map drains before the run moves on to the next one: this is why the loop is a fixpoint, not a single sweep.
38
50
  4. **Emit a live chat progress line** as the run advances (below).
39
51
 
40
- The run ends only when a full scan finds **nothing auto-workable** (the auto-workable frontier is exhausted), or on **human interrupt** (below). There is **no cap** on tickets, wall-clock, or tokens: a human wall never ends the run, it only shrinks the auto-workable set (auto **drains** rather than halts).
52
+ **After a context compaction mid-run, whatever the slot count** (a sequential run included), the orchestrator re-reads the record with `node <templates>/trailhead-auto-run.js read --repo <main-checkout>` and reuses its scope, slots, review choice, skip and unmerged lists whatever `sameSession` says, without asking. It knows it is mid-run from its own context (the compaction summary of an active run). The `sameSession` ask applies only at engagement, that is, a fresh `/trailhead:auto` invocation. With slots it also rebuilds the slot table (see "Parallel slots").
53
+
54
+ **Persisting the skip list.** Every time a ticket joins the run's skip list (marked needs-you, paused by the failure budget, stopped on), run `node <templates>/trailhead-auto-run.js skip --repo <main-checkout> --ticket <n>` (idempotent), so the list survives a compaction.
55
+
56
+ The run ends only when the queue finds **nothing auto-workable** (the auto-workable frontier is exhausted), or on **human interrupt** (below). There is **no cap** on tickets, wall-clock, or tokens: a human wall never ends the run, it only shrinks the auto-workable set (auto **drains** rather than halts).
57
+
58
+ ## Parallel slots
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
+
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.
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
+ - **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
+ - **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.
65
+ - **Active map.** A slot never writes the `.trailhead/active-map` marker of the main checkout (slots of different maps would overwrite each other). Each slot's map lives in its own work state (`--map`) and in the orchestrator's slot table, and every map-scoped call (frontier `queue`, `Decisions so far`, unblock) names the map explicitly. With 1 slot the marker follows the map in play, as before.
66
+ - **The slot table.** The orchestrator keeps the table (ticket, map, workspace, branch, current step) in its own context. After a context compaction it rebuilds the slot table from `trailhead-auto-slot.js list` and the slot work states, on top of the run's choices and lists restored by the general compaction rule in "The run loop".
67
+ - **Integration under `git: main`.** After Verify passes in a worktree slot, the orchestrator merges the slot branch into the local trunk (never a push), one merge at a time. First the Resolve stale-base preflight, unchanged from `../../_shared/teamwork.md`: the main checkout `--sync`, then the ticket branch `--sync` against it, with a re-review when a rebase rewrote the ticket's commits. Then `node <templates>/trailhead-auto-merge.js merge --repo <main-checkout> --ticket <n>` (one JSON line; it merges `trailhead/t<n>` into the trunk as a fast-forward when possible, else as a merge commit `chore(auto): merge trailhead/t<n>` carrying `Refs: #<n>`; another ticket's `.trailhead/session-ticket` marker in the main checkout never blocks it: a fast-forward creates no commit, and a true merge commit skips the commit-msg hook with `--no-verify` only when the marker names another ticket). The result decides the move:
68
+ - `merged` or `already-merged`: post the resolution comment naming the merge, close the ticket, append the `Decisions so far` pointer, **unblock its dependents** with `node <templates>/trailhead-unblock.js --issue <n> --repo <owner/name>`, then `node <templates>/trailhead-auto-merge.js remove --repo <main-checkout> --ticket <n>` and free the slot. Dependents that graduate onto the frontier open their slot at the new trunk SHA. The `behind` list in the result names the other slots now behind the trunk (informational).
69
+ - `conflict`: the branch stays **separate** and the workspace is kept (the helper already aborted the merge, the trunk is untouched). Right after the abort, **before any tracker call**, persist the branch: `node <templates>/trailhead-auto-run.js unmerged --repo <main-checkout> --ticket <n> --branch trailhead/t<n> --file <path> [--file <path> ...]` (one `--file` per conflicting path). Next, right after the abort, before parking, record the branch on the **merge ticket**: `node <templates>/trailhead-merge-ticket.js add --scope <map-<n>|whiteboard> --ticket <n> --file <path> [--file <path> ...] --workspace <path> --repo <owner/name>` (one `--file` per path in the merge result's `conflicts`; `--scope` is the ticket's map, or `whiteboard` for a loose ticket). Then park the ticket needs-you: the park reason names the branch, the workspace, the conflicting files and the merge ticket. Then, after the merge-ticket `add` succeeds, repeat the same `unmerged` call with `--merge-ticket <n>` (an upsert). A crash or a failed `add` in between still leaves the branch on the persisted unmerged list, with no merge ticket, which the run-end `add` retry reads. Add the branch to the run's in-memory **unmerged list** (branch, files), which the run-end summary reads, and keep the merge tickets the run touched (per scope) for the summary. The ticket stays open, so its dependents stay blocked. A helper error is reported in chat and named in the park reason; the branch stays on the unmerged list and the `add` is retried once at run end, and a retry that still fails is listed in the summary as no merge ticket written (never read an error as a success).
70
+ - `dirty`, `not-on-trunk`, `slot-dirty`, `in-progress` and `merge-failed`: park the ticket needs-you naming the status; the branch and the workspace are kept.
71
+ - `error`: hand back at once, with no further merge (an `error` can mean the main checkout was left mid-merge).
72
+ **The merge ticket.** Each unmerged branch is recorded on one merge ticket per map (or the whiteboard), opened at the first unmerged branch of that scope and updated after each later one, right when the `conflict` arises, never only at run end. It is a needs-you `task` (labels `trailhead:ticket`, `trailhead:task`, `trailhead:needs-you` and the scope label; a sub-issue of the map), so no run takes it, and it is closed by the human once every listed branch is merged or dropped. It lists each branch with its ticket, workspace and conflicting files, and suggests a merge order (oldest ticket first). The helper finds an existing one itself and reuses it; a map or the whiteboard with no unmerged branch gets no merge ticket. Held branches (`git: pr`, clone slots) and the other parked merge statuses are not on the unmerged list and open none.
73
+ Other in-flight slots are never rebased mid-step: each picks up the new trunk at its next stale-base preflight (Verify start, Resolve), which for an unpublished ticket branch syncs onto the local trunk once it is strictly ahead of `origin`.
74
+ - **A `remove` failure after the close is a cleanup error, not a park.** Any `remove` result other than `removed` (`error`, `not-merged`, `kept`) comes after the ticket is already resolved: never park or reopen it; free the slot, list the workspace and the branch in the run-end summary for the human to remove, and a later `remove` retry finishes it (`remove` is idempotent).
75
+ - **Integration stays held under `git: pr`, and for a clone slot.** Under `git: pr`, or for a clone slot (a superrepo: publishing a gitlink is a rail action), the run does not merge. A slot ticket that completes its whole local cycle on its branch is parked needs-you for integration with `node <templates>/trailhead-needs-you.js park`, the reason naming its branch and its workspace path; the branch and the workspace are **kept**, and both are listed in the run-end summary.
76
+
77
+ ## Parking a ticket for the human (the needs-you mark)
78
+ Every path above that hands a ticket to the human parks it **at once** with `node <templates>/trailhead-needs-you.js park --issue <n> --reason <text> --repo <owner/name>` (see `../../_shared/substrate-commands.md`): a human-necessary decision, fog, a HITL human-only checklist, a rail action, a stale-base stop, a failing UAT setup, a helper error on the UAT follow-up find, and a surviving blocking review concern (the same call). The trigger is the plan review not converging: when `trailhead-plan-review-rounds.js round` returns `exhausted`, `no-progress` or `ceiling`, the blocking concerns survive the rounds, and the ticket is parked after the `PLAN` and `PLAN-REVIEW` comments are posted, the reason quoting the full `text` of each concern in `surviving` (never just its key); the ticket joins the skip list (its slot frees) and the run continues. The helper secret-scans the reason, writes the `NEEDS YOU` comment (first line `NEEDS YOU`, then what the human must do), unassigns the run and adds `trailhead:needs-you` last. There are no new parking triggers: only the paths auto already hands back. The ticket joins the run's skip list.
79
+
80
+ A needs-you ticket is a mark for autonomous runs only: it stays on the frontier and the map for the human, and `/trailhead:work` still offers it. The queue skips it, and a re-invocation of `/trailhead:auto` does not reconsider it; it comes back to a run only after the human clears the label (a `work`/`quick` claim clears it, or `gh issue edit <n> --remove-label trailhead:needs-you`). A helper error from `park` is reported in chat and the ticket still joins the skip list for this run; never read it as a success.
41
81
 
42
82
  ## Per-ticket failure budget (a ticket that will not verify)
43
83
  On the green path a ticket resolves and the loop continues. On a **Verify failure**, auto does not hand back at the would-be checkpoint. Instead it **extends past the engine's normal bounded fix-and-re-review rounds**, taking the advisory "which fix" sub-choice as **delegate** (the accept / resolve-anyway decision still stays the human's, so at true exhaustion it does not silently resolve a red ticket). The per-ticket **failure budget** is: a **no-progress rule** (stop iterating once a round makes no measurable progress) **plus a hardcoded ceiling of 3 extra rounds** (tunable only in code; there is no config surface). At budget exhaustion, still red, auto **pauses the stuck ticket keeping its claim** (a `PAUSED` checkpoint) and **skips to the next frontier ticket** (skip-and-continue, not stop-the-run). A single stuck ticket never ends the run; its dependents stay blocked naturally. Skipped / stuck tickets are reported at run end. The accepted cost is stranded dependents and accumulated paused tickets.
44
84
 
85
+ ## Records as it goes
86
+ Each record the human needs is written on the tracker **when it arises**, never held for the run end: the per-ticket engine comments and resolution, the UAT follow-up (opened before the parent closes), the merge ticket (opened at the first unmerged branch of a scope and updated after each later one), the `PAUSED` checkpoint of a stuck ticket (written at budget exhaustion), and a `NEEDS YOU` comment on every needs-you ticket naming what the human must do (written the moment it is marked: for a rail action, the action to authorise; for a stale-base stop, the status), unless the ticket's latest `NEEDS YOU` comment already names the same action (the helper posts nothing new for it); a UAT follow-up (`trailhead:uat`) is exempt, since its own checklist is already the record. The chat progress line is a mirror, not a record. The run-end summary therefore only summarises records that already exist.
87
+
88
+ **Hard stop.** Every record that has arisen is already on the tracker (engine comments as each step posts them, atomic commits, the UAT follow-up, `NEEDS YOU` comments, `PAUSED` checkpoints), so a hard stop (closed terminal, crash) loses nothing that has arisen. The in-flight ticket of a crash has no `PAUSED` checkpoint, but it keeps its claim, its posted comments, its commits and the leftover work state (`auto: true`), which engagement step 2 resumes from.
89
+
45
90
  ## Reporting, interruption, resume
46
- - **In-run reporting = both.** The per-ticket engine comments (`DISCUSS`/`PLAN`/`VERIFY`/resolution, etc.) land on the Issues **as usual**, and auto **also emits a live chat progress line** as it advances (e.g. which ticket it just resolved and what it is taking next), so the human can watch a run without reading the tracker.
47
- - **Interruption = the host interrupt (Stop/Esc), the only mid-run halt.** There is no separate "stop after this ticket". Atomic commits plus ticket-boundary checkpoints make interruption **safe by construction**: on interrupt the in-flight ticket **keeps its claim and gets a `PAUSED` checkpoint** (per `../../_shared/teamwork.md`), cleanly resumable.
48
- - **Resume = the human re-invokes.** `auto` never resumes on its own. The human runs `/trailhead:auto` again; engagement step 2 above picks the interrupted ticket back up via `/trailhead:resume` before scanning, and the run's earlier set-aside (human-necessary) tickets are back on the frontier for this fresh run to reconsider. Paused or stuck tickets are not: they stay off the frontier until resumed (owned ones through step 2, the rest by `/trailhead:resume`).
91
+ - **In-run reporting = both.** The per-ticket engine comments (`DISCUSS`/`PLAN`/`VERIFY`/resolution, etc.) land on the Issues **as usual**, and auto **also emits a live chat progress line** as it advances (e.g. which ticket it just resolved and what it is taking next), so the human can watch a run without reading the tracker. With parallel slots the line names the tickets in the slots.
92
+ - **Interruption = the host interrupt (Stop/Esc), the only mid-run halt.** There is no separate "stop after this ticket". Atomic commits plus ticket-boundary checkpoints make interruption **safe by construction**: the host ends the turn at once and fires no hook, so **nothing is written at interrupt time**: no `PAUSED` checkpoint, no run-record clear, no run-end summary. On disk an Esc is the same as a crash: the in-flight tickets (every slot ticket, with parallel slots) keep their claim, their posted comments and commits, the leftover run record and their `auto: true` work states, which are the signature of both an Esc and a crash. The next `/trailhead:auto` writes the missing `PAUSED` checkpoint at resume (engagement step 2, naming the slot workspace path and branch) and resumes the ticket. A hard stop (closed terminal, crash) is covered by "Records as it goes": nothing that arose is lost, and the in-flight ticket is resumed from its work state (with slots, each slot's own work state survives in its workspace). `PAUSED` checkpoints the run itself decides (the failure budget, a stuck ticket) are still written when they arise.
93
+ - **Resume = the human re-invokes.** `auto` never resumes on its own. The human runs `/trailhead:auto` again; engagement step 2 above picks the interrupted ticket back up via `/trailhead:resume` before scanning, and the run's earlier needs-you tickets are not reconsidered: they stay marked, and the fresh run skips them until the human clears the label (a `work`/`quick` claim clears it). Paused or stuck tickets are not: they stay off the frontier until resumed (owned ones through step 2, the rest by `/trailhead:resume`).
49
94
 
50
95
  ## Run-end summary (every terminus)
51
- Whatever ends the run (frontier drained or human interrupt), auto emits **one run-end summary**, in **two places**: a **chat block** for the human, and the **same summary posted as a comment on the map issue** (the durable tracker record). The summary states:
52
- - **why it stopped** (auto-workable frontier exhausted, or human interrupt);
96
+ Whatever ends the run by itself (frontier drained), auto emits **one run-end summary**, **in full in chat** for the human, as a summary of records already on the tracker, never the only record (each line links the record it summarises: the ticket, its UAT follow-up, its checkpoint or `NEEDS YOU` comment). It is posted to the tracker too: under a one-map scope, the summary is posted as a comment on the map issue; under everything, each touched map gets a comment with **only its own part** (no other map's part, no whiteboard part); the whiteboard part stays in chat only (it has no issue), and a map the run never touched gets no comment. The summary states:
97
+ - **why it stopped** (auto-workable frontier exhausted; after a host interrupt the summary is not emitted at the interrupt, the resumed run's own summary covers it);
53
98
  - **what resolved** (the tickets auto closed this run);
54
- - **what remains**: the **set-aside** tickets (fog, `decision` convergences, out-of-scope rulings, HITL `task` checklists, human-only UAT steps, and rail actions including any **ready-to-integrate branch / PR** under `git: pr`) and **why each needs the human**, the **paused / stuck** tickets from the failure budget, and any **blocked dependents** still waiting.
99
+ - **what merged**: the tickets merged into the local trunk (under `git: main` with parallel slots), the branches **left unmerged** with their conflicting files (the run's unmerged list), the **merge tickets** the run opened or updated (one per map or whiteboard that had an unmerged branch, each linked; a branch whose `add` failed is listed as no merge ticket written), and that the local trunk is ahead of `origin` by N commits (pushing is the human's);
100
+ - **what remains**: the **needs-you tickets** (fog, `decision` convergences, out-of-scope rulings, HITL `task` checklists, and rail actions including any **ready-to-integrate branch / PR** under `git: pr`) and **why each needs the human**, the **paused / stuck** tickets from the failure budget, and any **blocked dependents** still waiting. Human-only UAT steps are not a needs-you reason: list instead the **UAT follow-ups opened this run** (what resolved can note them).
101
+
102
+ **The run record at the end.** The summary's skip and unmerged lists come from the run's in-memory skip and unmerged lists as the primary source, united with the record's (`read`), so entries lost to a compaction come back and entries whose write was `skipped` are not lost. Once the summary is posted, run `node <templates>/trailhead-auto-run.js clear --repo <main-checkout>`, then reset the work states (`--auto false`, below). Clearing first means a crash between the two leaves `auto: true` work states and no record (the next run asks again and still resumes the tickets), never a stale record with no crash evidence. A host interrupt clears nothing (it is the same on disk as a crash), so a record left behind is the signature of both an Esc and a crash mid-run, and the next `/trailhead:auto` resumes with its choices. A crash after the summary but before the `clear` leaves a record of a drained run: reusing it only re-drains the same scope (nothing left, the run ends again), announced by the reuse line.
55
103
 
56
- Before handing back, if a work state file remains (an interrupted or paused ticket), run `node <templates>/trailhead-work-state.js set --repo <working-root> --auto false`. During the run every work-state `set` adds `--auto true --auto-done <n>`, `n` being the tickets resolved so far in this run (`autoDone` in the file).
104
+ Before handing back, if a work state file remains (an interrupted or paused ticket), run `node <templates>/trailhead-work-state.js set --repo <working-root> --auto false`; with parallel slots, do it on each remaining workspace's work state: `env -u CLAUDE_CODE_SESSION_ID node <templates>/trailhead-work-state.js set --repo <workspace> --auto false`. During the run every work-state `set` adds `--auto true --auto-done <n>`, `n` being the tickets resolved so far in this run (`autoDone` in the file).
57
105
 
58
106
  Then hand back to the human with the normal session handoff framing (`../../_shared/session-handoff.md`): the human decides what to take next (dissolve fog, make a held decision, authorise a rail action, then re-invoke `/trailhead:auto` to drain more).