@marcomigozzi/trailhead 0.4.0 → 0.5.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 (65) hide show
  1. package/README.it.md +19 -14
  2. package/README.md +20 -15
  3. package/bin/lib/codex-projection.js +23 -2
  4. package/bin/lib/codex-projection.test.js +14 -0
  5. package/bin/trailhead.js +99 -36
  6. package/bin/trailhead.test.js +130 -5
  7. package/package.json +1 -1
  8. package/plugins/trailhead/.claude-plugin/plugin.json +7 -2
  9. package/plugins/trailhead/commands/adopt.md +1 -1
  10. package/plugins/trailhead/commands/bug.md +1 -1
  11. package/plugins/trailhead/commands/config.md +1 -1
  12. package/plugins/trailhead/commands/dashboard.md +1 -1
  13. package/plugins/trailhead/commands/grill.md +1 -1
  14. package/plugins/trailhead/commands/idea.md +1 -1
  15. package/plugins/trailhead/commands/inbox.md +1 -1
  16. package/plugins/trailhead/commands/map.md +1 -1
  17. package/plugins/trailhead/commands/new.md +1 -1
  18. package/plugins/trailhead/commands/note.md +1 -1
  19. package/plugins/trailhead/commands/pause.md +1 -1
  20. package/plugins/trailhead/commands/quick.md +3 -1
  21. package/plugins/trailhead/commands/resume.md +1 -1
  22. package/plugins/trailhead/commands/seed.md +1 -1
  23. package/plugins/trailhead/commands/split.md +1 -1
  24. package/plugins/trailhead/commands/ticket.md +1 -1
  25. package/plugins/trailhead/commands/todo.md +1 -1
  26. package/plugins/trailhead/commands/update.md +1 -1
  27. package/plugins/trailhead/commands/whiteboard.md +1 -1
  28. package/plugins/trailhead/commands/work.md +1 -1
  29. package/plugins/trailhead/hooks/trailhead-commit-guard.js +1 -1
  30. package/plugins/trailhead/hooks/trailhead-secret-guard.js +19 -4
  31. package/plugins/trailhead/hooks/trailhead-secret-guard.test.js +41 -1
  32. package/plugins/trailhead/skills/{trailhead/references/configuration.md → _shared/configuration-reference.md} +9 -3
  33. package/plugins/trailhead/skills/_shared/configuration.md +8 -0
  34. package/plugins/trailhead/skills/_shared/multi-map.md +27 -0
  35. package/plugins/trailhead/skills/_shared/out-of-scope.md +11 -0
  36. package/plugins/trailhead/skills/_shared/principles.md +8 -0
  37. package/plugins/trailhead/skills/_shared/session-handoff.md +22 -0
  38. package/plugins/trailhead/skills/_shared/substrate-commands.md +159 -0
  39. package/plugins/trailhead/skills/_shared/substrate.md +35 -0
  40. package/plugins/trailhead/skills/{trailhead/references → _shared}/teamwork.md +5 -4
  41. package/plugins/trailhead/skills/{trailhead/references → _shared}/techniques/acceptance-testing.md +3 -2
  42. package/plugins/trailhead/skills/{trailhead/references → _shared}/techniques/code-review.md +5 -1
  43. package/plugins/trailhead/skills/{trailhead/references → _shared}/techniques/plan-review.md +4 -7
  44. package/plugins/trailhead/skills/_shared/techniques/prototype.md +36 -0
  45. package/plugins/trailhead/skills/_shared/techniques.md +20 -0
  46. package/plugins/trailhead/skills/_shared/ticket-anatomy.md +32 -0
  47. package/plugins/trailhead/skills/_shared/ticket-language.md +3 -0
  48. package/plugins/trailhead/skills/trailhead/SKILL.md +25 -452
  49. package/plugins/trailhead/skills/trailhead-capture/SKILL.md +28 -0
  50. package/plugins/trailhead/skills/{trailhead → trailhead-capture}/references/capture.md +4 -4
  51. package/plugins/trailhead/skills/trailhead-chart/SKILL.md +51 -0
  52. package/plugins/trailhead/skills/trailhead-chart/references/charting.md +24 -0
  53. package/plugins/trailhead/skills/{trailhead → trailhead-chart}/references/inbox.md +1 -1
  54. package/plugins/trailhead/skills/trailhead-manage/SKILL.md +29 -0
  55. package/plugins/trailhead/skills/trailhead-view/SKILL.md +112 -0
  56. package/plugins/trailhead/skills/trailhead-work/SKILL.md +65 -0
  57. package/plugins/trailhead/skills/trailhead-work/references/ticket-engines.md +52 -0
  58. package/plugins/trailhead/skills/trailhead/references/techniques/prototype.md +0 -18
  59. /package/plugins/trailhead/skills/{trailhead/references → _shared}/techniques/codebase-map.md +0 -0
  60. /package/plugins/trailhead/skills/{trailhead/references → _shared}/techniques/debug.md +0 -0
  61. /package/plugins/trailhead/skills/{trailhead/references → _shared}/techniques/domain-vocabulary.md +0 -0
  62. /package/plugins/trailhead/skills/{trailhead/references → _shared}/techniques/grilling.md +0 -0
  63. /package/plugins/trailhead/skills/{trailhead/references → _shared}/techniques/research.md +0 -0
  64. /package/plugins/trailhead/skills/{trailhead/references → _shared}/techniques/tdd.md +0 -0
  65. /package/plugins/trailhead/skills/{trailhead → trailhead-manage}/references/updating.md +0 -0
@@ -0,0 +1,28 @@
1
+ ---
2
+ name: trailhead-capture
3
+ description: "trailhead capture cluster: the zero-friction captures that record work without breaking flow. Capture a bug ticket (bug), a small build ticket you will do now (todo), a fog idea (idea), a trigger-gated seed (seed), a verbatim note (note). A cohesion cluster of the trailhead skill split, loading the shared `_shared/` core; reached through the `/trailhead:<verb>` command wrappers and the bare `/trailhead` dispatcher. Not auto-invoked: it runs only when one of these verbs is dispatched."
4
+ argument-hint: "[bug|todo|idea|seed|note] [--of <ticket>] <text>"
5
+ ---
6
+
7
+ `trailhead-capture` is the **capture cluster** of the trailhead skill: the zero-friction captures that record work without breaking flow. `bug` captures a bug ticket; `todo` captures a small build ticket you will do now; `idea` captures a fog idea; `seed` captures a trigger-gated seed; `note` captures a verbatim note. Everything lives on the GitHub Issues; the repo holds code only.
8
+
9
+ ## Load first, in order
10
+
11
+ Before doing anything, read the shared core (paths relative to this file):
12
+
13
+ 1. `../_shared/principles.md`: refer by name, result-oriented output, no em-dashes, git per conventions, one ticket per session.
14
+ 2. `../_shared/ticket-language.md`: write all Issue prose and commit bodies in `config.ticket.language` (default `en`), independent of the chat language.
15
+ 3. `../_shared/substrate.md`: the GitHub-Issues model: labels, the frontier query, the map / ticket / dashboard / whiteboard anatomy, and the base-command cookbook pointer.
16
+ 4. `../_shared/session-handoff.md`: how every resolution closes (`/clear` first, then the next command).
17
+ 5. `../_shared/configuration.md`: the three config layers and the load contract.
18
+ 6. `../_shared/techniques.md`: the technique index and the subagent-type rule.
19
+
20
+ Load the **effective config** (per `../_shared/configuration.md`) at session start, from the map's project root (`.trailhead/config.json` plus the global file). A capture writes at most one issue; no isolation workspace is set up.
21
+
22
+ ## Routing: verb to engine
23
+
24
+ The **first word** of the arguments is the verb (`bug`, `todo`, `idea`, `seed`, or `note`); the rest is the text (or `--of <ticket> <text>` for `bug`).
25
+
26
+ - **`bug`** / **`todo`** / **`idea`** / **`seed`** / **`note`** to **`references/capture.md`**: the full per-verb protocol and the note/idea/seed/todo spectrum. Read that file and follow it.
27
+
28
+ The cross-cluster situational references and technique bodies these captures call live in `_shared/`; this cluster names each by its `../_shared/...` path where it needs one.
@@ -1,12 +1,12 @@
1
1
  # Capture: the verbs in detail
2
2
 
3
- Capture without breaking flow, but with the **tracker** as destination, not scattered files. The verbs are in the **Capture** table in SKILL.md's Commands section; here is the detail of each. One capture = one action + one confirmation line; it resolves nothing.
3
+ Capture without breaking flow, but with the **tracker** as destination, not scattered files. The verbs are named in this cluster's `../SKILL.md`; here is the detail of each. One capture = one action + one confirmation line; it resolves nothing.
4
4
 
5
- **Confirmation line.** Confirm the capture by the ticket's name (not a bare `#number`) and where it landed. **If you point at working it later, lead with `/clear`**: the confirmation is a place you point at the next ticket, so the [Session handoff](../SKILL.md)'s `/clear`-first rule applies — a compact, future-conditional one-liner is fine (a capture is not a resolution, so no full labelled block), along the lines of *"to work it later: `/clear`, then `/trailhead:work 45`"* (in the user's conversation language). Never a bare `/trailhead:work 45` with no `/clear`.
5
+ **Confirmation line.** Confirm the capture by the ticket's name (not a bare `#number`) and where it landed. **If you point at working it later, lead with `/clear`**: the confirmation is a place you point at the next ticket, so the [Session handoff](../../_shared/session-handoff.md)'s `/clear`-first rule applies: a compact, future-conditional one-liner is fine (a capture is not a resolution, so no full labelled block), along the lines of *"to work it later: `/clear`, then `/trailhead:work 45`"* (in the user's conversation language). Never a bare `/trailhead:work 45` with no `/clear`.
6
6
 
7
- **Language (do not skip):** the captured ticket (title + body) is written in `config.ticket.language` (default `en`), **regardless of the language you are chatting in**. A capture fired in Italian still lands as an English ticket. This is the easiest place to slip, because a capture echoes the user's one-line phrasing; translate it, do not mirror it. See SKILL.md's *Ticket language* rule.
7
+ **Language (do not skip):** the captured ticket (title + body) is written in `config.ticket.language` (default `en`), **regardless of the language you are chatting in**. A capture fired in Italian still lands as an English ticket. This is the easiest place to slip, because a capture echoes the user's one-line phrasing; translate it, do not mirror it. See the *Ticket language* rule in `../../_shared/ticket-language.md`.
8
8
 
9
- **Map or whiteboard:** a capture that produces a **ticket** (`todo`, `bug`, `seed`, and `idea` when it is sharp enough) must land on a **map** or on the **whiteboard** (map-less). **When a map is open, ask the user which** before creating it (active map vs whiteboard; several maps open -> the ask also picks the map). Whiteboard tickets get the `trailhead:whiteboard` label and **no** map label or `Parent:`; map tickets get the map's `trailhead:map-<n>` label + native sub-issue edge. **No map open at all -> whiteboard, no ask** (nowhere else to go). `note` and a non-sharp `idea` stay map-fog and skip this; with no open map they fall back to the global notes location (`$HOME/.claude/notes/`), or a now-sharp `idea` becomes a whiteboard ticket. **When a capture creates a whiteboard ticket, refresh the pinned dashboard** afterwards (a structural event: the whiteboard surface the dashboard indexes grew, and a whiteboard ticket has no native progress bar, so the dashboard is the only place it shows); a capture landing on a *map* does not (native sub-issue progress bars track map tickets). See SKILL.md's *Capture*, *The whiteboard*, and the dashboard freshness rule.
9
+ **Map or whiteboard:** a capture that produces a **ticket** (`todo`, `bug`, `seed`, and `idea` when it is sharp enough) must land on a **map** or on the **whiteboard** (map-less). **When a map is open, ask the user which** before creating it (active map vs whiteboard; several maps open -> the ask also picks the map). **Map eligibility is "is the map open", not "is it still in progress".** An **exhausted** map (destination reached) that is **still open** stays a valid capture target: a capture thematically tied to it is offered that map on the which-map ask, never silently dropped to the whiteboard just because the destination was reached (that would sever the thematic link). The exhausted state is a reason to *offer to close the map* (the dashboard's `exhausted · closeable` flag, see the dashboard freshness rule in `../../_shared/substrate.md`), never a reason to drop it as a capture target. Only a **closed** map is no longer eligible: then the whiteboard is the right fallback. Whiteboard tickets get the `trailhead:whiteboard` label and **no** map label or `Parent:`; map tickets get the map's `trailhead:map-<n>` label + native sub-issue edge. **No map open at all -> whiteboard, no ask** (nowhere else to go). `note` and a non-sharp `idea` stay map-fog and skip this; with no open map they fall back to the global notes location (`$HOME/.claude/notes/`), or a now-sharp `idea` becomes a whiteboard ticket. **When a capture creates a whiteboard ticket, refresh the pinned dashboard** afterwards (a structural event: the whiteboard surface the dashboard indexes grew, and a whiteboard ticket has no native progress bar, so the dashboard is the only place it shows); a capture landing on a *map* does not (native sub-issue progress bars track map tickets). See *The whiteboard* and the dashboard freshness rule in `../../_shared/substrate.md`.
10
10
 
11
11
  - **`note`** → a verbatim line in `## Not yet specified` (outside a project → `$HOME/.claude/notes/`). No questions, no rewriting.
12
12
  - **`idea`** → fog by default in `## Not yet specified`; becomes a ticket only if already phrasable as a sharp question. An idea in the fog is **not executable**: to work it, it must first **graduate** into a ticket (which happens when the frontier reaches it, or when you can now phrase it sharply). A `todo`, by contrast, is born a `build` ticket and is workable immediately.
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: trailhead-chart
3
+ description: "trailhead chart cluster: the verbs that give a map its start or bring outside work into it. Chart a new map from a loose idea (new), adopt an existing codebase into a map (adopt), open a ticket of any type on the fly (ticket), triage issues opened by others into the map (inbox), and run a standalone grilling session (grill). A cohesion cluster of the trailhead skill split, loading the shared `_shared/` core; reached through the `/trailhead:<verb>` command wrappers and the bare `/trailhead` dispatcher. Not auto-invoked: it runs only when one of these verbs is dispatched."
4
+ argument-hint: "[new|adopt|ticket|inbox|grill] [idea | <type> <title> | issue | topic]"
5
+ ---
6
+
7
+ `trailhead-chart` is the **chart cluster** of the trailhead skill: the verbs that give a map its start or bring outside work into it. `new` charts a new map from a loose idea; `adopt` maps an existing codebase into a map; `ticket` opens a ticket of any type on the fly; `inbox` triages issues opened by others; `grill` runs a standalone grilling session. Everything lives on the GitHub Issues; the repo holds code only.
8
+
9
+ ## Load first, in order
10
+
11
+ Before doing anything, read the shared core (paths relative to this file):
12
+
13
+ 1. `../_shared/principles.md`: refer by name, result-oriented output, no em-dashes, git per conventions, one ticket per session.
14
+ 2. `../_shared/ticket-language.md`: write all Issue prose and commit bodies in `config.ticket.language` (default `en`), independent of the chat language.
15
+ 3. `../_shared/substrate.md`: the GitHub-Issues model: labels, the frontier query, the map / ticket / dashboard / whiteboard anatomy, and the base-command cookbook pointer.
16
+ 4. `../_shared/session-handoff.md`: how every resolution closes (`/clear` first, then the next command).
17
+ 5. `../_shared/configuration.md`: the three config layers and the load contract.
18
+ 6. `../_shared/techniques.md`: the technique index and the subagent-type rule.
19
+
20
+ Load the **effective config** (per `../_shared/configuration.md`) at session start, from the map's project root (`.trailhead/config.json` plus the global file), before any isolation workspace is set up.
21
+
22
+ ## Routing: verb to engine
23
+
24
+ The **first word** of the arguments is the verb; the rest is the text (a loose idea, a `<type> <title>`, an issue number, or a topic).
25
+
26
+ - **`new`** / **`adopt`** to **`references/charting.md`**: how a map is born (Mode 1 and Mode 1-bis). Read that file.
27
+ - **`ticket <type> <title>`** to the **Open a ticket on the fly** engine below.
28
+ - **`inbox [issue]`** to **`references/inbox.md`**: the triage protocol. Read that file.
29
+ - **`grill [topic|ticket]`** to the **Standalone grill** engine below.
30
+
31
+ 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 (the gh cookbook, the out-of-scope rules, teamwork, the techniques).
32
+
33
+ ## Chart a new map / adopt a project: `new`, `adopt`
34
+
35
+ Read **`references/charting.md`** and follow it. It holds both protocols: **Mode 1** (chart a new map: name the destination, map the frontier breadth-first, harvest parked work from earlier maps, create the map plus the first-use repo setup, the per-map label, conventions, and the dashboard, create-then-wire the tickets, fire the research, offer config) and **Mode 1-bis** (adopt an existing project: map the codebase once into the repo's `trailhead:codebase` issue, backfill the decisions already embodied in the code, detect a monorepo / submodules / path-bound tooling to seed the `isolation:` convention, then map the frontier of the remainder). At the end of chart or adopt, make the **First-time config offer** (below).
36
+
37
+ ## Open a ticket on the fly: `ticket <type> <title>`
38
+
39
+ Open ticket(s) of the map on the fly, for any of the six types (`decision`, `research`, `prototype`, `build`, `bug`, `task`), the escape hatch the capture verbs do not cover. **Adding a ticket is a micro-charting act, so diverge briefly first, do not blind-commit to a single piece:** run a short breadth-first pass around the request: is this really *one* session-sized ticket, or a small **cluster** (a `decision` that needs a `research` before it, a UI `build` that needs a `prototype`, obvious siblings)? Does it imply a blocker? Surface the neighbours, *then* create the ticket(s): each gets `trailhead:ticket` plus its `trailhead:<type>`, its map's `trailhead:map-<n>` label and native sub-issue edge, a `Parent:` line, and a `## Question`; put each on the frontier, or wire `## Blocked by` plus `trailhead:blocked` (the three-move blocker wiring and the child-to-map link are in `../_shared/substrate.md`; the exact gh commands are in `../_shared/substrate-commands.md`). This is a framing brainstorm (is this the right work?), distinct from the `decision` engine's option brainstorm (which choice?). If `<type>` is missing or invalid, ask which of the six. *(The zero-friction captures, `bug` / `todo` / `idea` / `seed` / `note`, deliberately skip this diverge-first pass; they are one action, one confirmation, and are served by the capture cluster.)*
40
+
41
+ ## Triage the inbox: `inbox [issue]`
42
+
43
+ Read **`references/inbox.md`** and follow it. It triages issues opened by others (bug reports, requests, questions that are not trailhead tickets) and integrates the worthwhile ones **in place**: reframing them as tickets, keeping the reporter's authorship, applying the labels on adoption; the rest routes to fog, out-of-scope, duplicate, or needs-info. It also surfaces **parked `trailhead:fog`** whose thread went quiet-then-active (that is how you notice a fog has cleared), **and sweeps the map's own *Out of scope* for lines that are actually deferred** (a gate/trigger or a wanted-later feature), asking the user to re-route each to a seed / idea / todo (which map) and wiring it gate-first with a live trigger. This is the surface where the map's read-only advisory turns into an actual re-route.
44
+
45
+ ## Standalone grill: `grill [topic|ticket]`
46
+
47
+ 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.
48
+
49
+ ## First-time config offer (end of chart / adopt)
50
+
51
+ Charting hand-resolves nothing, so a fresh chart or adopt closes with this offer. When a project has a map but **no `.trailhead/config.json` yet**, put an **explicit two-way choice** to the user and wait for their pick: **(a) accept as-is** (continue on the current or proposed values, which writes an empty `{}`), or **(b) run `/trailhead:config`** to configure everything through the guided menu (models / design / TDD / ...). **Always name both paths and let the user choose; never silently take one.** You may show the values you would default to, but frame the close as this binary: **do not** pre-fill the config keys and ask for an open-ended inline edit, and **do not** interview the user key-by-key inline (that is `/trailhead:config`'s job). Ask this **only once**: running config writes the file, and *accept as-is* writes an empty `{}`, so once `.trailhead/config.json` exists it is never offered again (a `{}` file means "reviewed, using defaults").
@@ -0,0 +1,24 @@
1
+ # Charting a map: chart (Mode 1) and adopt (Mode 1-bis)
2
+
3
+ The two ways a map is born. Read this when the invocation is `/trailhead:new` or `/trailhead:adopt` (or smart entry routes to *chart* / *adopt*). The other chart-cluster verbs (`ticket`, `grill`, `inbox`) live in this cluster's `../SKILL.md` and `inbox.md`; work, capture, and the rest are served by their own clusters, sharing the `_shared/` core.
4
+
5
+ ## Mode 1: Chart the map (new project), `/trailhead:new`
6
+ The user invokes with a loose idea.
7
+
8
+ 0. **Ensure a tracker repo exists (offer to create one if missing).** Everything trailhead writes lives on a GitHub repo's Issues, so before charting, confirm the current directory has one. Detect it with `gh repo view`: on success a GitHub repo with a remote is present, so say nothing and proceed. If there is **no GitHub repo** (no `origin` / GitHub remote, or `gh repo view` fails), **stop and ask the user** whether to create one, offering **private (default)** or **public**. On their choice: if the directory is not yet a git repo, `git init` and make an initial commit first; then `gh repo create <name> --private|--public --source=. --remote=origin --push` (take `<name>` from the directory, confirmed with the user), so the repo exists on GitHub with `origin` set. Charting continues only once a GitHub repo is in place: every step below writes to its Issues. If the user declines to create one, there is nowhere to chart, so stop and say so. This never fires when a repo already exists (`/trailhead:adopt` and re-charting on an existing repo skip straight past it).
9
+ 1. **Name the destination.** Run the **Grilling** + **Domain vocabulary** techniques to pin down what this map tends toward. The destination fixes the scope, so it's settled first. Default: a working artifact.
10
+ 2. **Map the frontier.** Grill again, **breadth-first**: fan out across the whole space rather than deep on one thread, surfacing the open decisions and the first steps takeable now. **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*. 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:` `none`|`worktree` (the header, the last asked as *"will more than one session run on a single clone at once?"* → `worktree`; a monorepo leans `worktree`), **plus** the narrative a new contributor would need: release/deploy specifics (environments, manual steps, OTA vs full build, versioning), tooling that must be driven a certain way (a specific MCP/CLI, e.g. a Lovable project piloted via its MCP), code & style rules, testing/UAT norms. Distil into the issue (header + prose); link it from the map's Notes. Don't invent: leave out or ask what you can't determine. If one already exists, just link it (reuse across maps). Finally, **ensure the repo's `trailhead:dashboard` index issue exists** (see **The dashboard**): create and pin it if missing (the fixed 3rd pinned slot, alongside codebase + conventions), then **write it to list this map** (charting a map is a structural event); if it already exists, refresh it.
12
+ 4. **Create the tickets you can specify now** as child issues (each with `trailhead:ticket` + its type + this map's `trailhead:map-<n>` label, 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), then run the three-move wiring (`## Blocked by` line + native dependency + `trailhead:blocked` label, see **Substrate**). Wiring sorts them into frontier (unblocked) and blocked; the rest stays fog.
13
+ 5. **Fire the research subagents.** For each `research` ticket created, run the **Research** technique in parallel, findings on a `research/<name>` branch with a pointer from the ticket.
14
+ 6. **Offer config, then stop.** Charting is one session's work; it hand-resolves nothing. Before ending, make the **first-time config offer** (see **First-time config offer** in `../SKILL.md`): the project now has a fresh map and no `.trailhead/config.json`, so put the **two-way choice** to the user, **accept as-is** (writes `{}`) or **run `/trailhead:config`** for the guided menu, and let them pick. Name both paths; don't pre-fill the config keys and ask for an inline edit, and don't interview them key-by-key here (that's `/trailhead:config`'s job). Once the file exists it's never offered again. **No `trailhead:codebase` issue is created here**: greenfield has no code to map yet. It's created later, once the repo has substantial code (see Mode 2 step 1 and the **Codebase** row under Substrate), so at chart two of the three permanent pins (the dashboard + conventions) are filled and the codebase slot fills in later. Maps are never pinned.
15
+
16
+ ## Mode 1-bis: Adopt an existing project, `/trailhead:adopt`
17
+ 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.
18
+
19
+ 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.
20
+ 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".
21
+ 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.
22
+ 4. **Map the frontier of the remainder** → 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.
23
+
24
+ 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, propose `isolation: worktree` 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 + a gitlink bump in the parent**, isolated at the submodule (not the superproject); a submodule path is a natural `Scope:` (see **Working as a team**). **Before proposing `worktree`, check 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 propose **`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.
@@ -16,7 +16,7 @@ gh issue list --label trailhead:fog --state open --search "sort:updated-desc"
16
16
  ```
17
17
  This is **how you notice a fog has cleared**: you don't watch each thread; you run the inbox (as you would to triage new issues), and section B surfaces the parked items that moved. For each, decide: **graduate** (adopt in place, see Fog of war), **keep parked** (still vague), or **drop** (turned out out-of-scope/dead → `trailhead:out-of-scope` + close). Optionally, a participant can nudge readiness by commenting on the thread; you confirm at graduation.
18
18
 
19
- **C. Out of scope: any line 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 from SKILL.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).
19
+ **C. Out of scope: any line 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
21
  For each issue in either of the first two sections, decide, with the user for judgement calls, on your own for clear ones:
22
22
 
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: trailhead-manage
3
+ description: "trailhead manage cluster: the admin verbs that keep a trailhead install healthy. Show the effective config or change it through a guided menu (config); check for a newer trailhead and install it where that is safe (update). A cohesion cluster of the trailhead skill split, loading the shared `_shared/` core; reached through the `/trailhead:<verb>` command wrappers and the bare `/trailhead` dispatcher. Not auto-invoked: it runs only when one of these verbs is dispatched."
4
+ argument-hint: "[config|update] [get|set …]"
5
+ ---
6
+
7
+ `trailhead-manage` is the **manage cluster** of the trailhead skill: the admin verbs that keep a trailhead install healthy. `config` shows the effective config or changes it (a guided menu, or `get` / `set <key> <value>`); `update` checks for a newer trailhead and installs it where that is safe. Everything lives on the GitHub Issues; the repo holds code only.
8
+
9
+ ## Load first, in order
10
+
11
+ Before doing anything, read the shared core (paths relative to this file):
12
+
13
+ 1. `../_shared/principles.md`: refer by name, result-oriented output, no em-dashes, git per conventions, one ticket per session.
14
+ 2. `../_shared/ticket-language.md`: write all Issue prose and commit bodies in `config.ticket.language` (default `en`), independent of the chat language.
15
+ 3. `../_shared/substrate.md`: the GitHub-Issues model: labels, the frontier query, the map / ticket / dashboard / whiteboard anatomy, and the base-command cookbook pointer.
16
+ 4. `../_shared/session-handoff.md`: how every resolution closes (`/clear` first, then the next command).
17
+ 5. `../_shared/configuration.md`: the three config layers and the load contract.
18
+ 6. `../_shared/techniques.md`: the technique index and the subagent-type rule.
19
+
20
+ Load the **effective config** (per `../_shared/configuration.md`) at session start, from the map's project root (`.trailhead/config.json` plus the global file). These admin verbs resolve no ticket and set up no isolation workspace.
21
+
22
+ ## Routing: verb to engine
23
+
24
+ The **first word** of the arguments is the verb (`config` or `update`); the rest is that verb's argument (`get`, `set <key> <value>`, or empty).
25
+
26
+ - **`config [get|set …]`** to **`../_shared/configuration-reference.md`**: the config keys, their semantics, and the guided menu setup. With no argument it runs the guided setup; `config get` prints the merged config read-only (project `.trailhead/config.json` over global over defaults, showing which source wins each key); `config set <key> <value>` writes one key directly (to the project `.trailhead/config.json`, or to `~/.claude/trailhead/config.json` with `--global`). Read that file and follow it.
27
+ - **`update`** to **`references/updating.md`**: the update protocol (detect the install channel, compare the installed version with the latest from the matching source, install the newer one where it is safe). Read that file and follow it.
28
+
29
+ The full config **key reference** is a single source, shared with the rest of the skill: `../_shared/configuration-reference.md` (keys + guided setup).
@@ -0,0 +1,112 @@
1
+ ---
2
+ name: trailhead-view
3
+ description: "trailhead view cluster: the read-only renders of the trailhead surface. Show the low-res map: destination, decisions, frontier, fog (map); regenerate and show the pinned per-repo dashboard indexing every open map plus the whiteboard (dashboard); show the whiteboard of loose map-less tickets (whiteboard). 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
+ argument-hint: "[map|dashboard|whiteboard] [map]"
5
+ ---
6
+
7
+ `trailhead-view` is the **view cluster** of the trailhead skill: the read-only renders of the trailhead surface. `map` renders the low-res map (destination, decisions, frontier, fog); `dashboard` regenerates and shows the pinned per-repo index of every open map plus the whiteboard; `whiteboard` renders the loose, map-less tickets. These verbs present state; they change nothing on the tracker, except: the dashboard's create-and-pin self-heal, and a user-confirmed map close (the `map` render's exhaustion check for the map it renders, and the dashboard render / self-heal advisory's offer to close any other exhausted-but-open map). Everything lives on the GitHub Issues; the repo holds code only.
8
+
9
+ ## Load first, in order
10
+
11
+ Before doing anything, read the shared core (paths relative to this file):
12
+
13
+ 1. `../_shared/principles.md`: refer by name, result-oriented output, no em-dashes, git per conventions, one ticket per session.
14
+ 2. `../_shared/ticket-language.md`: write all Issue prose and commit bodies in `config.ticket.language` (default `en`), independent of the chat language.
15
+ 3. `../_shared/substrate.md`: the GitHub-Issues model: labels, the frontier query, the map / ticket / dashboard / whiteboard anatomy, and the base-command cookbook pointer.
16
+ 4. `../_shared/session-handoff.md`: how every resolution closes (`/clear` first, then the next command).
17
+ 5. `../_shared/configuration.md`: the three config layers and the load contract.
18
+ 6. `../_shared/techniques.md`: the technique index and the subagent-type rule.
19
+
20
+ Load the **effective config** (per `../_shared/configuration.md`) at session start, from the map's project root (`.trailhead/config.json` plus the global file). These are read-only renders: no isolation workspace is set up.
21
+
22
+ ## Routing: verb to engine
23
+
24
+ The **first word** of the arguments is the verb; the rest is an optional map selector.
25
+
26
+ - **`map [map]`** to the **Render the map** engine below.
27
+ - **`dashboard`** to the **Render the dashboard** engine below.
28
+ - **`whiteboard`** to the **Render the whiteboard** engine below.
29
+
30
+ The cross-cluster situational references these renders call live in `_shared/`; this cluster names each by its `../_shared/...` path where it needs one (the gh cookbook for the dashboard commands, the out-of-scope rules). The multi-cluster model, the frontier-order and blocked-by-drift rules, and the handoff are also in `_shared/`.
31
+
32
+ ## Render the map: `map [map]`
33
+
34
+ A single `trailhead:map` issue is the canonical artifact: an **index**, not a store, listing the decisions made and pointing at the tickets that hold their detail. A decision lives in exactly one place (its ticket); the map gists it and links.
35
+
36
+ **`map`** renders it at low resolution as a read-only view (it changes nothing on the map itself, except the user-confirmed close in the exhaustion check below), the map body plus the live ticket state, by name. **Pick which map** if the repo has several open: use the one the user named (`map <n>` shows that one and makes it active), else the active map (`.trailhead/active-map`); with no active map and more than one open, list them and ask which. See **Multiple maps on one repo** in `../_shared/multi-map.md`.
37
+
38
+ Render, by name:
39
+ - **Destination**: the one-line where-we're-headed.
40
+ - **Frontier**: takeable now (open, unassigned, not blocked/unverified, and **not** `trailhead:whiteboard`, which has its own view), each with its type. Present independent tickets as a **set to choose from**, not a crowned first (see **Frontier order carries no priority** in `../_shared/substrate.md`).
41
+ - **In progress**: claimed tickets, with who holds each.
42
+ - **Blocked**: with what each waits on; flag any **blocked-by drift** (prose `## Blocked by` differs from the native dependency, see **Blocked-by reconciliation** in `../_shared/substrate.md`) as an advisory line.
43
+ - **Decisions so far**: the index of what's settled.
44
+ - **Not yet specified** + **parked fog**: the coarse fog, and a count/link of open `trailhead:fog` issues.
45
+ - **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).
46
+ - **Inbox**: a count of untriaged inbound issues, as a nudge.
47
+ - **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).
48
+
49
+ **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`.
50
+
51
+ The map body it renders (the sections are matched by their **text**; the emoji are cosmetic anchors):
52
+
53
+ ```markdown
54
+ ## 🎯 Destination
55
+ <what reaching the end of this map looks like: the spec, decision, or change this effort tends toward. The working artifact, unless overridden. One or two lines; every session orients to it before choosing a ticket.>
56
+
57
+ ## 🗒️ Notes
58
+ <domain; vocabulary; standing preferences for this effort; any "stop at the spec" override; a link to the repo's `trailhead:codebase` issue and a link to the repo's `trailhead:conventions` issue (the way of working: git/release + notes). Config is NOT here: it lives in `.trailhead/config.json` at the repo root.>
59
+
60
+ ## ✅ Decisions so far
61
+ <!-- the index: one line per closed ticket -->
62
+ - [<closed ticket title>](link): <one-line gist of the answer>
63
+
64
+ ## 🌫️ Not yet specified
65
+ <!-- fog of war: in-scope fog not yet ticketable; graduates as the frontier advances -->
66
+
67
+ ## 🚫 Out of scope
68
+ <!-- work ruled beyond the destination; closed, never graduates -->
69
+ ```
70
+
71
+ So `Decisions so far` / `Not yet specified` / `Out of scope` refer to these blocks throughout.
72
+
73
+ ## Render the dashboard: `dashboard`
74
+
75
+ A repo's trailhead surface is spread across issues: one or more maps, a whiteboard, an inbox. The **dashboard** is the single pinned entry point that indexes all of it: a permanent, repo-scoped issue labelled **`trailhead:dashboard`**, created once at the first chart/adopt and occupying the **fixed 3rd pinned slot** alongside `trailhead:codebase` and `trailhead:conventions` (see `../_shared/substrate.md`). Maps themselves are **never pinned**: a pin is server-side and global to the repo, but the active map is client-side per-checkout state (`.trailhead/active-map`, gitignored), so no single map is "the" map to pin. The dashboard gives one stable pinned index that scales to any number of maps and to the whiteboard.
76
+
77
+ It holds:
78
+ - a link to **every open `trailhead:map`** (found via the `trailhead:map` label); GitHub renders each map's sub-issue **progress bar natively**, so the dashboard only links, it never recomputes per-ticket progress. **Per open map it also runs one cheap `0 open scoped issues?` pre-filter** and, for a candidate confirmed to have no remaining fog (see the exhaustion check in `../_shared/substrate-commands.md`), flags an **exhausted-but-open** map (destination reached, map issue never closed) as `exhausted · closeable`, so the render can offer to close it (below);
79
+ - the **whiteboard** as its own section (frontier / in-progress / blocked, or a link to the `whiteboard` view);
80
+ - dynamic **counts**: untriaged inbox size, whiteboard frontier size.
81
+
82
+ **`dashboard`** regenerates the pinned issue body from the live tracker and shows it, both the render and the on-demand refresh, consistent with `map` and `whiteboard`. It creates and pins the issue if the repo doesn't have one yet (ensuring the `trailhead:dashboard` label first, idempotently). The exact gh commands (find, pin/re-pin/unpin, self-heal, body generation with the count queries) are in `../_shared/substrate-commands.md`.
83
+
84
+ **Exhausted-but-open maps: flag and offer to close.** The one-shot "close this map?" prompt fires only at the exhaustion moment (a `work` handoff, or a `map` render on the exhausted map). If that moment was skipped, or the user chose to leave the map open, an exhausted map lingers with nothing re-surfacing it. So the dashboard is the recurring surface that catches it: for every open map flagged `exhausted · closeable` (the `0 open scoped issues?` pre-filter confirmed against remaining fog, see `../_shared/substrate-commands.md`), the render **offers to close it** (route any un-parked deferred *Out of scope* line first, exactly as the `map` exhaustion check does), `gh issue close` on a yes, leave it open on a no. Never auto-close: flag + offer only. The count runs per map, but the body read (fog confirm + deferred routing) is bounded to the exhausted candidates, so this stays within the dashboard-only-links spirit.
85
+
86
+ **Freshness: structural events + on demand.** The pinned issue is rewritten when the surface changes **structurally** and **on demand** via `dashboard`. The structural events are: a map is **charted** or **exhausted** (a map appears or disappears), and a **whiteboard ticket is born or resolved**. It is **not** rewritten on every *map* ticket resolve: that would churn a pinned issue (and its notifications) constantly, and each map's native sub-issue progress bar already tracks its per-ticket progress. **The whiteboard has no such native progress bar** (its tickets are sub-issues of nothing), so the dashboard's whiteboard section and count are the only place whiteboard state shows: a whiteboard ticket appearing or being resolved *is* a structural change to the indexed surface, and refreshes the dashboard. Links + counts stay good enough refreshed at these events and on demand.
87
+
88
+ **Self-heal (read-only entry points).** A repo that adopted trailhead before the dashboard existed, or one whose pin was later dropped, has no pinned index until the next chart/adopt or an explicit `dashboard`. To close that gap, the frequent **read-only entry points self-heal the pin**: the bare `/trailhead` smart entry, the `map` render, and the `whiteboard` render each ensure the dashboard exists and is pinned before rendering, idempotently and cheaply, mirroring the "ensure the label exists before applying it" pattern: ensure the `trailhead:dashboard` label, then
89
+ - **if no dashboard issue exists**, create it, write the current surface once, and pin it (the fixed 3rd slot);
90
+ - **if it exists but is unpinned** (a dropped pin is exactly the drift to self-heal), re-pin it;
91
+ - **if it exists and is already pinned**, do nothing.
92
+
93
+ **Create + pin only when missing: never rewrite the body on the read-only renders.** `map` and `whiteboard` are frequent read-only views, not the on-demand refresh, so the freshness rule above holds unbroken (`dashboard` and structural events stay the only writers of the body). The one body write those paths ever make is the initial one at creation (the dashboard appearing is itself a structural event).
94
+
95
+ **Exhausted-map advisory on the self-heal entry points.** Because bare `/trailhead`, the `map` render, and the `whiteboard` render already touch the dashboard to self-heal the pin, each also runs the per-map exhaustion advisory documented in the self-heal spec (`../_shared/substrate-commands.md`): the `0 open scoped issues?` pre-filter, a fog confirm on each candidate, then a one-line `exhausted · closeable` advisory with the offer to close it, routing any un-parked deferred *Out of scope* line first (never auto-close, and never a body rewrite here). This keeps the offer recurring across every read-only entry point, not just `/trailhead:dashboard`, so a missed exhaustion moment always gets a second chance. (On `/trailhead:map <n>` the map render's own **Exhaustion check** already handles the map it is rendering; this advisory covers any other exhausted-but-open map.)
96
+
97
+ ## Render the whiteboard: `whiteboard`
98
+
99
+ Not all work belongs to a map. A **bug**, a **todo**, or a one-off **task** may be about something else entirely, or simply not worth charting a map for. The **whiteboard** is where that loose work lives: tickets labelled **`trailhead:whiteboard`**, map-less (no `trailhead:map-<n>` label, no `Parent:` line, no native sub-issue edge). A ticket is on a map **or** on the whiteboard, never both. Loose tickets are created there by a capture routed to the whiteboard or born there by `quick`, and worked with `quick <n>` or `work <n>` (the capture and work clusters own those paths). This engine only **renders** the whiteboard.
100
+
101
+ It has its **own frontier**, off every map's (see **Map frontier vs whiteboard frontier** in `../_shared/substrate.md`):
102
+ ```bash
103
+ gh issue list --label "trailhead:ticket" --label "trailhead:whiteboard" --state open \
104
+ --search "no:assignee -label:trailhead:blocked -label:trailhead:unverified"
105
+ ```
106
+
107
+ **`whiteboard`** renders this as a read-only view, a mirror of the `map` render minus destination, decisions, and fog (the whiteboard has no direction, only loose work), by name:
108
+ - **Frontier**: takeable now (open, unassigned, not blocked/unverified whiteboard tickets), each with its type. Present independent tickets as a **set to choose from**, not a crowned first (see **Frontier order carries no priority** in `../_shared/substrate.md`).
109
+ - **In progress**: claimed whiteboard tickets, with who holds each.
110
+ - **Blocked**: any blocked whiteboard ticket, with what it waits on (a whiteboard ticket can still block on another).
111
+
112
+ **Dashboard self-heal.** Like the `map` render, ensure the repo's pinned `trailhead:dashboard` index exists and is pinned before rendering (create + pin only if missing, re-pin if the pin was dropped; never rewrite its body here). See the **Render the dashboard** self-heal above.
@@ -0,0 +1,65 @@
1
+ ---
2
+ name: trailhead-work
3
+ 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 off the map without splitting (quick), checkpoint the ticket in play (pause), resume it from its checkpoint (resume), and split an oversized ticket into children (split). 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
+ argument-hint: "[work|quick|pause|resume|split] [ticket | \"text\" | note]"
5
+ ---
6
+
7
+ `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. Everything lives on the GitHub Issues; the repo holds code only.
8
+
9
+ ## Load first, in order
10
+
11
+ Before doing anything, read the shared core (paths relative to this file):
12
+
13
+ 1. `../_shared/principles.md`: refer by name, result-oriented output, no em-dashes, git per conventions, one ticket per session.
14
+ 2. `../_shared/ticket-language.md`: write all Issue prose and commit bodies in `config.ticket.language` (default `en`), independent of the chat language.
15
+ 3. `../_shared/substrate.md`: the GitHub-Issues model: labels, the frontier query, the map / ticket / dashboard / whiteboard anatomy, and the base-command cookbook pointer.
16
+ 4. `../_shared/session-handoff.md`: how every resolution closes (`/clear` first, then the next command).
17
+ 5. `../_shared/configuration.md`: the three config layers and the load contract.
18
+ 6. `../_shared/techniques.md`: the technique index and the subagent-type rule.
19
+
20
+ Load the **effective config** (per `../_shared/configuration.md`) at session start, from the map's project root (`.trailhead/config.json` plus the global file), **before** any isolation workspace is set up. 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.
21
+
22
+ ## Routing: verb to engine
23
+
24
+ The **first word** of the arguments is the verb; the rest is the text (a ticket number, a `"text"` seed, or a pause note).
25
+
26
+ - **`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.
27
+ - **`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.
28
+ - **`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.
29
+
30
+ 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).
31
+
32
+ ## Work the map: `work`
33
+
34
+ The user invokes with a map (URL or number). A ticket is optional: without one, you pick the next decision.
35
+
36
+ 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. 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**, as a distinct line in the next-step block (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.
37
+ 2. Choose the ticket. 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).
38
+ - **First, if the conventions header has NO `isolation:` key at all** (never chosen, so silently defaulting to `none`), sanity-check it **once** before working: if there's a real collision risk, don't just work the shared checkout. The risk signals are **another in-progress ticket** (a concurrent session is plausible) or **path-bound tooling in this ticket's `Scope:`** (a React Native/Expo app, native toolchain, a local `node_modules`). On a signal, surface a one-line suggestion naming it and the fitting mode (`worktree` for builds that run from any path, `clone` for path-bound apps) and offer to set it now. Whatever the user picks (**including "keep `none`"**), **write the `isolation:` key into the conventions issue** so the choice is recorded and this never prompts again. If the key is already present (any value, `none` included), it was chosen: respect it and skip this check. This is the one convention worth surfacing (it changes where you work); don't nag beyond writing the key once.
39
+ - **Under `isolation: none`, 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`.
40
+ - **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`.
41
+ - **Under `isolation: worktree` or `clone`, set up the isolated workspace NOW, before any file edit or diagnostic probe** (not later at Execute/Fix): for `worktree`, create/enter this ticket's `git worktree` on a `trailhead/t<n>` branch for the repo the ticket's `Scope:` points at (**the submodule itself for submodule-scoped work**, e.g. `git -C app 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` + the install step, or a **folder copy that brings `node_modules`** to skip the reinstall (then verify the copy's git is independent, `git -C <copy> rev-parse --absolute-git-dir` resolves inside it, else commits leak back to the original). **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`.
42
+ 3. 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.
43
+ 4. 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.
44
+ 5. 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 + close) instead of resolving it. If the decision invalidates other parts of the map, update or delete them.
45
+ 6. **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.
46
+
47
+ ## Work one ticket whole, off the map: `quick`
48
+
49
+ 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. 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>"`.
50
+
51
+ It runs the **full engine** (the `build`/`bug` cycle in `references/ticket-engines.md`: Discuss → Plan → Execute → Verify → Resolve), differing from `work` on three points:
52
+ - **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.
53
+ - **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.)
54
+ - **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 4 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.
55
+ - **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 `isolation: worktree`/`clone` it also sets up the ticket's `trailhead/t<n>` workspace before any edit and re-checks the claim; under `isolation: none`, the scope-free check applies too.
56
+ - **Refreshes the dashboard.** `quick "<text>"` opening a **new whiteboard ticket** changes the surface the pinned dashboard indexes, so refresh it right after the ticket exists (a structural event; the whiteboard has no native progress bar, so the dashboard is the only place the ticket shows). Resolving a whiteboard ticket refreshes it again. `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`.
57
+
58
+ Type follows the ticket: `quick "<text>"` defaults to `build` (`bug` if a defect); `quick <n>` uses the existing type and its matching engine.
59
+
60
+ **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`.
61
+
62
+ ## Pause, resume, split
63
+
64
+ - **`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.
65
+ - **`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`).
@@ -0,0 +1,52 @@
1
+ # Ticket types and their engine
2
+
3
+ The per-type engines the `work` verb dispatches to (Mode 2 step 3, in `../SKILL.md`). The `build`/`bug` engine is this cluster's single-cluster reference; `decision`/`research`/`prototype`/`task` round out the six types. `quick` (the off-map, no-split variant) lives inline in `../SKILL.md`.
4
+
5
+ Every ticket is **HITL** (human in the loop, speaking for themselves) or **AFK** (driven by the agent alone). A HITL ticket resolves only through the live exchange: the agent never stands in for the human's side.
6
+
7
+ Technique names below (**Grilling**, **Domain vocabulary**, **Prototype**, **Research**, **TDD**, **Code review**, **Acceptance testing**, **Debug**) are indexed in `../../_shared/techniques.md`; their full protocols live in `_shared/`, read from `../../_shared/techniques/<name>.md`.
8
+
9
+ ## `decision`: HITL, *default for decision tickets*
10
+ Close a choice, but widen before you narrow:
11
+
12
+ 1. **Diverge (only if the option space isn't already clear).** Mapping the frontier usually framed the options already; skip straight to grilling when it did. When it didn't (the ticket names a question but the candidate answers aren't laid out) open the space *first*: brainstorm the plausible options, name each concretely, and post them as an `OPTIONS` comment. Divergence has no judgement yet: surface possibilities, don't rank them. If a missing **fact** is what's blocking clarity, spin a `research` ticket and block on it; if the question is "how could it look/behave", spin a `prototype`. Don't grill a space you haven't opened.
13
+ 2. **Converge.** Run the **Grilling** + **Domain vocabulary** techniques over the options to pick one. Record the decision and the why.
14
+
15
+ The two phases are distinct on purpose: divergence generates, grilling chooses. Never let the agent invent the options *and* pick for the human: the choice stays theirs.
16
+
17
+ ## `research`: AFK
18
+ Reading docs, third-party APIs, or local resources to surface a fact a decision waits on. Resolved by the **Research** technique. The only type that runs in parallel and more than one per session.
19
+
20
+ ## `prototype`: HITL
21
+ Raise the fidelity of the discussion with a rough, concrete artifact to react to: the **Prototype** technique. Link the prototype as an asset.
22
+
23
+ ## `build`: the engine inside the map
24
+ The ticket that *builds*. Lean cycle, all in **ticket comments**:
25
+
26
+ 1. **Discuss**: **never auto-grill.** Start from Plan. If reading the ticket surfaces blocking ambiguity, **stop and ask the user** whether they want a **Grilling** round: don't launch it on your own initiative. Only on their assent run the short round and post a `DISCUSS` comment with the closed choices. The user may also ask for it themselves at any time.
27
+ 2. **Plan**: a `PLAN` comment: the steps, the seams, the files touched, the verification criteria. Apply **TDD** per `config.tdd` (`seams`/`on`/`off`). Produce this plan via a *planner subagent* on `config.models.plan` (spawn it as the built-in **`Explore`** agent, read-only; inline when the model is unset or equals the session model; Execute then runs as its own subagent, see below). If `config.plan_review` is on, run **Cross-AI plan review** before Execute: send the plan to external AI CLIs and converge on their concerns. Now that the files/seam are known, run the **undeclared-coupling check** (`../../_shared/teamwork.md`) against in-progress tickets.
28
+ 3. **Execute**: **if this ticket changes user-facing UI (a new screen, a redesign, a changed layout), run the Prototype technique FIRST, before any real UI code**: surface a mockup to react to, routed by `config.design` (disk / claude.ai/design / stitch) and gated by `config.design.approval` (`explicit` waits for a go-ahead, `auto` proceeds after surfacing). For a redesign or new screen the default is to offer mockups; don't jump straight to code on a UI ticket. **Once the mockup is approved, reproduce it with 100% fidelity** (layout, spacing, sizing, colour, type, copy, and every state), never a loose approximation "in the spirit of" it; if part of the approved mockup is genuinely infeasible, stop and raise it rather than silently deviate (full statement in the **Prototype** technique, `../../_shared/techniques/prototype.md`). Then implement with **atomic commits** (conventional commits, each with a `Refs: #<n>` trailer for this ticket, see **Principles** in `../../_shared/principles.md`), following the repo's `trailhead:conventions` `git:`, straight to `main` by default, or a feature branch + PR when `git: pr`. **Under `isolation: worktree`, do the work in this ticket's `git worktree`** (`trailhead/t<n>` branch), integrating to the trunk at Resolve (see `../../_shared/teamwork.md`). One commit = one verifiable step. Run this via an **executor subagent on `config.models.execute`** (spawn it as the built-in **`general-purpose`** agent, which can Edit/Write/commit; inline when the model is unset or equals the session model); the commits land on `main`, visible. New scope surfacing mid-execute is captured/split, not worked here.
29
+ 4. **Verify**: a `VERIFY` comment: run the tests / the plan's criterion; then the **Code review** technique (its subagent reviews *adversarially*: it doesn't defer to a `CLAUDE.md` convention or a "settled decision" memory), **run in the background and collected by polling, never a blind blocking wait** (a hard engine default on every host, see *Reviews never block the session* in `../../_shared/techniques.md`); then **Acceptance testing** if the change is user-facing (browser-drive it, or **walk the user through a guided UAT conversationally, one step at a time**, never a checklist dumped in a comment): interactive by design, so it runs inline, not backgrounded. Report the outcome honestly (if a test fails, say so).
30
+ 5. **Resolve**: a resolution comment with what was done → `gh issue close` → update `Decisions so far` on the map. **If this change materially altered the codebase** (a new module/layer, dependency, seam, or build/test command), **patch the affected facet of the `trailhead:codebase` issue** (see **Codebase** in `../../_shared/substrate.md`) in place (a line or two, not a re-map). Then close the session with the **Session handoff** ritual (`../../_shared/session-handoff.md`): `/clear` + next command.
31
+
32
+ New scope that surfaces mid-build (while planning, executing, or testing) → **capture or split, never expand this ticket in flight** (see **Scope that surfaces while working a ticket**, served by the capture cluster).
33
+
34
+ ## `bug`: the fix with diagnosis first
35
+ A defect to correct. Unlike `build`, you don't start from implementation: first you **understand**.
36
+
37
+ **New bug ticket vs reopen: decide first.** A bug in the work of an already-closed ticket almost always means a **new** `bug` ticket, *not* reopening the old one, because `Decisions so far` is append-only history: that ticket really did deliver, and the defect surfaced later is a new event. The one exception is a **premature close**: a ticket closed before its Verify ever passed, i.e. it never actually delivered. The discriminator is sharp: **did that ticket genuinely pass Verify?**
38
+ - **Yes** → new bug ticket carrying `Regression of: <closed ticket name+link>` in its body. Leave the original closed. Open it with `/trailhead:bug --of <ticket> <text>` (served by the capture cluster), which fills the pointer for you.
39
+ - **No** (closed by mistake, never delivered) → this isn't a new bug, it's a bad close: `gh issue reopen <n>` and finish it.
40
+
41
+ Then run the cycle, all in ticket comments:
42
+
43
+ 1. **Repro**: a `REPRO` comment: how to reproduce, expected vs observed behaviour.
44
+ 2. **Diagnose**: run the **Debug** technique. A `DIAGNOSIS` comment with the root cause.
45
+ 3. **Fix**: implement the correction with **atomic commits** (each with a `Refs: #<n>` trailer for this ticket, see **Principles** in `../../_shared/principles.md`; per the repo's `trailhead:conventions` `git:`, `main` by default, else a branch + PR; in this ticket's `git worktree` under `isolation: worktree`), via an **executor subagent on `config.models.execute`** (the built-in **`general-purpose`** agent; inline when unset/same as session). Where sensible, a test that fails before and passes after (**TDD**).
46
+ 4. **Verify**: a `VERIFY` comment: the repro now passes, no regressions; **Code review** on the fix, **run in the background and collected by polling, never a blind blocking wait** (a hard engine default on every host, see *Reviews never block the session* in `../../_shared/techniques.md`); **Acceptance testing** if the bug was user-facing (browser-drive the fixed flow, or **walk the user through a guided UAT conversationally, step by step**, not a checklist to self-serve): interactive by design, so it runs inline, not backgrounded.
47
+ 5. **Resolve**: a resolution comment (cause + fix) → `gh issue close` → update `Decisions so far`. **If the fix materially altered the codebase**, patch the affected facet of the `trailhead:codebase` issue (see **Codebase** in `../../_shared/substrate.md`) in place (a line or two). Then close the session with the **Session handoff** ritual (`../../_shared/session-handoff.md`): `/clear` + next command.
48
+
49
+ A **blocking** bug that halts other work is worked immediately; an isolated one is a normal frontier ticket. New scope surfacing while fixing → capture or split, never expand this ticket in flight (see **Scope that surfaces while working a ticket**).
50
+
51
+ ## `task`: HITL or AFK
52
+ Manual work that must happen before a *decision* can be made: signing up for a service to judge its API, provisioning access, moving data to see its shape. The agent drives it alone where it can (AFK); otherwise it hands the human a precise checklist (HITL). Resolved when the work is done; the answer records what was done and the resulting facts (where credentials live, new URLs, row counts) later tickets depend on.
@@ -1,18 +0,0 @@
1
- # Prototype
2
- A prototype is throwaway code that answers one open question. The question decides the shape: pick the branch before you build, because getting it wrong wastes the whole exercise.
3
-
4
- - "Does this logic / state model feel right?" → a single shareable HTML file, the question stated in a visible intro. **Isolate the answering logic in a pure, liftable module** (a reducer, a state machine, or plain functions, no DOM) chosen by the question; the page is a thin shell over it, and free-play controls plus tabbed guided walkthroughs (each resetting to a known state) push it through the cases hard to reason about on paper. Keeping the logic pure is what lets the validated module lift straight into real code; drivable by a non-developer.
5
- - "What should this look like?" → several radically different UI variations on one route, switchable via a URL param and a floating bottom bar. **Prefer adjusting an existing populated page** over a fresh throwaway route. Make **3 variants** (cap 5), *structurally* different: different layouts, not colours or copy; the switcher cycles with ←/→ (skip when an input is focused), wraps, and is **hidden in production builds**.
6
-
7
- If genuinely ambiguous and the user's unreachable, default by context (backend module → logic; page/component → UI) and state the assumption at the top. Build the **cheapest concrete artifact** that settles it: outline, stub, static mockup, rough take. Optimise for reaction, not reuse: no tests, no error handling beyond runnable, no abstractions. Throwaway from day one and clearly labelled as such, sited next to the code it prototypes, trivial to run (one command or a double-clickable file). No persistence by default: surface the full relevant state after every action so the change is visible. Fold only the validated decision into real code; never let a prototype silently graduate into production.
8
-
9
- **For UI tickets specifically:** before writing any real UI code, **ask the user whether they want mockups first** (default yes for a new screen). If yes, route by `config.design` (see [Configuration](#configuration)):
10
- - **`disk` (default)**: a throwaway static HTML mockup next to the code, linked from the ticket as an asset (on a throwaway branch, out of main). Fast, self-contained.
11
- - **`claude.ai/design` mode**: push the mockup to a **design-system project** on claude.ai/design via the **`/design-sync` skill + `claude_design` MCP (DesignSync)**, where the user edits it visually. On the **first** UI screen for this map, ask the user which design-system project to attach to. Three paths: (a) **pick an existing one**, listing the **10 most-recently-updated** design systems from DesignSync `list_projects` (sorted `updatedAt` desc) to choose from; (b) **paste a URL**, taking the projectId from a `claude.ai/design/p/<id>` link and confirming with `get_project` that it's a `PROJECT_TYPE_DESIGN_SYSTEM`; (c) **create a new one**, asking the user first for its name (don't invent it), then `create_project`. **Cache the chosen projectId in the project `.trailhead/config.json` under `design.project`**. **Later screens reuse** it (push another component/preview under the same `planId` flow: `finalize_plan` → `write_files`). **Link the specific screen** back from the ticket: its own claude.ai/design URL (`claude.ai/design/p/<projectId>/…`), not merely the project. **DesignSync only targets `PROJECT_TYPE_DESIGN_SYSTEM` projects**: verify with `get_project` before pushing; a regular `PROJECT_TYPE_PROJECT` (or any project not in `list_projects`) **cannot** be driven by the MCP, so for one of those the user drives it by hand: produce the mockup and hand it over, or fall back to `disk`. This path needs the account's claude.ai design login (`/design-login` when the session has none); if it's unavailable, fall back to `disk` and say so. **On a host without DesignSync (e.g. Codex, which has no `claude_design` MCP or `/design-sync` skill), `claude.ai/design` mode is not available at all: always use `disk` (local HTML mockups) there, whatever `config.design` says.**
12
-
13
- Either way the rough screen is **reacted to before real UI code**. How approval gates the build depends on `config.design.approval`:
14
- - **`explicit` (default)**: surface the mockup and **wait for the user's clear go-ahead**; do not write UI code until they approve. Record the approval on the ticket, then proceed.
15
- - **`auto`**: surface the mockup and **proceed without blocking** on a confirmation (the user can still object). Record the direction on the ticket.
16
-
17
- **In `claude.ai/design` mode, re-fetch the design before you build on it.** Between surfacing the mockup and writing the UI code the user may have edited it directly on claude.ai/design, so once it's approved (or, in `auto`, right before you consume it), pull the **current** state from the project (`list_files` → `get_file`) and build from *that*, never from the version you last pushed. Note on the ticket that the design was re-fetched at that point.
18
-