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, off the map, without splitting; 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.
Load first, in order
Before doing anything, read ../_shared/load-first.md and follow it: the shared-core load contract (the six core files, in order, then the effective config). _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.
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.
Routing: verb to engine
The first word of the arguments is the verb; the rest is the text (a ticket number, a "text" seed, or a pause note).
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.
quick [ticket | "text"] to the Work one ticket whole engine below. It runs the full build/bug cycle from references/ticket-engines.md, off the map, without splitting.
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.
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.
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).
Preflight: reject a closed or missing named ticket (before anything else)
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:
- Open → proceed into the engine (config load, selection, claim, the rest).
- 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.
This guard is the explicit-number paths only. Bare work selects from the live frontier (open by construction) and quick "<text>" opens a fresh ticket, so neither has a prior ticket to check and both skip it.
Work the map: work
The user invokes with a map (URL or number). A ticket is optional: without one, you pick the next decision.
- 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. See Multiple maps on one repo in ../_shared/multi-map.md. 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.
- 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). 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) 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). 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).
- 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 ask the user once whether they will work more than one ticket at once on this machine this session. 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. Because trailhead resolves one ticket per session, this asks at most once per ticket work-start (so once per session in normal work; a within-session ticket switch re-asks at the new ticket's work-start), and 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.
- 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.
- Write the session-ticket marker (a cheap hint for tooling like a statusline): 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 just lets an external tool show "what am I on" offline; the tracker stays the source of truth. 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.
- When the resolved mode is
worktree or clone (explicit, or auto answered yes), set up the isolated workspace NOW, before any file edit or diagnostic probe (not later at Execute/Fix): if the repo has submodules (a .gitmodules), isolation: worktree is escalated to clone (a superproject worktree cannot safely carry submodule state; see ../_shared/teamwork.md). For worktree (no submodules), create/enter this ticket's git worktree on a trailhead/t<n> branch for the package/path the ticket's Scope: names (a submodule-bearing repo would have escalated to clone above), e.g. git worktree add ../<repo>-t<n> -b trailhead/t<n>; for clone, make an independent working copy at ../<repo>-t<n> (branch trailhead/t<n>) by whichever is faster: git clone (add --recurse-submodules when the repo has submodules) + the install step, or a folder copy that brings node_modules (and the submodule working trees) to skip the reinstall (then verify the copy's git is independent, git -C <copy> rev-parse --absolute-git-dir resolves inside it, and the same inside every submodule, else commits leak back to the original); a clone with submodules is provisioned via git submodule update --init --recursive (nested included), each scoped submodule pinned at the superproject's recorded commit. If this clone was reached via auto's work-start ask (answered yes), that ask already served as the clone gate: do not re-ask, proceed to set up the clone. Under an explicit isolation: clone, run the gate as follows. Because clone is heavy (a full clone + an install), gate it, every time, before cloning: ask the user whether they expect to work more than one ticket at once on this machine this session. If no, skip the clone and work this ticket in the current checkout (isolation only exists to stop concurrent workspaces from colliding; a lone ticket has nothing to collide with, so the shared-checkout caveats apply and that is fine, and git: still decides the branch as usual); if yes, clone as above. The gate is clone-only: worktree is cheap, so set it up without asking. Do every subsequent step in whatever workspace you ended up in. This is the whole point of isolation: if you diagnose and edit in the shared checkout "just to start" while a concurrent workspace exists, you are already colliding. Set the workspace up silently (the clone gate-question is the one exception you voice); it's undone at Resolve when the branch integrates and the worktree/clone is removed. See ../_shared/teamwork.md.
- Brief the ticket and confirm scope, before the engine runs. With the ticket claimed, read it and give the user a brief, result-oriented summary of what resolving it will do (its goal and the shape of the work, not a plan yet), then ask whether they want to change anything. This is a HITL confirm gate: on assent, proceed to the engine. If the user asks for a change, hold it against the ticket. A change consistent with the ticket is folded in and the engine proceeds. A change that diverges from the ticket is surfaced as a mismatch, and the user is asked, never forced, whether to (a) work the ticket as-is, or (b) open a separate new ticket for the divergent request. Offer both and decide neither: open the separate ticket only if the user chooses it (via Scope that surfaces while working a ticket, served by the capture cluster, the same capture-or-split path used for scope that surfaces mid-work), never automatically. Under an autonomous run (
auto) this is a process confirm gate, so it follows the advisory boundary in references/auto.md: auto proceeds (takes the delegate option) without stopping to brief.
- Resolve it with its type's engine in
references/ticket-engines.md, zoom as needed: fetch the full body of related/closed tickets on demand. If in doubt on a decision ticket, run Grilling + Domain vocabulary. If in doubt on a build, stop and ask (see the Discuss step): never auto-grill.
- Record the resolution: a comment with the answer,
gh issue close, add the pointer to Decisions so far. Then unblock dependents: for every ticket this one was blocking, if it was the last open blocker, remove its trailhead:blocked label so it graduates onto the frontier.
- Add newly-surfaced tickets (create-then-wire, labelling blocked ones
trailhead:blocked); graduate the fog that became specifiable, clearing the patch from Not yet specified. If the answer reveals a ticket sits beyond the destination, apply the out-of-scope vs deferred test (../_shared/out-of-scope.md): if it's really deferred on something outside the map, ask the user to route it to a seed/idea/todo; only if it's truly beyond the destination rule it out of scope (label, then drop its native sub-issue edge to reclaim a slot, then close: the eager drop in ../_shared/substrate-commands.md, 404-tolerant, references intact) instead of resolving it. If the decision invalidates other parts of the map, update or delete them.
- Hand off. Close the session with the Session handoff ritual (
../_shared/session-handoff.md): confirm the ticket is resolved by name, then the scannable next-step block with /clear first (never the next command without it), naming the next frontier ticket and giving its number (or, when several mutually independent tickets sit on the frontier, presenting them as a set to choose from rather than crowning the first-listed), or /trailhead:map if the frontier is empty/ambiguous. This holds in any later follow-up too, not only at the moment of resolution. If this resolution leaves the map exhausted (no open tickets and no fog left: the destination is reached), say so; but before closing it out, if Out of scope holds any deferred line (apply the out-of-scope-vs-deferred tells in ../_shared/out-of-scope.md), stop and ask the user what to do with each (a seed gated on its trigger, an idea, or a todo, in this or another map), so nothing wanted-later is lost when the map goes quiet. Only once the deferred items are routed (or the user says leave them), ask the user whether to close the map issue now that the destination is reached: gh issue close it on a yes (it stays the record), leave it open on a no; never close a map unprompted. Either way refresh the dashboard so an exhausted/closed map drops off (a structural event). Maps aren't pinned, so there's no pin to free; the dashboard, codebase, and conventions stay pinned regardless.
Work one ticket whole, off the map: quick
A streamlined way to just get one ticket done, without map ceremony. quick "<text>" opens a whiteboard ticket (a build, or a bug when the text is clearly a defect) and works it end to end now; quick <n> works an existing ticket (whiteboard or map) the same way. For quick <n>, the Preflight above runs first: a closed, out-of-scope, superseded, or nonexistent ticket stops immediately, before config, claim, or isolation setup. Claim it first; still one ticket per session. Bare /trailhead:quick (no argument, e.g. offered in a Session handoff) asks for the piece to work, like a capture missing its text, then proceeds as quick "<text>".
It runs the full engine (the build/bug cycle in references/ticket-engines.md: Discuss → Plan → Execute → Verify → Resolve), differing from work on these points:
- Briefs and confirms scope first, exactly as Work the map step 3. With the ticket claimed, summarise what resolving it will do and ask whether to change anything, applying the same consistency rule (a divergent request is offered as work-as-is vs a separate new ticket, opened only if the user chooses it, never forced; via Scope that surfaces while working a ticket). A just-written
quick "<text>" needs only a one-line confirmation; quick <n> on an existing ticket gets the full brief.
- 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.
- 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.)
- 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.
- Same isolation setup as
work. 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; no active-map marker, though: a whiteboard ticket has no map). Under auto it runs the same work-start concurrency ask 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.
- 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 (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.
- Honours
config.effort. quick is the natural home for the cost-aware path: under effort: lean 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), at the start before planning. 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. The correctness spine (atomic Refs: commits, tests/criterion, post-commit self-check, Resolve) is never skipped. Under effort: standard (default), and on any non-yes answer, the full cycle runs, exactly as today.
Type follows the ticket: quick "<text>" defaults to build (bug if a defect); quick <n> uses the existing type and its matching engine.
Ensure the whiteboard label exists before applying it (a repo that adopted trailhead before the whiteboard existed never created it): gh label create trailhead:whiteboard --color C5DEF5 --description "Map-less ticket: lives on the whiteboard" 2>/dev/null || true. See The whiteboard in ../_shared/substrate.md.
Pause, resume, split
pause [note] / resume [ticket]: checkpoint the ticket in play so it can be resumed later, and pick it back up from its latest PAUSED checkpoint. Read ../_shared/teamwork.md (Pausing & resuming) and follow it: the PAUSED checkpoint contents, release-vs-keep the claim, and (under isolation: worktree/clone) committing the work-in-progress in the ticket's isolated workspace and recording its path in the checkpoint, then re-entering it on resume.
split [ticket]: split the named (or in-play) ticket into children that replace it. Read ../_shared/teamwork.md (Splitting a ticket) and follow it: create the children (each wired to the map and to the split origin), re-point the original's blockers onto the right children, then supersede & close the original (trailhead:superseded).
1---2name: trailhead-work3description: 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 off the map without splitting (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---56`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, off the map, without splitting; `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.78## Load first, in order910Before doing anything, read `../_shared/load-first.md` and follow it: the shared-core load contract (the six core files, in order, then the effective config). `_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.1112Config 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.1314## Routing: verb to engine1516The **first word** of the arguments is the verb; the rest is the text (a ticket number, a `"text"` seed, or a pause note).1718- **`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.19- **`quick [ticket | "text"]`** to the **Work one ticket whole** engine below. It runs the full `build`/`bug` cycle from `references/ticket-engines.md`, off the map, without splitting.20- **`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.21- **`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.2223The 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).2425## Preflight: reject a closed or missing named ticket (before anything else)2627**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:2829- **Open** → proceed into the engine (config load, selection, claim, the rest).30- **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.3132This guard is the **explicit-number paths only**. Bare `work` selects from the live frontier (open by construction) and `quick "<text>"` opens a fresh ticket, so neither has a prior ticket to check and both skip it.3334## Work the map: `work`3536The user invokes with a map (URL or number). A ticket is optional: without one, you pick the next decision.37381. 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. See **Multiple maps on one repo** in `../_shared/multi-map.md`. **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.392. 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). 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) 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`). 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).40 - **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 ask the user once whether they will work more than one ticket at once on this machine this session. **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. Because trailhead resolves one ticket per session, this asks at most once per ticket work-start (so once per session in normal work; a within-session ticket switch re-asks at the new ticket's work-start), and 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`.41 - **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`.42 - **Write the session-ticket marker** (a cheap hint for tooling like a statusline): 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 just lets an external tool show "what am I on" offline; the tracker stays the source of truth. 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`.43 - **When the resolved mode is `worktree` or `clone` (explicit, or `auto` answered yes), set up the isolated workspace NOW, before any file edit or diagnostic probe** (not later at Execute/Fix): **if the repo has submodules (a `.gitmodules`), `isolation: worktree` is escalated to `clone`** (a superproject worktree cannot safely carry submodule state; see `../_shared/teamwork.md`). For `worktree` (no submodules), create/enter this ticket's `git worktree` on a `trailhead/t<n>` branch for the package/path the ticket's `Scope:` names (a submodule-bearing repo would have escalated to `clone` above), e.g. `git worktree add ../<repo>-t<n> -b trailhead/t<n>`; for `clone`, make an independent working copy at `../<repo>-t<n>` (branch `trailhead/t<n>`) by whichever is faster: `git clone` (add `--recurse-submodules` when the repo has submodules) + the install step, or a **folder copy that brings `node_modules`** (and the submodule working trees) to skip the reinstall (then verify the copy's git is independent, `git -C <copy> rev-parse --absolute-git-dir` resolves inside it, and the same inside every submodule, else commits leak back to the original); a clone with submodules is provisioned via `git submodule update --init --recursive` (nested included), each scoped submodule pinned at the superproject's recorded commit. **If this `clone` was reached via `auto`'s work-start ask (answered yes), that ask already served as the clone gate: do not re-ask, proceed to set up the clone. Under an explicit `isolation: clone`, run the gate as follows.** **Because `clone` is heavy (a full clone + an install), gate it, every time, before cloning: ask the user whether they expect to work more than one ticket at once on this machine this session.** If **no**, **skip the clone** and work this ticket in the current checkout (isolation only exists to stop concurrent workspaces from colliding; a lone ticket has nothing to collide with, so the shared-checkout caveats apply and that is fine, and `git:` still decides the branch as usual); if **yes**, clone as above. The gate is **`clone`-only**: `worktree` is cheap, so set it up without asking. Do every subsequent step in whatever workspace you ended up in. This is the whole point of isolation: if you diagnose and edit in the shared checkout "just to start" while a concurrent workspace exists, you are already colliding. Set the workspace up silently (the `clone` gate-question is the one exception you voice); it's undone at Resolve when the branch integrates and the worktree/clone is removed. See `../_shared/teamwork.md`.443. **Brief the ticket and confirm scope, before the engine runs.** With the ticket claimed, read it and give the user a **brief, result-oriented summary of what resolving it will do** (its goal and the shape of the work, not a plan yet), then **ask whether they want to change anything**. This is a HITL confirm gate: on assent, proceed to the engine. **If the user asks for a change, hold it against the ticket.** A change **consistent** with the ticket is folded in and the engine proceeds. A change that **diverges** from the ticket is surfaced as a mismatch, and the user is **asked, never forced**, whether to (a) work the ticket **as-is**, or (b) open a **separate new ticket** for the divergent request. Offer both and decide neither: open the separate ticket **only if the user chooses it** (via **Scope that surfaces while working a ticket**, served by the capture cluster, the same capture-or-split path used for scope that surfaces mid-work), never automatically. **Under an autonomous run (`auto`) this is a process confirm gate**, so it follows the advisory boundary in `references/auto.md`: `auto` proceeds (takes the delegate option) without stopping to brief.454. Resolve it with its type's engine in **`references/ticket-engines.md`**, **zoom as needed**: fetch the full body of related/closed tickets on demand. If in doubt on a `decision` ticket, run **Grilling** + **Domain vocabulary**. If in doubt on a `build`, **stop and ask** (see the Discuss step): never auto-grill.465. Record the resolution: a comment with the answer, `gh issue close`, add the pointer to *Decisions so far*. Then **unblock dependents**: for every ticket this one was blocking, if it was the last open blocker, remove its `trailhead:blocked` label so it graduates onto the frontier.476. Add newly-surfaced tickets (create-then-wire, labelling blocked ones `trailhead:blocked`); graduate the fog that became specifiable, clearing the patch from *Not yet specified*. If the answer reveals a ticket sits beyond the destination, apply the **out-of-scope vs deferred** test (`../_shared/out-of-scope.md`): if it's really deferred on something outside the map, ask the user to route it to a `seed`/`idea`/`todo`; only if it's truly beyond the destination rule it **out of scope** (label, then **drop its native sub-issue edge** to reclaim a slot, then close: the eager drop in `../_shared/substrate-commands.md`, 404-tolerant, references intact) instead of resolving it. If the decision invalidates other parts of the map, update or delete them.487. **Hand off.** Close the session with the **Session handoff** ritual (`../_shared/session-handoff.md`): confirm the ticket is resolved by name, then the scannable **next-step block with `/clear` first** (never the next command without it), naming the next frontier ticket and giving its number (or, when several mutually independent tickets sit on the frontier, presenting them as a **set to choose from** rather than crowning the first-listed), or `/trailhead:map` if the frontier is empty/ambiguous. This holds in any later follow-up too, not only at the moment of resolution. **If this resolution leaves the map exhausted** (no open tickets and no fog left: the destination is reached), say so; but **before closing it out, if *Out of scope* holds any deferred line** (apply the out-of-scope-vs-deferred tells in `../_shared/out-of-scope.md`), **stop and ask the user what to do with each** (a `seed` gated on its trigger, an `idea`, or a `todo`, in this or another map), so nothing wanted-later is lost when the map goes quiet. Only once the deferred items are routed (or the user says leave them), **ask the user whether to close the map issue** now that the destination is reached: `gh issue close` it on a yes (it stays the record), leave it open on a no; never close a map unprompted. Either way **refresh the dashboard** so an exhausted/closed map drops off (a structural event). Maps aren't pinned, so there's no pin to free; the dashboard, codebase, and conventions stay pinned regardless.4950## Work one ticket whole, off the map: `quick`5152A streamlined way to just get one ticket done, without map ceremony. `quick "<text>"` opens a **whiteboard** ticket (a `build`, or a `bug` when the text is clearly a defect) and works it end to end now; `quick <n>` works an **existing** ticket (whiteboard or map) the same way. For `quick <n>`, the **Preflight** above runs first: a closed, out-of-scope, superseded, or nonexistent ticket stops immediately, before config, claim, or isolation setup. Claim it first; still one ticket per session. **Bare `/trailhead:quick`** (no argument, e.g. offered in a Session handoff) asks for the piece to work, like a capture missing its text, then proceeds as `quick "<text>"`.5354It runs the **full engine** (the `build`/`bug` cycle in `references/ticket-engines.md`: Discuss → Plan → Execute → Verify → Resolve), differing from `work` on these points:55- **Briefs and confirms scope first, exactly as Work the map step 3.** With the ticket claimed, summarise what resolving it will do and ask whether to change anything, applying the same consistency rule (a divergent request is offered as work-as-is vs a separate new ticket, opened only if the user chooses it, never forced; via **Scope that surfaces while working a ticket**). A just-written `quick "<text>"` needs only a one-line confirmation; `quick <n>` on an existing ticket gets the full brief.56- **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.57- **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.)58- **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.59- **Same isolation setup as `work`.** `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; no active-map marker, though: a whiteboard ticket has no map). Under `auto` it runs the same work-start concurrency ask **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.60- **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** (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`.61- **Honours `config.effort`.** `quick` is the natural home for the cost-aware path: under `effort: lean` 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`), at the start before planning. 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**. The correctness spine (atomic `Refs:` commits, tests/criterion, post-commit self-check, Resolve) is never skipped. Under `effort: standard` (default), and on any non-yes answer, the full cycle runs, exactly as today.6263Type follows the ticket: `quick "<text>"` defaults to `build` (`bug` if a defect); `quick <n>` uses the existing type and its matching engine.6465**Ensure the whiteboard label exists before applying it** (a repo that adopted trailhead before the whiteboard existed never created it): `gh label create trailhead:whiteboard --color C5DEF5 --description "Map-less ticket: lives on the whiteboard" 2>/dev/null || true`. See **The whiteboard** in `../_shared/substrate.md`.6667## Pause, resume, split6869- **`pause [note]`** / **`resume [ticket]`**: checkpoint the ticket in play so it can be resumed later, and pick it back up from its latest `PAUSED` checkpoint. Read **`../_shared/teamwork.md`** (Pausing & resuming) and follow it: the `PAUSED` checkpoint contents, release-vs-keep the claim, and (under `isolation: worktree`/`clone`) committing the work-in-progress in the ticket's isolated workspace and recording its path in the checkpoint, then re-entering it on resume.70- **`split [ticket]`**: split the named (or in-play) ticket into children that replace it. Read **`../_shared/teamwork.md`** (Splitting a ticket) and follow it: create the children (each wired to the map and to the split origin), re-point the original's blockers onto the right children, then supersede & close the original (`trailhead:superseded`).