@marcomigozzi/trailhead 0.5.0 → 0.5.1

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 (35) hide show
  1. package/README.it.md +4 -2
  2. package/README.md +4 -2
  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 +65 -10
  7. package/package.json +1 -1
  8. package/plugins/trailhead/.claude-plugin/plugin.json +1 -1
  9. package/plugins/trailhead/agents/trailhead-code-review.md +9 -0
  10. package/plugins/trailhead/agents/trailhead-codebase-map.md +9 -0
  11. package/plugins/trailhead/agents/trailhead-debug.md +9 -0
  12. package/plugins/trailhead/agents/trailhead-executor.md +9 -0
  13. package/plugins/trailhead/agents/trailhead-fix.md +9 -0
  14. package/plugins/trailhead/agents/trailhead-plan.md +9 -0
  15. package/plugins/trailhead/agents/trailhead-research.md +9 -0
  16. package/plugins/trailhead/hooks/hooks.json +4 -0
  17. package/plugins/trailhead/hooks/trailhead-install-guard.js +176 -0
  18. package/plugins/trailhead/hooks/trailhead-install-guard.test.js +118 -0
  19. package/plugins/trailhead/skills/_shared/configuration-reference.md +20 -6
  20. package/plugins/trailhead/skills/_shared/techniques/code-review.md +2 -2
  21. package/plugins/trailhead/skills/_shared/techniques/codebase-map.md +1 -1
  22. package/plugins/trailhead/skills/_shared/techniques/debug.md +4 -1
  23. package/plugins/trailhead/skills/_shared/techniques/plan-review.md +1 -1
  24. package/plugins/trailhead/skills/_shared/techniques/prototype.md +5 -2
  25. package/plugins/trailhead/skills/_shared/techniques/research.md +2 -1
  26. package/plugins/trailhead/skills/_shared/techniques.md +2 -2
  27. package/plugins/trailhead/skills/trailhead/SKILL.md +1 -1
  28. package/plugins/trailhead/skills/trailhead-capture/SKILL.md +1 -0
  29. package/plugins/trailhead/skills/trailhead-chart/SKILL.md +1 -0
  30. package/plugins/trailhead/skills/trailhead-chart/references/inbox.md +6 -6
  31. package/plugins/trailhead/skills/trailhead-manage/SKILL.md +1 -0
  32. package/plugins/trailhead/skills/trailhead-manage/references/updating.md +12 -0
  33. package/plugins/trailhead/skills/trailhead-view/SKILL.md +1 -0
  34. package/plugins/trailhead/skills/trailhead-work/SKILL.md +1 -0
  35. package/plugins/trailhead/skills/trailhead-work/references/ticket-engines.md +8 -8
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/env node
2
+ // Tests for trailhead-install-guard.js. Run: node trailhead-install-guard.test.js
3
+ // No framework: plain asserts + child_process for the end-to-end hook behaviour.
4
+ const assert = require('assert');
5
+ const { execFileSync } = require('child_process');
6
+ const path = require('path');
7
+
8
+ const HOOK = path.join(__dirname, 'trailhead-install-guard.js');
9
+ const { detectInstall, isVerified } = require('./trailhead-install-guard.js');
10
+
11
+ let passed = 0;
12
+ const ok = (name, cond) => { assert.ok(cond, name); passed++; };
13
+
14
+ // Run the hook end-to-end: pipe tool_input JSON on stdin, capture {code, out}.
15
+ function runHook(command) {
16
+ const input = JSON.stringify({ tool_input: { command } });
17
+ try {
18
+ const out = execFileSync('node', [HOOK], { input });
19
+ return { code: 0, out: out.toString() };
20
+ } catch (e) {
21
+ return { code: e.status, out: (e.stdout || '').toString(), err: (e.stderr || '').toString() };
22
+ }
23
+ }
24
+
25
+ // --- unit: detectInstall positives (a NAMED public-registry install) ---
26
+ const pos = [
27
+ ['npm install lodash', 'npm', 'lodash'],
28
+ ['npm i react', 'npm', 'react'],
29
+ ['pnpm add vite', 'npm', 'vite'],
30
+ ['yarn add left-pad', 'npm', 'left-pad'],
31
+ ['bun add hono', 'npm', 'hono'],
32
+ ['pip install requests', 'pip', 'requests'],
33
+ ['pip3 install numpy', 'pip', 'numpy'],
34
+ ['python -m pip install flask', 'pip', 'flask'],
35
+ ['python3 -m pip install flask', 'pip', 'flask'],
36
+ ['uv add httpx', 'pip', 'httpx'],
37
+ ['uv pip install httpx', 'pip', 'httpx'],
38
+ ['poetry add django', 'pip', 'django'],
39
+ ['pipx install black', 'pip', 'black'],
40
+ ['cargo add serde', 'crates', 'serde'],
41
+ ['cargo install ripgrep', 'crates', 'ripgrep'],
42
+ ['go get example.com/foo/bar', 'go', 'example.com/foo/bar'],
43
+ ['go install golang.org/x/tools/cmd/stringer', 'go', 'golang.org/x/tools/cmd/stringer'],
44
+ ['gem install rails', 'gem', 'rails'],
45
+ ];
46
+ for (const [cmd, eco, name] of pos) {
47
+ const d = detectInstall(cmd);
48
+ ok(`detects: ${cmd}`, d && d.ecosystem === eco && d.packages.includes(name));
49
+ }
50
+
51
+ // Scoped and versioned names are still names (bare name reported).
52
+ ok('detects scoped npm name', (() => { const d = detectInstall('npm install @scope/pkg'); return d && d.packages[0] === '@scope/pkg'; })());
53
+ ok('strips npm @version', (() => { const d = detectInstall('npm install lodash@4.17.21'); return d && d.packages[0] === 'lodash'; })());
54
+ ok('strips pip == specifier', (() => { const d = detectInstall('pip install requests==2.0'); return d && d.packages[0] === 'requests'; })());
55
+ ok('strips pip extra', (() => { const d = detectInstall('pip install requests[security]'); return d && d.packages[0] === 'requests'; })());
56
+ ok('scoped versioned npm keeps scope', (() => { const d = detectInstall('npm install @scope/pkg@1.2.3'); return d && d.packages[0] === '@scope/pkg'; })());
57
+
58
+ // --- unit: detectInstall negatives (no typed registry name) ---
59
+ const neg = [
60
+ 'npm install', // bare: from package.json/lock
61
+ 'npm ci', // lockfile install
62
+ 'pip install -r requirements.txt', // requirements file
63
+ 'pip install --requirement req.txt',
64
+ 'pip install -e .', // editable local
65
+ 'npm install ./local-tarball.tgz', // local path
66
+ 'npm install file:../pkg', // file: ref
67
+ 'cargo build', // not an install verb
68
+ 'go mod download', // not get/install
69
+ 'bundle install', // unrecognised binary
70
+ 'pip install git+https://github.com/x/y', // vcs url
71
+ 'npm install --save-dev', // flag only, no name
72
+ 'ls -la', // unrelated
73
+ 'go build ./...', // not get/install
74
+ ];
75
+ for (const cmd of neg) {
76
+ ok(`ignores: ${cmd}`, detectInstall(cmd) === null);
77
+ }
78
+
79
+ // `-r`'s value must not be read as a package even with a trailing name absent.
80
+ ok('consumes -r value, no phantom name', detectInstall('pip install -r requirements.txt') === null);
81
+
82
+ // Compound commands: an install in any segment is caught, not just the first.
83
+ ok('detects install after && chain', (() => { const d = detectInstall('cd app && npm install lodash'); return d && d.packages.includes('lodash'); })());
84
+ ok('detects install after ; chain', (() => { const d = detectInstall('echo hi; pip install requests'); return d && d.packages.includes('requests'); })());
85
+ ok('detects install in a piped tail', (() => { const d = detectInstall('true | cargo add serde'); return d && d.packages.includes('serde'); })());
86
+ ok('compound with no install stays null', detectInstall('cd app && npm run build && ls') === null);
87
+
88
+ // --- unit: isVerified ---
89
+ ok('sentinel recognised', isVerified('TRAILHEAD_VERIFIED_INSTALL=1 npm install lodash'));
90
+ ok('no sentinel', !isVerified('npm install lodash'));
91
+
92
+ // --- end-to-end: block ---
93
+ const b1 = runHook('npm install lodash');
94
+ ok('blocks an unvetted named install (exit 2)', b1.code === 2 && /UNVETTED_PACKAGE_INSTALL/.test(b1.out));
95
+ ok('block names the package', /lodash/.test(b1.out) && /"decision":"block"/.test(b1.out));
96
+ const b2 = runHook('pip install some-obscure-pkg');
97
+ ok('blocks a pip named install (exit 2)', b2.code === 2 && /some-obscure-pkg/.test(b2.out));
98
+
99
+ // --- end-to-end: allow ---
100
+ const a1 = runHook('TRAILHEAD_VERIFIED_INSTALL=1 npm install lodash');
101
+ ok('sentinel allows a vetted install (exit 0)', a1.code === 0 && a1.out.trim() === '');
102
+ const a2 = runHook('npm install');
103
+ ok('bare install allowed (exit 0)', a2.code === 0 && a2.out.trim() === '');
104
+ const a3 = runHook('ls -la');
105
+ ok('non-install allowed (exit 0)', a3.code === 0 && a3.out.trim() === '');
106
+ const a4 = runHook('pip install -r requirements.txt');
107
+ ok('requirements-file install allowed (exit 0)', a4.code === 0 && a4.out.trim() === '');
108
+
109
+ // --- end-to-end: unparseable stdin allowed ---
110
+ const u = (() => {
111
+ try {
112
+ const out = execFileSync('node', [HOOK], { input: 'not json' });
113
+ return { code: 0, out: out.toString() };
114
+ } catch (e) { return { code: e.status, out: (e.stdout || '').toString() }; }
115
+ })();
116
+ ok('unparseable stdin allowed (exit 0)', u.code === 0);
117
+
118
+ console.log(`✓ install-guard: ${passed} assertions passed`);
@@ -4,7 +4,7 @@ Effective config = built-in defaults ← global `~/.claude/trailhead/config.json
4
4
 
5
5
  | Key | Values (default **bold**) | Effect |
6
6
  |---|---|---|
7
- | `models.{plan,execute,research,review,debug}` | a model id (**inherit session**) | model for that activity, see note |
7
+ | `models.{plan,execute,research,review,debug}` | a model id (**Balanced-profile default**) | model for that activity; an unset key resolves to its default **Balanced** tier, see note |
8
8
  | `models.codex.{plan,execute,research,review,debug}` | an OpenAI model id (**inherit session**) | per-activity subagent model on **Codex** (OpenAI ids), the honoured namespace there; independent of `models.*` (Claude ids); projected into `~/.codex/agents/trailhead-*.toml` and dispatched by `agent_type`. See note |
9
9
  | `ticket.language` | an ISO 639-1 code (**`en`**) | the language trailhead **writes** its GitHub prose in (ticket titles/bodies, engine comments, map body & commit descriptions), see note. Independent of the language the agent converses in |
10
10
  | `design` | **`disk`** \| `claude.ai/design` \| `stitch` | mode for UI mockups (**Prototype**). `disk` = throwaway local HTML (any host); `claude.ai/design` = create/edit the mockup on claude.ai/design via Anthropic's **official Claude Design MCP** (`claude mcp add --scope user --transport http claude-design https://api.anthropic.com/v1/design/mcp`, auth `/design-login`), **Claude only**; `stitch` = create/edit the mockup via the **Google Stitch MCP** (`claude mcp add --scope user --transport http stitch https://stitch.googleapis.com/mcp`), the **cross-host** hosted option (Claude Code **and** Codex). A hosted mode's projectId is created/reused at execution and cached in `design.project`; which surface it uses is `design.surface`. If the configured mode's MCP isn't connected, Prototype offers a one-time install before falling back (see Prototype). **On Codex** `claude.ai/design` isn't available, so Prototype **offers to switch to `stitch`** (cross-host) before degrading to `disk`; `stitch` and `disk` run on Codex unchanged |
@@ -32,10 +32,22 @@ Effective config = built-in defaults ← global `~/.claude/trailhead/config.json
32
32
 
33
33
  **Inline update check (hookless hosts).** On a host without a hook bus (the descriptor's `hookBus == 'none'`) the `trailhead-check-update.js` SessionStart hook and the statusline flag are both absent, so the engine does the check **inline at session start**, co-located with the models-collapse notice above. It is lightweight and degrades silently: read the shared cache (`$XDG_CACHE_HOME/trailhead/update-check.json`, the same file the hook writes on Claude Code) and, if it reports an update, surface a one-line notice pointing at `/trailhead:update` (the pure `updateNotice(host, cache)` helper in `bin/lib/host-descriptor.js` is the canonical wording, and returns null on a host that already notifies via the hook, so it never double-fires); if the cache is absent or stale (older than a few hours), do a minimal throttled compare of the installed `VERSION` against the latest (npm registry or the GitHub release, per install channel) and rewrite the cache. Never block or fail the session on a network or parse error. `/trailhead:update` stays the on-demand installer.
34
34
 
35
- If a value is unset, inherit the session model (never hard-fail on an unknown id: warn and inherit).
35
+ If a value is unset, it resolves to its **Balanced**-profile default for that activity (the default profile: a project with no config still gets the Balanced tier per key, resolved to a concrete id at load time, strong → `claude-opus-4-8`, standard → Sonnet-class, fast → Haiku-class). To use the session model instead of the Balanced default, set the key explicitly to `inherit session`. Never hard-fail on an unknown id: warn and inherit the session model.
36
36
 
37
37
  **Everything else runs in the main session on its own model, there is no key for it:** charting, grilling, naming the destination, framing and **writing the tickets**, graduating fog, and map/decision operations. That's deliberate: it's HITL/orchestration work (you reason *with* the user), so the model that writes a ticket is the one grilling you; it can't fan out to a subagent. Practical upshot: **run a charting session (`/trailhead:new`/`:adopt`/`:ticket`) on your strong model**. The `models.*` keys above govern only the fan-out activities, not the quality of the tickets themselves. **Store a full model id** (e.g. `claude-opus-5`, `claude-fable-5`), never a bare family alias like `opus`; a family now spans several live versions, so an alias can't say which one. **`models.plan` and `models.execute` are always distinct choices**: never collapse them into one shared model pick, even when the user would pick the same id for both.
38
38
 
39
+ **Model-tier profiles.** Three profile seeds for the guided setup's Models step (§3), one philosophy: *smart at every level* (research is never the strongest tier; execute and debug are never the fastest). Choosing a profile **applies all five keys directly** (no further per-key questions); the separate **Manual** path is where `plan`/`execute`/the rest are picked one by one. **The default profile is `Balanced`** (the **(consigliato)** one): a project with no config resolves its five model keys as Balanced. **High** and **Low** are the alternatives, and **Manual** lets you set each key yourself (including `inherit session` per key). `fix` follows `execute` and codebase-map follows `research`, so neither needs its own profile entry.
40
+
41
+ | profile | plan | execute | research | review | debug |
42
+ |---|---|---|---|---|---|
43
+ | **High** | strong | strong | standard | strong | strong |
44
+ | **Balanced** | strong | standard | fast | strong | strong |
45
+ | **Low** | standard | standard | fast | fast | standard |
46
+
47
+ **Tier-class -> id**, resolved from the lineup actually reachable at setup time (verify, don't trust these verbatim):
48
+ - **Claude** (`models.*`, ids only, no effort): strong = Opus-class, resolved to **`claude-opus-4-8`** by default (the newer `claude-opus-5` stays offered in the per-key menus, but is not the default pick) · standard = Sonnet-class · fast = Haiku-class.
49
+ - **Codex** (`models.codex.*`, `{model, effort}`): strong = `gpt-5.6-sol` @ `high` (debug on **High** may be `xhigh`) · standard = `gpt-5.6-terra` @ `medium` · fast = `gpt-5.6-luna` @ `low`.
50
+
39
51
  **Ticket language.** `ticket.language` (ISO 639-1, default `en`) sets the language trailhead **writes** in on the Issues: ticket titles and bodies, engine comments (DISCUSS/PLAN/VERIFY/OPTIONS/REPRO/DIAGNOSIS/PAUSED/resolution), the map body sections, and commit message **descriptions**. It's fully decoupled from the language the agent *converses* in (its session/global setting); setting `ticket.language` never changes how the agent talks to the user. **Untouched by it:** code and identifiers, the fixed `trailhead:*` label names, config keys/values, and the conventional-commit type prefix (`feat:`/`fix:` stay English). The skill's own source docs (SKILL.md, `references/`) are not project prose and stay English.
40
52
 
41
53
  **tdd.** `seams` (default) = TDD at critical seams only; `on` = TDD for all behaviour with a definable input→output; `off` = tests after, or none for throwaway. The `build` engine's Plan/Execute honour this.
@@ -63,13 +75,15 @@ Example `config.json` (same shape for the project `.trailhead/config.json` and t
63
75
 
64
76
  1. **Scope**: 🌍 Global default (`~/.claude/trailhead/config.json`) · 📁 This project (`.trailhead/config.json` at the repo root). Skip to global outside a repo.
65
77
  2. **🌐 Ticket language**: the language trailhead writes its GitHub prose & commit descriptions in (`ticket.language`, ISO 639-1). Offer 🇬🇧 English (`en`, default) · 🇮🇹 Italiano (`it`) · 🇪🇸 Español (`es`) · 🇫🇷 Français (`fr`) · 🇩🇪 Deutsch (`de`) · 🇵🇹 Português (`pt`), plus a free-code option for any other ISO 639-1 code. Note in the prompt that this is *not* the language the agent converses in.
66
- 3. **🧠 Models**: **always two separate questions, one for `plan` and one for `execute`; never merge them into a single model pick.** Ask each as its own `AskUserQuestion` menu (current value pre-selected), and offer the **same versioned lineup** to both. Each option is a **full model id with its version**; never a bare family alias (`opus`/`sonnet`/`haiku`), which can't say *which* live version. Build the list from the models actually reachable **at setup time** (verify, don't trust this file verbatim), offering **at least the two most recent versions per family**, e.g. `claude-fable-5`, `claude-opus-5` / `claude-opus-4-8`, `claude-sonnet-5` / `claude-sonnet-4-8`, `claude-haiku-4-5`, plus **inherit session** (the default). If that lineup exceeds the menu's option cap, drill **family → version** or include a free-id "Other" so every recent version stays reachable. The rarer `research`/`review`/`debug` sit behind an "Advanced" option, each its own separate choice, same versioned rule.
67
- - **Anti-pattern, never do this:** a single screen whose options are *combined* plan+execute presets, e.g. `🧠 Plan 4.8 / Exec Sonnet 5`, `🚀 Tutto Opus 5`, `Plan Opus 5 / Exec Sonnet 5`. That bundles the two decisions into one pick. Forbidden. Plan is asked, answered, and only then is Execute asked, as a second menu. Two questions, two answers, always.
68
- - **On Codex (host-detected), ask `models.codex.*` instead of the Claude lineup.** Same discipline: `plan` and `execute` as two separate `AskUserQuestion` menus, never merged; the rarer `research`/`review`/`debug` behind Advanced, each its own choice. Offer OpenAI model ids verified reachable at setup time (do not trust this list verbatim): `gpt-5.6-sol` (flagship) / `gpt-5.6-terra` (balanced) / `gpt-5.6-luna` (fast), plus **inherit session** (default). Avoid the retiring `gpt-5.4` / `gpt-5.4-mini` and the deprecated `gpt-5.3-codex`. Write the answers to `models.codex.*`; the Claude `models.*` lineup is not asked on Codex (those ids cannot run there). After writing, tell the user to **re-run the installer** (the same `--host=codex` install command) so the pins are re-projected into `~/.codex/agents/trailhead-*.toml`: the menu updates `.trailhead/config.json`, the installer is what regenerates the TOMLs and enables `features.multi_agent_v2`.
78
+ 3. **🧠 Models**: a **profile** (Balanced by default) applied whole, or **Manual** for the per-key questions.
79
+ - **🎚️ Profile, asked first (and often the only Models question).** Present one `AskUserQuestion` menu: 🏔️ **High** · ⚖️ **Balanced (consigliato)** · 🍃 **Low** · ✍️ **Manual (specify each model)**. **On a project with no existing config, pre-select ⚖️ Balanced** (the default profile); when a config already exists, pre-select the profile its keys match (or **Manual** when they match none). **On High / Balanced / Low this is terminal:** seed the five model keys from the **Model-tier profiles** table above, resolving each tier-class to a concrete id **at setup time** (verify the lineup, don't trust ids verbatim), **write them, and do NOT ask the per-key questions**. Only **Manual** proceeds to the per-key questions below (where any key may be left at `inherit session`). **Host branch:** on **Claude** the profile seeds `models.*` with tier-class ids; on **Codex** it seeds `models.codex.*` as `{model, effort}` (effort is the Codex-only face), and after applying keep the **re-run the installer** instruction from the Codex per-key path below so the pins are re-projected into `~/.codex/agents/trailhead-*.toml`.
80
+ - **The per-key questions (Manual path only): always two separate questions, one for `plan` and one for `execute`; never merge them into a single model pick.** Ask each as its own `AskUserQuestion` menu (current value pre-selected), and offer the **same versioned lineup** to both. Each option is a **full model id with its version**; never a bare family alias (`opus`/`sonnet`/`haiku`), which can't say *which* live version. Build the list from the models actually reachable **at setup time** (verify, don't trust this file verbatim), offering **at least the two most recent versions per family**, e.g. `claude-fable-5`, `claude-opus-5` / `claude-opus-4-8`, `claude-sonnet-5` / `claude-sonnet-4-8`, `claude-haiku-4-5`, plus **inherit session** (the no-override option, the session model). If that lineup exceeds the menu's option cap, drill **family → version** or include a free-id "Other" so every recent version stays reachable. The rarer `research`/`review`/`debug` sit behind an "Advanced" option, each its own separate choice, same versioned rule.
81
+ - **Anti-pattern, never do this (in the Manual path):** a single screen whose options are *combined* plan+execute presets, e.g. `🧠 Plan 4.8 / Exec Sonnet 5`, `🚀 Tutto Opus 5`, `Plan Opus 5 / Exec Sonnet 5`. That bundles the two decisions into one pick. Forbidden. In Manual, Plan is asked, answered, and only then is Execute asked, as a second menu: two questions, two answers, always. (A **named tier profile** applied whole is **not** this anti-pattern: it is a coherent per-key tier assignment chosen deliberately, not an ad-hoc plan+execute combo, which is why picking a profile is terminal.)
82
+ - **On Codex (host-detected), the Manual path asks `models.codex.*` instead of the Claude lineup.** Same discipline: `plan` and `execute` as two separate `AskUserQuestion` menus, never merged; the rarer `research`/`review`/`debug` behind Advanced, each its own choice. Offer OpenAI model ids verified reachable at setup time (do not trust this list verbatim): `gpt-5.6-sol` (flagship) / `gpt-5.6-terra` (balanced) / `gpt-5.6-luna` (fast), plus **inherit session** (default). Avoid the retiring `gpt-5.4` / `gpt-5.4-mini` and the deprecated `gpt-5.3-codex`. Write the answers to `models.codex.*`; the Claude `models.*` lineup is not asked on Codex (those ids cannot run there). After writing, tell the user to **re-run the installer** (the same `--host=codex` install command) so the pins are re-projected into `~/.codex/agents/trailhead-*.toml`: the menu updates `.trailhead/config.json`, the installer is what regenerates the TOMLs and enables `features.multi_agent_v2`.
69
83
  4. **🎨 Design**: 💾 Local disk · 🖼️ claude.ai/design (Anthropic's official Claude Design MCP, Claude only) · 🧵 Stitch (Google Stitch MCP, cross-host: Claude Code + Codex). *Mode only, don't ask for a URL here; the concrete project is created or reused at execution time and its projectId cached in `design.project` (see Prototype).* If claude.ai/design or Stitch is picked, ask the **🖼️ Design surface**: 🖌️ Canvas (a single project of screens to react to; default; **who makes it:** on claude.ai/design you do, the `/design` Artifact or a hand-made web-app project, not MCP-driven; on Stitch the MCP generates the screens) · 🧩 Design system (push components into a shared design-system project, for a shared library; **the MCP creates and syncs it**) → `design.surface`. Then **✅ Mockup approval**: 🖐️ Explicit (wait for a go-ahead before UI code) · ⏩ Auto (proceed without blocking).
70
84
  5. **🧪 TDD**: 🎯 Seams only · ✅ On (all definable behaviour) · ⛔ Off.
71
85
  6. **🖥️ Acceptance testing**: 🤖 Auto · ▶️ Always browser · 🙋 Guided UAT only. If browser is enabled, ask **🔗 base URL** and confirm it's a web app.
72
- 7. **🧑‍⚖️ Plan review**: **before presenting this menu, first probe the PATH for known external AI CLIs** (e.g. `gemini`, `codex`, and any others the Cross-AI plan review technique recognises): run the detection, then build the menu from what was actually found. Never present this step with a hard-coded two-option list; the options are computed from the probe:
86
+ 7. **🧑‍⚖️ Plan review**: **before presenting this menu, first probe the PATH for known external AI CLIs** (probe **every** CLI in the Cross-AI plan review roster, do not shorten the list: `claude`, `gemini`, `codex`, `qwen`, `kimi`, `agy`, `opencode`, `cursor-agent`, `coderabbit`, plus any OpenAI-compatible local server configured; `kimi` counts only when `kimi --help` mentions `--output-format`, per the technique): `command -v` each one, then build the menu from what was actually found. **Exclude the current host's own CLI** (never offer `codex` when running on Codex, nor `claude` when running on Claude Code): a session reviewing a plan with its own model is not cross-AI, so the host's own CLI must never appear as an option. Never present this step with a hard-coded two-option list; the options are computed from the probe:
73
87
  - **No CLI found**: offer only ⛔ Off, and say in the prompt that no external AI CLI was detected on the PATH (so the other choices would do nothing).
74
88
  - **One CLI found**: ⛔ Off · 🤝 Use `<name>`. (With a single CLI, "all" and "pick specific" collapse to the same thing, so don't show both.)
75
89
  - **Two or more found**: ⛔ Off · 🤝 All detected (`<name>`, `<name>`, …) · 🎯 Pick specific. Always show all three; never drop *Pick specific*. On *Pick specific*, ask **which CLIs** as a **multi-select** listing each detected CLI by name (never a free-text field). List the actual names found by the probe, not the placeholder examples from this file.
@@ -1,5 +1,5 @@
1
1
  # Code review
2
- Runs after execute, before closing a `build`/`bug` ticket: as a subagent when the diff is non-trivial, inline for a tiny change. Spawn it as the built-in **`Explore`** agent (read-only: it reviews and reports, it must never Edit/Write or commit); **never** a specialised reviewer agent that another installed tool registered, which follows a different protocol and may write its own review file instead of trailhead's `VERIFY` comment.
2
+ Runs after execute, before closing a `build`/`bug` ticket: as a subagent when the diff is non-trivial, inline for a tiny change. Dispatch the **`trailhead-code-review`** agent (read-only: it reviews and reports, it must never Edit/Write or commit), passing `config.models.review` as the model override; **never** an external-plugin reviewer agent that another installed tool registered, which follows a different protocol and may write its own review file instead of trailhead's `VERIFY` comment.
3
3
 
4
4
  **Review adversarially; don't defer to authority.** A review subagent still loads the project/global `CLAUDE.md` (Claude Code doesn't strip it per-spawn; auto-memory, though, is *not* inherited). Treat those conventions (and any "this decision is settled / final / don't revisit" note) as **non-authoritative for your verdict**: judge the diff against the ticket's spec and the code itself, never rubber-stamp because a memory or a CLAUDE.md says a choice is closed. Convention endorsement may suppress a *style* smell; it may **never** suppress a correctness or security finding. The point of an independent review is to disagree when the code warrants it.
5
5
 
@@ -10,7 +10,7 @@ Review the DIFF, not the whole tree: pin a fixed point (the ticket's base commit
10
10
  - **Reuse & simplification**: hunt the classic smells, each a *judgement call* (never a hard violation), and for each name it → say how to simplify: duplicated code (extract), mysterious name (rename), primitive obsession & data clumps (introduce a type), long parameter list, feature envy / message chains (move or collapse), repeated switches, speculative generality (delete), shotgun surgery & divergent change, middle-man / shallow pass-through (delete or deepen). Suppress any a documented standard endorses; skip what tooling enforces.
11
11
  - **Standards & spec**: does it match the repo's documented conventions AND do what the ticket actually asked (missing requirements, scope creep, plausibly-wrong implementations)? Suppress smells a documented standard endorses; skip anything tooling already enforces.
12
12
 
13
- Classify each finding: **Critical** (bug/security/breaks the spec) → **Warning** (should fix) → **Info** (judgement call). Before reporting ANY finding, **verify it adversarially**: re-read the actual code, confirm the failing path is reachable and the claim holds. Discard plausible-but-wrong findings; an unverified finding is worse than none. Quote the offending hunk (and the standard/spec line) per finding. Report in the ticket's `VERIFY` comment, grouped by severity with file:line and the fix.
13
+ Classify each finding: **Critical** (bug/security/breaks the spec) → **Warning** (should fix) → **Info** (judgement call). Before reporting ANY finding, **verify it adversarially**: re-read the actual code, confirm the failing path is reachable and the claim holds. Discard plausible-but-wrong findings; an unverified finding is worse than none. Quote the offending hunk (and the standard/spec line) per finding. Report in the ticket's `VERIFY` comment, **led by a machine-checkable status tag on its own first line**: `status: clean` when no **Critical** finding remains (nothing blocks Resolve), or `status: issues_found` when one or more Criticals are still open (the caller must iterate the fix-and-re-review loop below, or run the HITL checkpoint, before resolving). Warnings/Info alone leave the tag `status: clean`, since they don't gate resolution. Emit that line verbatim as the comment's first line, then the findings grouped by severity with file:line and the fix, so the Verify step branches on the tag without parsing prose.
14
14
 
15
15
  **Converge on the blockers with a bounded budget and an explicit boundary.** A **Critical** finding is *blocking*: it must be fixed before Resolve. Fixing one can introduce or reveal another, so the review is a **loop, not a single pass**: fix every Critical, then re-review the new diff, up to `config.code_review.rounds` (default 2) fix-and-re-review rounds. Stop when no Critical remains, then Resolve, leaving Warnings/Info noted for the caller to weigh. **If the rounds are exhausted with Critical findings still open**, never resolve silently and never silently keep going: surface an explicit HITL checkpoint in the chat, **name each surviving blocker**, and put the choice to the user, (a) **run another 2 review rounds** on the remaining blockers (the point of this ask), or (b) **proceed with the blockers recorded** (resolve consciously accepting or deferring them, noting each in the `VERIFY` comment). **Wait for their pick; never auto-advance in either direction over open blocking findings.** (This mirrors **Cross-AI plan review**'s boundary ask: see `plan-review.md` step 4.)
16
16
 
@@ -1,5 +1,5 @@
1
1
  # Codebase map (fan-out)
2
- One-time at project adoption. Fan out 5 read-only reader subagents in parallel (single message), each owning ONE non-overlapping facet; no facet reads another's territory. Spawn each reader as the built-in **`Explore`** agent (read-only by construction); **never** a specialised mapper agent that another installed tool registered.
2
+ One-time at project adoption. Fan out 5 read-only reader subagents in parallel (single message), each owning ONE non-overlapping facet; no facet reads another's territory. Dispatch each reader as the **`trailhead-codebase-map`** agent (read-only by construction); **never** an external-plugin mapper agent that another installed tool registered.
3
3
  - **Architecture**: pattern, layers, data flow, entry points, module seams; where the deep modules live.
4
4
  - **Stack & dependencies**: languages, runtime, frameworks, manifests, build/config, external integrations (DBs, APIs, auth, webhooks).
5
5
  - **Decisions already embodied / conventions**: style, naming, error handling, the repo's documented standards; patterns a new change must match.
@@ -1,7 +1,7 @@
1
1
  # Debug (scientific method)
2
2
  A bug is a science problem: observe, hypothesize, test, confirm, verify. Never patch on a hunch: an unconfirmed fix is a guess that recurs. Treat the issue as persistent debug state: log every hypothesis, test, and eliminated cause as comments, so a context reset never restarts the investigation.
3
3
 
4
- When run as a subagent, spawn it as the built-in **`general-purpose`** agent (it adds instrumentation and regression tests, so it needs Write); **never** a specialised debugger agent that another installed tool registered.
4
+ When run as a subagent, dispatch the **`trailhead-debug`** agent (full tools: it adds instrumentation and regression tests, so it needs Write), passing `config.models.debug` as the model override; **never** an external-plugin debugger agent that another installed tool registered.
5
5
 
6
6
  **Redact as you go.** These notes are posted on public Issues. When you show a command, output, or artifact, replace every secret with `<REDACTED>` and run loops against env vars, never inlined credentials; quote only the signal-carrying lines, not whole dumps. If a redacted artifact isn't enough to move forward, say so and ask the user; never paste the raw one to compensate.
7
7
 
@@ -13,5 +13,8 @@ When run as a subagent, spawn it as the built-in **`general-purpose`** agent (it
13
13
  6. **Instrument: one variable at a time.** Each probe maps to a specific prediction. Prefer a debugger/REPL breakpoint over ten logs; never "log everything and grep." Tag debug logs with a unique prefix (`[DEBUG-a4f2]`) so cleanup is one grep. For perf regressions, measure a baseline first (profiler, query plan), then bisect; logs mislead here.
14
14
  7. **Confirm root cause, not symptom.** Enumerate candidate causes across ≥2 categories (code / config / environment / data). Ask the AND-gate: *could this need more than one condition at once?* If yes, a single cause can't account for the symptom: find the co-occurring cause(s); the fix must address all or it recurs. Record confirmed cause(s); append eliminated branches (never delete: auditability).
15
15
  8. **RCA branching: when a hypothesis fails.** Disproof is progress: move it to Eliminated with the evidence, test the next ranked one. If 3+ fixes failed or you can't explain current behaviour, your mental model is wrong; restart: write what's certain, what's ruled out, generate *new* hypotheses, begin again from reproduction.
16
+
17
+ **Pre-fix checkpoint: a confirmed cause, or no fix.** Before any fix is written, the `DIAGNOSIS` must record (1) the root cause stated as a *falsifiable* hypothesis and (2) the falsification test whose result would disprove it, actually run with the prediction holding. A cause that has not survived its own falsification test is still a guess: stay in diagnosis (loop steps 5–8, generating new hypotheses), never proceed to Fix. This is the hard gate on the `bug` engine's Diagnose→Fix boundary: only a confirmed cause graduates to the fix.
18
+
16
19
  9. **Verify the fix.** State the oracle before the assertion (specified > derived/model > metamorphic > implicit-crash; never settle for "didn't crash"). Write the regression test at a correct seam exercising the real bug pattern; if none can exist, that absence IS a finding: flag it. Watch it fail, apply the fix, watch it pass. Then: original repro gone, full suite green, boundary neighbours (N-1/N/N+1, empty/singleton) pass, all `[DEBUG-]` probes and throwaway harnesses removed, winning hypothesis recorded in the commit.
17
20
 
@@ -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).
@@ -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
 
@@ -1,8 +1,8 @@
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`. 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. **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`: 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
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.
8
8
 
@@ -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
  ---
@@ -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
  ---
@@ -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
  ---
@@ -24,10 +24,10 @@ Raise the fidelity of the discussion with a rough, concrete artifact to react to
24
24
  The ticket that *builds*. Lean cycle, all in **ticket comments**:
25
25
 
26
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.
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` (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.
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). **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.
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`); **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 **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. **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
31
 
32
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
33
 
@@ -41,10 +41,10 @@ A defect to correct. Unlike `build`, you don't start from implementation: first
41
41
  Then run the cycle, all in ticket comments:
42
42
 
43
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.
44
+ 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`).
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`** (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.
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`); **read the review's leading `status:` tag to branch deterministically (`status: clean` -> Resolve, `status: issues_found` -> iterate the loop or run the checkpoint first)**; **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`. **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
48
 
49
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
50