@marcomigozzi/trailhead 0.5.0 → 0.6.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 (42) hide show
  1. package/README.it.md +8 -5
  2. package/README.md +8 -5
  3. package/bin/lib/codex-projection.js +79 -58
  4. package/bin/lib/codex-projection.test.js +103 -15
  5. package/bin/trailhead.js +106 -15
  6. package/bin/trailhead.test.js +76 -11
  7. package/package.json +2 -2
  8. package/plugins/trailhead/.claude-plugin/plugin.json +1 -1
  9. package/plugins/trailhead/agents/trailhead-code-review.md +10 -0
  10. package/plugins/trailhead/agents/trailhead-codebase-map.md +10 -0
  11. package/plugins/trailhead/agents/trailhead-debug.md +10 -0
  12. package/plugins/trailhead/agents/trailhead-executor.md +10 -0
  13. package/plugins/trailhead/agents/trailhead-fix.md +10 -0
  14. package/plugins/trailhead/agents/trailhead-plan.md +10 -0
  15. package/plugins/trailhead/agents/trailhead-research.md +10 -0
  16. package/plugins/trailhead/agents/trailhead-verify.md +10 -0
  17. package/plugins/trailhead/hooks/hooks.json +4 -0
  18. package/plugins/trailhead/hooks/trailhead-install-guard.js +176 -0
  19. package/plugins/trailhead/hooks/trailhead-install-guard.test.js +118 -0
  20. package/plugins/trailhead/skills/_shared/configuration-reference.md +39 -12
  21. package/plugins/trailhead/skills/_shared/configuration.md +1 -1
  22. package/plugins/trailhead/skills/_shared/session-handoff.md +1 -1
  23. package/plugins/trailhead/skills/_shared/substrate-commands.md +4 -2
  24. package/plugins/trailhead/skills/_shared/substrate.md +1 -1
  25. package/plugins/trailhead/skills/_shared/techniques/code-review.md +2 -2
  26. package/plugins/trailhead/skills/_shared/techniques/codebase-map.md +1 -1
  27. package/plugins/trailhead/skills/_shared/techniques/debug.md +4 -1
  28. package/plugins/trailhead/skills/_shared/techniques/plan-review.md +1 -1
  29. package/plugins/trailhead/skills/_shared/techniques/prototype.md +6 -3
  30. package/plugins/trailhead/skills/_shared/techniques/research.md +2 -1
  31. package/plugins/trailhead/skills/_shared/techniques/verify.md +12 -0
  32. package/plugins/trailhead/skills/_shared/techniques.md +4 -3
  33. package/plugins/trailhead/skills/trailhead/SKILL.md +1 -1
  34. package/plugins/trailhead/skills/trailhead-capture/SKILL.md +1 -0
  35. package/plugins/trailhead/skills/trailhead-chart/SKILL.md +2 -1
  36. package/plugins/trailhead/skills/trailhead-chart/references/charting.md +1 -1
  37. package/plugins/trailhead/skills/trailhead-chart/references/inbox.md +6 -6
  38. package/plugins/trailhead/skills/trailhead-manage/SKILL.md +1 -0
  39. package/plugins/trailhead/skills/trailhead-manage/references/updating.md +12 -0
  40. package/plugins/trailhead/skills/trailhead-view/SKILL.md +2 -1
  41. package/plugins/trailhead/skills/trailhead-work/SKILL.md +2 -0
  42. package/plugins/trailhead/skills/trailhead-work/references/ticket-engines.md +12 -8
@@ -4,7 +4,7 @@ Get a second opinion on a `build` PLAN from **independent external AI CLIs** bef
4
4
 
5
5
  When enabled, run this after the PLAN is drafted and before Execute:
6
6
 
7
- 1. **Detect reviewers.** The candidate set is `config.plan_review` **when it names specific CLIs** (e.g. `codex` → only codex), otherwise the default roster: `gemini`, `codex`, `qwen`, `kimi`, `agy` (Antigravity), `opencode`, `cursor-agent`, `coderabbit` (and any OpenAI-compatible local server you've configured). `command -v` **only the candidates** on the PATH (the binary name, not the slug: `agy` for Antigravity, `cursor-agent` for cursor); don't probe (or mention) CLIs the config didn't ask for; with a specific list, a name not on that list is simply not in play. **`kimi` needs more than existence:** the `kimi` binary is claimed by both the Kimi Code CLI and the legacy Python `kimi-cli`, which take different headless flags, so confirm it's Kimi Code by checking `kimi --help` mentions `--output-format` before counting it in. **Skip any reviewer that resolves to the same model as the session running trailhead**: a same-model review isn't *cross-AI*, so it adds no independent perspective (e.g. drop the `claude` CLI when trailhead itself runs on Claude). Match by underlying model, not just CLI name; keeping such a CLI in `config.plan_review` is harmless: the skip happens here, at execution, so the config stays valid if the session model later changes. **If no independent reviewer remains, note it in the PLAN comment and skip; never fail the build over a missing reviewer.**
7
+ 1. **Detect reviewers.** The candidate set is `config.plan_review` **when it names specific CLIs** (e.g. `codex` → only codex), otherwise the default roster: `claude`, `gemini`, `codex`, `qwen`, `kimi`, `agy` (Antigravity), `opencode`, `cursor-agent`, `coderabbit` (and any OpenAI-compatible local server you've configured). `command -v` **only the candidates** on the PATH (the binary name, not the slug: `agy` for Antigravity, `cursor-agent` for cursor); don't probe (or mention) CLIs the config didn't ask for; with a specific list, a name not on that list is simply not in play. **`kimi` needs more than existence:** the `kimi` binary is claimed by both the Kimi Code CLI and the legacy Python `kimi-cli`, which take different headless flags, so confirm it's Kimi Code by checking `kimi --help` mentions `--output-format` before counting it in. **Skip any reviewer that resolves to the same model as the session running trailhead**: a same-model review isn't *cross-AI*, so it adds no independent perspective. **Always drop the current host's own CLI: `codex` when trailhead runs on Codex, `claude` when it runs on Claude Code** (a session reviewing its own plan with itself is not a second opinion). Beyond the host's own CLI, match by underlying model too, not just CLI name; keeping such a CLI in `config.plan_review` is harmless: the skip happens here, at execution, so the config stays valid if the session model later changes. **If no independent reviewer remains, note it in the PLAN comment and skip; never fail the build over a missing reviewer.**
8
8
 
9
9
  2. **Send the same brief to each reviewer.** Give every CLI the ticket's `## Question`, the drafted PLAN, and the map's relevant Notes/decisions in a **self-contained prompt**, so the reviewer needn't explore the repo. Ask for a short list of concerns (*risks, missing steps, wrong assumptions, simpler alternatives*) each tagged **blocking** or **non-blocking**.
10
10
 
@@ -6,8 +6,11 @@ A prototype is throwaway code that answers one open question. The question decid
6
6
 
7
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
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.md#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.
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.md#configuration)).
10
+
11
+ **Route strictly by `config.design`; disk is not a shortcut.** Read `config.design` (and its cached `design.project`) *before* producing anything, and obey it. When it names a hosted mode (`claude.ai/design` or `stitch`), the mockup **must** be built there, reusing the cached `design.project` when present. **Producing a local disk mockup while `config.design` names a hosted mode is a defect**, never a convenient default: `disk` is used only when `config.design` is literally `disk`, or as the explicit, announced fallback the chain below reaches after the hosted mode's MCP offer is declined or unavailable. If you cannot reach the configured mode, run its offer/fallback and **tell the user why**; never silently shortcut to disk. When unsure of the mode, re-read this technique before routing, do not guess.
12
+
13
+ - **`disk`** (the default config *value*, not a fallback to pick at will): 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
14
  - **`claude.ai/design` mode**: build the mockup on claude.ai/design, where the user edits it visually. `config.design.surface` picks which surface (unset, a cached `design.project` infers `design-system`, so an existing project keeps its surface; see [Configuration](../configuration.md#configuration)). The two surfaces are reached by **different mechanisms** (see below): `design-system` is driven by the DesignSync MCP, while `canvas` is **not** MCP-driven (no MCP call creates or drives a regular claude.ai/design canvas project). On the **first** UI screen for this map you attach to a canvas or a project; **cache its id/URL in the project `.trailhead/config.json` under `design.project`** and **later screens reuse** it. **Link the specific screen** back from the ticket: the canvas **Artifact** URL (or a hand-made web-app project's `claude.ai/design/p/<id>`), or the design-system project's `claude.ai/design/p/<projectId>/…` URL, not merely the project. **With several screens, link each screen separately** (its own artboard/file URL), not only the index screen.
12
15
  - **`canvas` (default)**: a one-off screen to react to, editable visually. **No MCP creates or drives a claude.ai/design canvas project** (the `claude-design` MCP only creates `PROJECT_TYPE_DESIGN_SYSTEM` projects), so a canvas is produced one of two real ways: (a) the built-in **`/design` skill** → a canvas published as an **Artifact** (`claude.ai/code/artifact/<id>`), a preview of the Claude Design editor inside Claude Code, editable and exportable there; this is the default and the only in-session path. (b) a regular claude.ai/design project **created by hand in the web app**, whose `claude.ai/design/p/<id>` URL you then **paste in** to attach. Cache the Artifact URL / pasted `/p/<id>` in `design.project`; later screens are added to the same Artifact/project. **When the canvas holds more than one screen, maintain an index (overview) screen and keep it in sync:** every time you produce or add a screen, regenerate the index from the **current** screen set so it lists the new one too (never leave a stale index behind that misses later screens), size its per-screen tiles/thumbnails **large enough to read comfortably** (a legible preview, not a postage stamp), and give each screen its **own standalone artboard/file, linkable on its own outside the index** (openable directly, not only through an index tile). **Do not** try to create a canvas project via the MCP: no such call exists.
13
16
  - **`design-system`**: push the mockup as a component into a **design-system project** via the **`/design-sync` skill + `claude-design` MCP (DesignSync)**, the design-system slice of the same official MCP, for a shared component library kept in sync. On the first screen, three paths: (a) **pick an existing one**, listing the **10 most-recently-updated** design systems from DesignSync `list_projects` (sorted `updatedAt` desc); (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** (name from the user), then `create_project`. Later screens reuse it (`finalize_plan` → `write_files`). **DesignSync only targets `PROJECT_TYPE_DESIGN_SYSTEM` projects**: verify with `get_project` before pushing; a regular `PROJECT_TYPE_PROJECT` **cannot** be driven by DesignSync (nor by any MCP); reach a regular claude.ai/design project through the `canvas` surface instead (the `/design` skill's Artifact, or a hand-made web-app project).
@@ -23,7 +26,7 @@ If genuinely ambiguous and the user's unreachable, default by context (backend m
23
26
  **Author full-viewport mockups with an intrinsic height, not `100vh` (any card-rendered surface).** A design-system pane renders each screen as a card with no real viewport, so a full-viewport app shell sized `height: 100vh` collapses to a thin strip (only the titlebar shows). Give the app shell an intrinsic fixed height instead (e.g. `height: 800px`); a flowing or scrolling page (like an overview index) renders fine because its content gives it real height, but a single-viewport app does not.
24
27
 
25
28
  **Two DesignSync mechanics the publish must get right (DesignSync surface).** Specific to a DesignSync design system, alongside the shared index rule above:
26
- - **Screen files at project ROOT, not in subfolders.** The Design System pane lists only root-level `.html` as openable Pages; a screen in a subfolder (`states/empty.html`) opens **blank** and is never offered in the Pages menu. Publish each screen as a flat root file.
29
+ - **Screen files at project ROOT, not in subfolders, even when a twin already lives in one.** The Design System pane lists only root-level `.html` as openable Pages; a screen in a subfolder (`chat-states/loading.html`) opens **blank** and is never offered in the Pages menu. The manifest / `@dsCard` still embeds such a screen as a thumbnail in `index.html`, but that is the index, not the Pages menu, and there is no way today to surface a subfolder screen in Pages. So **always publish each screen as a flat root file**, and do **not** mirror an existing project's subfolder grouping: if a sibling screen already sits under a folder (`chat-states/`), that placement is the anomaly to avoid (its own Page is unreachable too), not a convention to inherit. The legitimate grouping need (browse the "Chat states" screens together) is served by the **index**, which groups the thumbnails via the manifest / `@dsCard`: grouping is an index/browsing concern, never a filesystem-folder one. Every openable Page stays a flat root `.html`.
27
30
  - **Verify the index against the real `_ds_manifest.json`.** `register_assets` does **not** rewrite an existing manifest, so after any screen add, move, rename, or delete the manifest goes **stale** (keeps listing old or deleted paths). After such a change, read `_ds_manifest.json` and confirm it lists the current flat screen paths, rewriting it directly if not. This is the concrete, verified form of the index-update step above.
28
31
 
29
32
  Either way the rough screen is **reacted to before real UI code**. How approval gates the build depends on `config.design.approval`:
@@ -1,5 +1,5 @@
1
1
  # Research
2
- Spawn a focused subagent (AFK, parallelisable) with exactly ONE clear question: sharp enough that the answer is a decision, not a survey. Vague scope breeds rabbit holes; a narrow question is its own leash. Spawn it as the built-in **`general-purpose`** agent (it writes findings to a throwaway `research/<name>` branch, so it needs Write); **never** a specialised researcher agent that another installed tool registered.
2
+ Spawn a focused subagent (AFK, parallelisable) with exactly ONE clear question: sharp enough that the answer is a decision, not a survey. Vague scope breeds rabbit holes; a narrow question is its own leash. Dispatch the **`trailhead-research`** agent (full tools: it writes findings to a throwaway `research/<name>` branch, so it needs Write), passing `config.models.research` as the model override; **never** an external-plugin researcher agent that another installed tool registered.
3
3
 
4
4
  - **Treat your own prior knowledge as hypothesis, not fact**: training is stale. "As of my training" is a warning flag, not a citation.
5
5
  - **Chase primary, high-trust sources**: official docs, source code, specs, first-party APIs; never a secondary write-up. Follow every claim back to the source that owns it; resolve *current* docs rather than reciting from memory.
@@ -7,5 +7,6 @@ Spawn a focused subagent (AFK, parallelisable) with exactly ONE clear question:
7
7
  - **Guard the classic traps:** a negative claim ("X isn't possible") needs official verification, not a failed search; "didn't find it" ≠ "doesn't exist"; old docs may describe a deprecated path (check the changelog); verify ALL config scopes before ruling one out.
8
8
  - **Distinguish verified fact from inference explicitly.** Assign honest confidence (LOW when only training data backs a claim). "I couldn't find X", "sources contradict", "LOW confidence" are valuable findings, not failures; never hide uncertainty behind confident prose.
9
9
  - **Investigation, not confirmation:** gather evidence, then conclude; don't start from the answer you want and collect support.
10
+ - **Stop at sufficiency; no redundant re-reads.** Once the evidence is decision-ready, conclude: gathering past a confident answer is analysis paralysis, not rigour. Don't re-open a source you have already extracted from in this pass (re-reading the same file to re-derive a fact you already hold); cross-referencing a *new* source for confirmation is different, and still required.
10
11
  - Capture findings as a durable Markdown artifact on a throwaway `research/<name>` branch (out of main), each claim carrying its source URL; leave a pointer on the ticket. Return a **decision-ready answer** (verdict, tradeoffs, confidence, citations) not a raw data dump.
11
12
 
@@ -0,0 +1,12 @@
1
+ # Goal-backward verification
2
+
3
+ Prove the change **delivers what the ticket promised**, not merely that steps ran or the tests pass. This is distinct from **Code review** (which hunts bugs and quality issues adversarially) and from **Acceptance testing** (which proves it does what the *user* wanted, interactively): this is a **goal-backward** reasoning check against the ticket's own stated intent.
4
+
5
+ Run it in the `build`/`bug` **Verify** step, after the tests / the plan's criterion have run, as a subagent on `config.models.verify` (dispatch the **`trailhead-verify`** agent, read-only, passing `config.models.verify` as the model override; inline when the model is unset or equals the session model, and inline on a `subagentToolkit == none` host). Like the other reviews it **never blocks the session**: run it in the background and collect it by polling (see *Reviews never block the session* in `../techniques.md`).
6
+
7
+ 1. **Restate the goal.** From the ticket's `## Question` and the `PLAN`'s verification criteria, list what "done" means as concrete, checkable claims. If the ticket named acceptance criteria, those are the claims.
8
+ 2. **Work backward from each claim to the diff.** For every claim, find the commit(s) and lines that satisfy it, or record it as **unmet**. Read the actual diff and the resulting code, not the commit messages.
9
+ 3. **Check for silent gaps.** A task marked done whose criterion no code satisfies; a criterion the tests do not actually exercise; a partial implementation that passes the tests but misses an edge the ticket called out.
10
+ 4. **Verdict.** Post the result with a leading `status:` tag the engine branches on: `status: met` (every claim satisfied) or `status: gaps_found` (list each unmet or partial claim with its evidence). On `gaps_found` the engine loops back to Execute/Fix for the gap, or runs the HITL checkpoint, before Resolve; it never resolves over an unmet goal silently.
11
+
12
+ Adversarial posture: do not defer to a `CLAUDE.md` convention, a "settled decision" memory, or the executor's own claim that it is done. The diff and the ticket's stated goal are the only authorities.
@@ -1,10 +1,10 @@
1
1
  ## Techniques
2
2
 
3
- The ticket engines call these by name. Each technique's full protocol lives in its own file under `techniques/`: **read that file the first time a session needs the technique**, so only the ones in play load into context (don't preload them). They are `trailhead`'s own, self-contained; subagents are spawned with the built-in `Agent` tool.
3
+ The ticket engines call these by name. Each technique's full protocol lives in its own file under `techniques/`: **read that file the first time a session needs the technique**, so only the ones in play load into context (don't preload them). They are `trailhead`'s own, self-contained; each technique is dispatched to its **trailhead-owned agent** (`trailhead-<technique>`, shipped as committed `agents/trailhead-*.md`) via the `Agent` tool, with the technique's full protocol injected at spawn.
4
4
 
5
- **Pin the subagent type; never borrow a foreign agent.** Every technique subagent carries its full protocol in the prompt (from its `techniques/` file), so it needs no specialised agent, and must not use one: **always set `subagent_type` to a built-in agent, never a project- or plugin-provided type** (a specialised `*-reviewer`, `*-researcher`, `*-mapper`, or similarly named agent that another installed tool registered). Even when such an agent's name or description matches the task perfectly, it follows *its own* protocol and artifact conventions (a specialised code-review agent writes its own review file, not trailhead's `VERIFY` ticket comment), so selecting it silently swaps trailhead's engine for another tool's. The split by what the technique touches: **read-only techniques** (Code review, Codebase map, and the Plan step) spawn **`Explore`** (reads, searches, runs tests; no Edit/Write); **repo-modifying techniques** (Research, Execute, Fix, Debug) spawn **`general-purpose`**. On a `subagentToolkit == none` host there is no fan-out and this is moot: every technique runs inline in the one session. (Codex is **not** such a host: it has the native multi_agent toolkit, so techniques fan out there too; see the Codex adapter's §D.)
5
+ **Dispatch each technique to its trailhead-owned agent.** Every technique subagent carries its full protocol in the prompt (from its `techniques/` file), so the agent definition stays **thin**: identity + tool policy, model-agnostic. Dispatch each technique to **its own `trailhead-<technique>` agent** (set `subagent_type` accordingly): the Plan step → `trailhead-plan`, Execute → `trailhead-executor`, Research → `trailhead-research`, Code review → `trailhead-code-review`, Debug → `trailhead-debug`, Fix → `trailhead-fix`, Codebase map → `trailhead-codebase-map`, Goal-backward verification → `trailhead-verify`. These agents ship with trailhead (committed `agents/trailhead-*.md`) and are registered wherever trailhead is installed as a Claude Code **plugin** (its `agents/` load automatically), or its agent files are otherwise present as subagent types. **If a `trailhead-<technique>` agent is NOT registered in the current environment** (the `Agent`/`Task` tool reports the type unknown, or it is absent from the available-agents list, as on a skills-only install that did not register the agents), **run that technique inline in the main session**, following its `techniques/` protocol directly. Do **not** divert to a host built-in (`general-purpose`/`Explore`) or any external-plugin agent as the engine target. Inline is a graceful degradation: the technique still runs, only on the session model rather than its `config.models.<key>` pin (the same inline path used when that model equals the session model). The dispatch rule is therefore: the real `trailhead-<technique>` agent when it is registered, else inline in the main session, never a foreign agent. **Pass the `config.models.<key>` model as the `Agent`-tool `model` override at spawn** so config stays authoritative (the `.md` is model-agnostic; the override wins), running inline instead when that model is unset or equals the session model. On Claude Code the `Agent` tool's `model` parameter accepts only the short family aliases (`opus` / `sonnet` / `haiku` / `fable`), not the full ids `config.models.*` stores, so **map the configured id to its family alias only for the literal `model` value passed at spawn**: `claude-opus-*` -> `opus`, `claude-sonnet-*` -> `sonnet`, `claude-haiku-*` -> `haiku`, `claude-fable-*` -> `fable`; if an id matches none of these, don't guess: warn and inherit the session model (per `configuration-reference.md`). The alias names a family, not a version, so the host resolves it to that family's current model: the exact version the id pinned cannot be conveyed to the `Agent` tool and is not honoured at subagent spawn. **Keep the inline-vs-subagent decision on the full id**, running inline only when `config.models.<key>` is unset or its full id equals the session model's, never on the alias, so a deliberate same-family but different-version pin (e.g. `claude-opus-5` under a `claude-opus-4-8` session) still dispatches instead of collapsing to inline. (Codex projects `models.codex.*`, OpenAI ids, separately and is unaffected by this.) **Never dispatch to an external-plugin agent** (a `gsd-*`, `*-reviewer`, `*-researcher`, `*-mapper`, or similarly named agent another installed tool registered): it follows *its own* protocol and artifact conventions (a foreign code-review agent writes its own review file, not trailhead's `VERIFY` ticket comment), silently swapping trailhead's engine for another tool's. Host built-ins (`Explore` / `general-purpose`) stay available in general but are **no longer the engine-dispatch target**. The tool policy is baked into each agent's `tools:` field, mirroring the old split: **read-only** (`trailhead-plan`, `trailhead-code-review`, `trailhead-codebase-map`, `trailhead-verify`: reads, searches, runs tests; no Edit/Write) vs **full** (`trailhead-executor`, `trailhead-research`, `trailhead-debug`, `trailhead-fix`: can Edit/Write/commit). On a `subagentToolkit == none` host there is no fan-out and this is moot: every technique runs inline in the one session. (Codex is **not** such a host: it has the native multi_agent toolkit, so techniques fan out there too, dispatched by `agent_type` to the projected `~/.codex/agents/trailhead-*.toml`; see the Codex adapter's §D.)
6
6
 
7
- **Reviews never block the session (engine-wide, every host).** The two **review** techniques, **Code review** (build/bug Verify) and **Cross-AI plan review** (build Plan), always run as a **background job collected by polling**, never a blind open-ended blocking wait. This is a hard default: not a `config` toggle, and not a Codex-only or fallback posture. Because trailhead works one ticket at a time and Resolve depends on the review's outcome, "background" here means a **poll loop with a one-line progress note, the session responsive and interruptible between polls**, the outcome always awaited; what is banned is the blind freeze, never "proceed without the result." On Claude Code the `Agent` tool already backgrounds the review and notifies on completion, so poll its progress instead of awaiting it blindly; on Codex, collect it through the §D bounded-wait poll loop (external plan-review CLIs via §H). **Acceptance testing is excluded**: it is interactive by design (guided UAT / browser-drive with the user in the loop), not a blind wait to background.
7
+ **Reviews never block the session (engine-wide, every host).** The three **review** techniques, **Code review** (build/bug Verify), **Goal-backward verification** (build/bug Verify), and **Cross-AI plan review** (build Plan), always run as a **background job collected by polling**, never a blind open-ended blocking wait. This is a hard default: not a `config` toggle, and not a Codex-only or fallback posture. Because trailhead works one ticket at a time and Resolve depends on the review's outcome, "background" here means a **poll loop with a one-line progress note, the session responsive and interruptible between polls**, the outcome always awaited; what is banned is the blind freeze, never "proceed without the result." On Claude Code the `Agent` tool already backgrounds the review and notifies on completion, so poll its progress instead of awaiting it blindly; on Codex, collect it through the §D bounded-wait poll loop (external plan-review CLIs via §H). **Acceptance testing is excluded**: it is interactive by design (guided UAT / browser-drive with the user in the loop), not a blind wait to background.
8
8
 
9
9
  | Technique | File | In one line |
10
10
  |---|---|---|
@@ -16,5 +16,6 @@ The ticket engines call these by name. Each technique's full protocol lives in i
16
16
  | **Codebase map** | `techniques/codebase-map.md` | one-time fan-out of 5 read-only readers, distilled into the repo's `trailhead:codebase` issue |
17
17
  | **Debug** | `techniques/debug.md` | scientific method: reproduce → localise → falsifiable hypotheses → confirm cause → verify |
18
18
  | **Code review** | `techniques/code-review.md` | review the diff on 4 axes, adversarially verify each finding before reporting |
19
+ | **Goal-backward verification** | `techniques/verify.md` | check the change delivers what the ticket promised, working backward from its stated goal |
19
20
  | **Acceptance testing** | `techniques/acceptance-testing.md` | prove it does what the *user* wanted: automated → browser-drive → guided UAT |
20
21
  | **Cross-AI plan review** | `techniques/plan-review.md` | send a PLAN to external AI CLIs, converge on their concerns (opt-in via `config.plan_review`) |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: trailhead
3
- description: "Start and drive a large project (more than one agent session can hold) as a map of decision tickets on GitHub Issues, resolving one at a time until the way to the destination is clear. Self-contained: map-based onboarding and a discuss→plan→execute→verify engine with atomic commits, with everything (map, discussion, plan, verification) living on the Issues. No dependencies beyond an authenticated gh CLI. Invoke only when the user explicitly runs `/trailhead` (or a `/trailhead:<verb>` command) or asks to chart, adopt, or work a trailhead map, and do not auto-invoke for generic planning, project, or task-tracking requests."
3
+ description: "Start and drive a large project (more than one agent session can hold) as a map of decision tickets on GitHub Issues, resolving one at a time until the way to the destination is clear. Self-contained (zero external-plugin dependencies: trailhead ships its own `trailhead-*` engine agents and needs only an authenticated gh CLI): map-based onboarding and a discuss→plan→execute→verify engine with atomic commits, with everything (map, discussion, plan, verification) living on the Issues. Invoke only when the user explicitly runs `/trailhead` (or a `/trailhead:<verb>` command) or asks to chart, adopt, or work a trailhead map, and do not auto-invoke for generic planning, project, or task-tracking requests."
4
4
  argument-hint: "[new|adopt|work|quick|whiteboard|ticket|inbox|map|dashboard|config|grill|split|pause|resume|update|bug|todo|idea|seed|note] [text | ticket]"
5
5
  ---
6
6
 
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: trailhead-capture
3
+ user-invocable: false
3
4
  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
5
  argument-hint: "[bug|todo|idea|seed|note] [--of <ticket>] <text>"
5
6
  ---
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: trailhead-chart
3
+ user-invocable: false
3
4
  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
5
  argument-hint: "[new|adopt|ticket|inbox|grill] [idea | <type> <title> | issue | topic]"
5
6
  ---
@@ -36,7 +37,7 @@ Read **`references/charting.md`** and follow it. It holds both protocols: **Mode
36
37
 
37
38
  ## Open a ticket on the fly: `ticket <type> <title>`
38
39
 
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
+ Open ticket(s) of the map on the fly, for any of the six types (`decision`, `research`, `prototype`, `build`, `bug`, `task`), the escape hatch the capture verbs do not cover. **Adding a ticket is a micro-charting act, so diverge briefly first, do not blind-commit to a single piece:** run a short breadth-first pass around the request: is this really *one* session-sized ticket, or a small **cluster** (a `decision` that needs a `research` before it, a UI `build` that needs a `prototype`, obvious siblings)? Does it imply a blocker? Surface the neighbours, *then* create the ticket(s): each gets `trailhead:ticket` plus its `trailhead:<type>`, its map's `trailhead:map-<n>` label and native sub-issue edge, a `Parent:` line, and a `## Question`; put each on the frontier, or, for a known-blocked one, create it already carrying `trailhead:blocked` (in its `--label` list) and wire its `## Blocked by` + native dependency (the three-move blocker wiring, with the label riding at creation, and the child-to-map link are in `../_shared/substrate.md`; the exact gh commands are in `../_shared/substrate-commands.md`). This is a framing brainstorm (is this the right work?), distinct from the `decision` engine's option brainstorm (which choice?). If `<type>` is missing or invalid, ask which of the six. *(The zero-friction captures, `bug` / `todo` / `idea` / `seed` / `note`, deliberately skip this diverge-first pass; they are one action, one confirmation, and are served by the capture cluster.)*
40
41
 
41
42
  ## Triage the inbox: `inbox [issue]`
42
43
 
@@ -9,7 +9,7 @@ The user invokes with a loose idea.
9
9
  1. **Name the destination.** Run the **Grilling** + **Domain vocabulary** techniques to pin down what this map tends toward. The destination fixes the scope, so it's settled first. Default: a working artifact.
10
10
  2. **Map the frontier.** Grill again, **breadth-first**: 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
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.
12
+ 4. **Create the tickets you can specify now** as child issues (each with `trailhead:ticket` + its type + this map's `trailhead:map-<n>` label, **plus `trailhead:blocked` when the mapped dependency graph already shows it has a blocker**, and set as a **native sub-issue** of the map, see **Child→map link** under Substrate), then wire the blocking in a **second pass** (issues need ids before they can reference each other): for each ticket **read its Question and wire a blocker for every still-open ticket whose output it consumes** (don't stop at the first/obvious one) by wiring its `## Blocked by` line + native dependency together (moves 1-2 of the three-move wiring; the `trailhead:blocked` label, move 3, already rode in at creation, see **Substrate**). **Set the blocked label at creation, not via a post-creation `--add-label` pass:** a bulk `--add-label` burst spawns one repo-side label-guard run per ticket and reddens the Actions history with sheddable no-op runs; riding the label in the ticket's `issues.opened` event avoids that entirely. Wiring sorts them into frontier (unblocked) and blocked; the rest stays fog.
13
13
  5. **Fire the research subagents.** For each `research` ticket created, run the **Research** technique in parallel, findings on a `research/<name>` branch with a pointer from the ticket.
14
14
  6. **Offer config, then stop.** Charting is one session's work; it hand-resolves nothing. Before ending, make the **first-time config offer** (see **First-time config offer** in `../SKILL.md`): the project now has a fresh map and no `.trailhead/config.json`, so put the **two-way choice** to the user, **accept as-is** (writes `{}`) or **run `/trailhead:config`** for the guided menu, and let them pick. Name both paths; don't pre-fill the config keys and ask for an inline edit, and don't interview them key-by-key here (that's `/trailhead:config`'s job). Once the file exists it's never offered again. **No `trailhead:codebase` issue is created here**: greenfield has no code to map yet. It's created later, once the repo has substantial code (see Mode 2 step 1 and the **Codebase** row under Substrate), so at chart two of the three permanent pins (the dashboard + conventions) are filled and the codebase slot fills in later. Maps are never pinned.
15
15
 
@@ -2,23 +2,23 @@
2
2
 
3
3
  `/trailhead:inbox [issue]` triages issues that aren't trailhead tickets (bug reports, feature requests, questions) and **integrates the worthwhile ones into the map**, keeping the reporter's authorship.
4
4
 
5
- `/trailhead:inbox` presents **three sections**:
5
+ `/trailhead:inbox` presents **three sections**, each named for what it holds. **Present them to the user by these descriptive names, never as a bare letter or "section A/B/C"** (result-oriented output: no internal labels). The three:
6
6
 
7
- **A. New: to triage.** Open issues carrying **no** `trailhead:*` label (plus any `trailhead:unverified` from the trust guard):
7
+ **Incoming issues to triage.** Open issues carrying **no** `trailhead:*` label (plus any `trailhead:unverified` from the trust guard):
8
8
  ```bash
9
9
  gh issue list --state open --json number,title,author,labels \
10
10
  | jq '[.[] | select([.labels[].name] | any(startswith("trailhead:")) | not)]'
11
11
  ```
12
12
 
13
- **B. Parked fog: any ready to graduate?** The open `trailhead:fog` issues, **most-recently-active first**: recent discussion is exactly where the fog has likely cleared, so you review those, not all of them:
13
+ **Parked fog ready to graduate?** The open `trailhead:fog` issues, **most-recently-active first**: recent discussion is exactly where the fog has likely cleared, so you review those, not all of them:
14
14
  ```bash
15
15
  gh issue list --label trailhead:fog --state open --search "sort:updated-desc"
16
16
  ```
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.
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 the parked-fog section 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 (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).
19
+ **Out-of-scope lines actually deferred?** Scan the map's *Out of scope* section for lines that are **deferred**, not truly ruled out: ones that **name a gate or trigger** (*gated by X*, *quando X*, *dipende da X*), carry a **this-only scope qualifier** (*per questo slice*, *for now*, *in this milestone*, not for the product), describe a **feature wanted later** (already annotated `→ future map`, or plainly still on the roadmap), or **are the gate of an existing `trailhead:seed`**. That last one is a **near-deterministic check**: cross-reference every open `trailhead:seed`'s trigger / `## Blocked by` against the *Out of scope* lines; a line some seed waits on is a **live gate**, so it cannot be "ruled out" and the cluster is **mis-wired** (the gate was left behind), re-route it. A line **decided against for the product** (won't be built, wrong approach, no "for this slice" qualifier) stays put; and a line **already properly parked** (annotated `→ future map` with its dependent seeds wired to a live trigger) is settled, skip it. Only **un-parked** deferred lines surface here. For each deferred line, **ask the user** (never decide silently) how to re-route it, `seed` / `idea` / `todo`, and **which map** it belongs to (this one, or a future / other map), then apply the **out-of-scope-vs-deferred** rules (see `../../_shared/out-of-scope.md`): **re-route the gate first**, tie its dependents to the gate's new form with a **live trigger** (never a dead "ruled out" line), and **move the whole cluster together**. Update or remove the *Out of scope* line to match where each item went (a future-map item stays as an annotated `→ future map` line; an item pulled into this map leaves *Out of scope* entirely).
20
20
 
21
- For each issue in either of the first two sections, decide, with the user for judgement calls, on your own for clear ones:
21
+ For each issue in the first two sections (incoming issues, parked fog), decide, with the user for judgement calls, on your own for clear ones:
22
22
 
23
23
  - **Adopt → a ticket.** Integrate it *in place* (same issue number, so the reporter stays credited): append a trailhead block to the body: `Parent: <map name+link>`, `Adopted-from: opened by @<author>`, a reframed `## Question` (the decision/build the *map* will act on), and `## Blocked by`, **without erasing the reporter's original text above it**. Apply `trailhead:ticket` + the right type label (`bug`/`decision`/`build`/`research`/`task`) as a write-access maintainer (which also satisfies Trust & provenance), plus the map's `trailhead:map-<n>` label + native sub-issue edge (like any ticket, so it joins the right map's frontier); add `trailhead:blocked` if it has an open blocker. It lands on the frontier. Comment to the reporter that it's been picked up.
24
24
  - **Fog.** In scope but not sharp enough to ticket → **keep the issue open and label it `trailhead:fog`** (parked: still visible and tracked, but the `trailhead:*` label drops it out of the inbound query, so it won't re-surface at every triage). Then use the open issue as a **clarification space**: post a comment with the questions that would make it concrete, in the spirit of **Grilling**, but async ("what would *done* look like? which users? where does it show?"), and invite the reporter and anyone interested to refine it in the thread. The fog dissipates through that conversation as much as through other work landing; others can carry the discussion with the reporter without you in the loop. **You don't lose it**: `gh issue list --label trailhead:fog` is the durable list, and the reporter follows their own open issue. When the thread (or the wider work) has made the question sharp, **adopt it in place**: swap `trailhead:fog` for `trailhead:ticket` + the type label, add `Parent:` and the now-answerable `## Question`, and it lands on the frontier: same issue, same reporter, the clarifying discussion preserved above.
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: trailhead-manage
3
+ user-invocable: false
3
4
  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
5
  argument-hint: "[config|update] [get|set …]"
5
6
  ---
@@ -39,4 +39,16 @@ Show `installed → latest` and **confirm before touching anything** (auto-insta
39
39
 
40
40
  For `dev`/`npm`, once the update lands: **refresh the check cache** so the statusline's `⬆` flag clears (`rm` the cache and re-run the hook, as in step 1), then tell the user to **restart or reload the agent** so the new skill, commands, and hooks are picked up. Report the new version plainly (`now on <latest>`).
41
41
 
42
+ ## 5. Verify the install landed cleanly, and offer to fix
43
+
44
+ After the cache refresh, run a **lightweight integrity check** (fs existence plus the version compare already in hand: no tests, no extra network) and, on any failure, **surface it and offer to fix, never fix silently**:
45
+
46
+ - **Version settled.** Re-read the refreshed cache: `updateAvailable` should now be `false` with `installed == latest`. If it is **still `true` right after a successful update**, the version sources disagree, classically the source `plugin.json` / `marketplace.json` version lagging `package.json`, so the installed VERSION marker never reaches `latest`. Report this precisely; on the **dev** channel name the diverging files so the maintainer can reconcile them in a release commit. Do **not** edit any version file here (the rule below stands): this command reports the divergence, it does not patch it.
47
+ - **Engine surface present** (re-runnable, so offer the fix). Check the install has what it should for the host, and if anything is missing say what and why it matters, then **offer to re-run the installer** to place it (ask first, never auto-run):
48
+ - **Claude**: the `trailhead-*` **agents** resolve as subagent types (present under `${CLAUDE_CONFIG_DIR:-~/.claude}/agents/`, or plugin-registered), the engine skill dirs and the `/trailhead:*` commands exist, and the hooks are registered in `settings.json`. **Absent agents are the high-value catch**: without them every technique runs inline on the session model, silently dropping the `config.models.*` split, so flag it and offer `node bin/trailhead.js --claude` (dev/npm) or `npx -y @marcomigozzi/trailhead@latest`.
49
+ - **Codex**: the projected skill under `skills/trailhead/`, the `~/.codex/agents/trailhead-*.toml` registry, and `features.multi_agent_v2` in `config.toml`; offer the `--codex` reinstall if missing.
50
+ - **All clear**: say so in one line alongside `now on <latest>` (e.g. `install verified: agents, commands, hooks, version all in place`).
51
+
52
+ The check is lightweight and degrades silently: a probe that can't run (an unreadable path, a host detail absent) is reported as unchecked, never a hard failure of the update.
53
+
42
54
  Never edit `package.json`/`plugin.json` versions here: this command **consumes** releases, it does not cut them.
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: trailhead-view
3
+ user-invocable: false
3
4
  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
5
  argument-hint: "[map|dashboard|whiteboard] [map]"
5
6
  ---
@@ -41,7 +42,7 @@ Render, by name:
41
42
  - **In progress**: claimed tickets, with who holds each.
42
43
  - **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
44
  - **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
+ - **Not yet specified** + **parked fog**: the coarse fog with **each top-level entry numbered** (`1.`, `2.`, ...; one number per top-level bullet or line, numbered top-to-bottom in displayed order and restarting at 1 on every render, nested sub-points not numbered separately), and a count/link of open `trailhead:fog` issues. The numbers are a **render-time handle**, stable within a single render and never persisted into the map body: they exist so a specific fog point can be named by its number when offering to dissolve or graduate one. Resolve any number the user gives against the **current** render's list; if the map changed since it was rendered, re-render and confirm the point before graduating it.
45
46
  - **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
47
  - **Inbox**: a count of untriaged inbound issues, as a nudge.
47
48
  - **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).
@@ -1,5 +1,6 @@
1
1
  ---
2
2
  name: trailhead-work
3
+ user-invocable: false
3
4
  description: "trailhead work cluster: the verbs that resolve tickets on the map and manage a ticket's lifecycle. Work the next frontier ticket or the one you name (work), work one ticket whole 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
5
  argument-hint: "[work|quick|pause|resume|split] [ticket | \"text\" | note]"
5
6
  ---
@@ -54,6 +55,7 @@ It runs the **full engine** (the `build`/`bug` cycle in `references/ticket-engin
54
55
  - **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
56
  - **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
57
  - **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`.
58
+ - **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.
57
59
 
58
60
  Type follows the ticket: `quick "<text>"` defaults to `build` (`bug` if a defect); `quick <n>` uses the existing type and its matching engine.
59
61
 
@@ -23,11 +23,13 @@ Raise the fidelity of the discussion with a rough, concrete artifact to react to
23
23
  ## `build`: the engine inside the map
24
24
  The ticket that *builds*. Lean cycle, all in **ticket comments**:
25
25
 
26
+ **Size triage (`effort`-gated, before Discuss).** When `config.effort` is `lean`, run the **size triage and ask-to-skip offer defined canonically under effort in `../../_shared/configuration-reference.md`** before Discuss: classify the change, and on trivial/small surface the one consolidated offer (its skippable steps here: the plan subagent -> a short inline plan; TDD; Cross-AI plan review; the full Code-review subagent -> a lighter inline pass; the Goal-backward verification subagent -> a quick inline goal check), going lean **only on an explicit yes**. On `normal`, on a no/silence/ambiguity, and always under `effort: standard` (the default), run the full cycle below unchanged. The correctness spine (atomic `Refs:` commits, the tests / the plan's criterion, the post-commit self-check, Resolve) is never in the offer.
27
+
26
28
  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.
29
+ 2. **Plan**: a `PLAN` comment: the steps, the seams, the files touched, the verification criteria. Apply **TDD** per `config.tdd` (`seams`/`on`/`off`). Produce this plan via a *planner subagent* on `config.models.plan` (dispatch the **`trailhead-plan`** agent, read-only, passing `config.models.plan` as the model override; inline when the model is unset or equals the session model; Execute then runs as its own subagent, see below). If `config.plan_review` is on, run **Cross-AI plan review** before Execute: send the plan to external AI CLIs and converge on their concerns. Now that the files/seam are known, run the **undeclared-coupling check** (`../../_shared/teamwork.md`) against in-progress tickets. **Plan to sufficiency:** stop once you can name the steps, seams, and files with confidence, and never re-read a file already read in this planning pass; act when you have enough to plan, rather than re-deriving what the pass already established. **The single exception:** under an accepted `lean` skip (see the Size triage above), produce a short inline plan here instead of the planner subagent, and skip Cross-AI plan review; absent that accepted skip, this step runs in full as described.
30
+ 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). **Route by `config.design` and never shortcut a hosted mode to a local disk mockup**: when `config.design` is `claude.ai/design` or `stitch`, the mockup is built there (reusing a cached `design.project`); `disk` is only for `config.design: disk` or the technique's explicit, announced fallback, never a convenience default. Read `config.design` before producing anything. For a redesign or new screen the default is to offer mockups; don't jump straight to code on a UI ticket. **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`** (dispatch the **`trailhead-executor`** agent, full tools that can Edit/Write/commit, passing `config.models.execute` as the model override; inline when the model is unset or equals the session model); the commits land on `main`, visible. **Before installing any new dependency, run the verify-before-install gate** (slopsquatting barrier): check the package's legitimacy (name / typosquat similarity to popular packages, age / first-seen date, download counts, repository metadata present and matching) and **stop and ask the user on a suspicious result** (block-and-ask), never installing on your own judgement. On a host with a hook bus the `trailhead-install-guard` `PreToolUse` hook enforces this, blocking a named public-registry install (npm / PyPI / crates.io / Go / RubyGems) until it is vetted; once vetted, proceed by re-running the exact install command prefixed with `TRAILHEAD_VERIFIED_INSTALL=1`. New scope surfacing mid-execute is captured/split, not worked here.
31
+ 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`); **read the review's leading `status:` tag to branch deterministically: `status: clean` -> proceed to Resolve, `status: issues_found` -> iterate the fix-and-re-review loop (or run the HITL checkpoint) before resolving**; then the **Goal-backward verification** technique on `config.models.verify` (does the change deliver what the ticket promised, backward from its `Question` and the `PLAN`'s criteria; the read-only `trailhead-verify` subagent), **run in the background and collected by polling** like Code review, branching on its leading `status:` tag (`status: met` -> proceed, `status: gaps_found` -> loop back to Execute for the gap, or run the HITL checkpoint, before resolving); 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). **The single exception:** under an accepted `lean` skip, run a lighter inline review pass instead of the full Code-review subagent and a quick inline goal check instead of the full Goal-backward verification subagent (or skip either only when the change is truly cosmetic); the tests / criterion and the honest outcome report still run regardless.
32
+ 5. **Resolve**: a resolution comment with what was done → `gh issue close` → update `Decisions so far` on the map. **Post-commit self-check (before the comment goes out).** For each commit the comment cites, confirm the hash resolves (`git cat-file -e <hash>^{commit}`, or `git show --stat <hash>`) and that every file the comment names as touched actually appears in that commit's diff (`git show --name-only --format= <hash>`); correct or drop any citation that fails the check, so a hallucinated or mis-stated commit never lands in the tracker record. **If this change materially altered the codebase** (a new module/layer, dependency, seam, or build/test command), **patch the affected facet of the `trailhead:codebase` issue** (see **Codebase** in `../../_shared/substrate.md`) 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
33
 
32
34
  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
35
 
@@ -40,11 +42,13 @@ A defect to correct. Unlike `build`, you don't start from implementation: first
40
42
 
41
43
  Then run the cycle, all in ticket comments:
42
44
 
45
+ **Size triage (`effort`-gated).** Under `config.effort: lean`, run the **size triage and ask-to-skip offer defined canonically under effort in `../../_shared/configuration-reference.md`**, classifying the fix as in the `build` engine. On trivial/small, offer to skip only the *heavy verification* a tiny fix does not warrant: lighten or skip **TDD**, run a **lighter inline Code review** instead of the full subagent, and a **quick inline goal check** instead of the full **Goal-backward verification** subagent, going lean **only on an explicit yes**. **Diagnose is never in the offer**: a bug always needs a confirmed root cause before Fix, whatever its size. On a no/silence/ambiguity, and under `effort: standard` (default), the full cycle runs.
46
+
43
47
  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
+ 2. **Diagnose**: run the **Debug** technique. A `DIAGNOSIS` comment with the root cause **stated as a falsifiable hypothesis plus the falsification test that confirmed it**. **Pre-fix checkpoint: only a confirmed cause proceeds to Fix** (a cause that has not survived its falsification test stays in diagnosis, looping the hypothesis steps); the full gate lives in the **Debug** technique (`../../_shared/techniques/debug.md`).
49
+ 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`** (dispatch the **`trailhead-fix`** agent, full tools, passing `config.models.execute` as the model override; inline when unset/same as session). Where sensible, a test that fails before and passes after (**TDD**). The **verify-before-install gate** from the `build` Execute step applies here too: vet any new dependency (typosquat similarity, age, downloads, repository metadata) and stop and ask the user on a suspicious result, before installing.
50
+ 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`); **read the review's leading `status:` tag to branch deterministically (`status: clean` -> Resolve, `status: issues_found` -> iterate the loop or run the checkpoint first)**; then the **Goal-backward verification** technique on `config.models.verify` (does the fix deliver what the ticket promised, backward from its `Question`; the read-only `trailhead-verify` subagent), background and collected by polling, branching on its leading `status:` tag (`status: met` -> proceed, `status: gaps_found` -> loop back to Fix for the gap, or run the checkpoint); **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.
51
+ 5. **Resolve**: a resolution comment (cause + fix) → `gh issue close` → update `Decisions so far`. **Run the post-commit self-check before posting, exactly as in the `build` Resolve step** (each cited hash resolves and the files it names appear in that commit's diff; correct or drop any citation that fails). **If the fix materially altered the codebase**, patch the affected facet of the `trailhead:codebase` issue (see **Codebase** in `../../_shared/substrate.md`) in place (a line or two). Then close the session with the **Session handoff** ritual (`../../_shared/session-handoff.md`): `/clear` + next command.
48
52
 
49
53
  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
54