@marcomigozzi/trailhead 0.5.1 → 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.
- package/README.it.md +4 -3
- package/README.md +4 -3
- package/bin/lib/codex-projection.js +8 -8
- package/bin/lib/codex-projection.test.js +5 -5
- package/bin/trailhead.js +5 -5
- package/bin/trailhead.test.js +18 -8
- package/package.json +2 -2
- package/plugins/trailhead/.claude-plugin/plugin.json +1 -1
- package/plugins/trailhead/agents/trailhead-code-review.md +1 -0
- package/plugins/trailhead/agents/trailhead-codebase-map.md +1 -0
- package/plugins/trailhead/agents/trailhead-debug.md +1 -0
- package/plugins/trailhead/agents/trailhead-executor.md +1 -0
- package/plugins/trailhead/agents/trailhead-fix.md +1 -0
- package/plugins/trailhead/agents/trailhead-plan.md +1 -0
- package/plugins/trailhead/agents/trailhead-research.md +1 -0
- package/plugins/trailhead/agents/trailhead-verify.md +10 -0
- package/plugins/trailhead/skills/_shared/configuration-reference.md +30 -17
- package/plugins/trailhead/skills/_shared/configuration.md +1 -1
- package/plugins/trailhead/skills/_shared/session-handoff.md +1 -1
- package/plugins/trailhead/skills/_shared/substrate-commands.md +4 -2
- package/plugins/trailhead/skills/_shared/substrate.md +1 -1
- package/plugins/trailhead/skills/_shared/techniques/prototype.md +1 -1
- package/plugins/trailhead/skills/_shared/techniques/verify.md +12 -0
- package/plugins/trailhead/skills/_shared/techniques.md +3 -2
- package/plugins/trailhead/skills/trailhead-chart/SKILL.md +1 -1
- package/plugins/trailhead/skills/trailhead-chart/references/charting.md +1 -1
- package/plugins/trailhead/skills/trailhead-view/SKILL.md +1 -1
- package/plugins/trailhead/skills/trailhead-work/SKILL.md +1 -0
- package/plugins/trailhead/skills/trailhead-work/references/ticket-engines.md +7 -3
package/README.it.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
🇬🇧 [English](README.md) · 🇮🇹 **Italiano**
|
|
8
8
|
|
|
9
|
-
[](https://www.npmjs.com/package/@marcomigozzi/trailhead) [](LICENSE)   
|
|
9
|
+
[](https://trailhead.marcomigozzi.it) [](https://www.npmjs.com/package/@marcomigozzi/trailhead) [](LICENSE)   
|
|
10
10
|
|
|
11
11
|
</div>
|
|
12
12
|
|
|
@@ -296,7 +296,7 @@ Il passo **📊 statusline** offre di installare la status bar di trailhead per
|
|
|
296
296
|
| Key | Values (default **bold**) | Effetto |
|
|
297
297
|
|---|---|---|
|
|
298
298
|
| `ticket.language` | un codice ISO 639-1 (**`en`**) | la lingua in cui trailhead **scrive** la sua prosa GitHub e le descrizioni dei commit, disaccoppiata dalla lingua in cui conversa |
|
|
299
|
-
| `models.{plan,execute,research,review,debug}` | un id modello completo **versionato** (**inherit session**) | quale modello esegue ogni attività; `plan` ed `execute` sono sempre impostati separatamente |
|
|
299
|
+
| `models.{plan,execute,research,review,debug,verify,codebase-map}` | un id modello completo **versionato** (**inherit session**) | quale modello esegue ogni attività; `plan` ed `execute` sono sempre impostati separatamente |
|
|
300
300
|
| `design` | **`disk`** \| `claude.ai/design` \| `stitch` | dove vanno i mockup UI: HTML usa-e-getta locale, claude.ai/design via l'MCP ufficiale Claude Design di Anthropic, o Google Stitch (cross-host, via l'MCP Stitch) |
|
|
301
301
|
| `design.surface` | **`canvas`** \| `design-system` | sotto `claude.ai/design` o `stitch`, quale superficie: un progetto canvas (default) o un progetto design-system via `/design-sync` |
|
|
302
302
|
| `design.approval` | **`explicit`** \| `auto` | attendi l'approvazione del mockup prima del codice UI, o procedi senza bloccare |
|
|
@@ -306,11 +306,12 @@ Il passo **📊 statusline** offre di installare la status bar di trailhead per
|
|
|
306
306
|
| `plan_review` | **`off`** \| `on` \| CLI list | invia i PLAN di `build` ad AI CLI esterne (Gemini, Codex, …) per un secondo parere e converge sulle loro obiezioni |
|
|
307
307
|
| `plan_review.rounds` | integer (**`2`**) | numero massimo di round converge-and-re-review |
|
|
308
308
|
| `code_review.rounds` | integer (**`2`**) | numero massimo di round fix-and-re-review in Code review prima del checkpoint HITL di confine |
|
|
309
|
+
| `effort` | **`standard`** \| `lean` | quanto lavora il motore `build`/`bug`/`quick` su una modifica: `standard` esegue ogni step, `lean` fa una triage di dimensione sulle modifiche piccole e offre di saltare quelli pesanti (plan, TDD, cross-AI review, code review completa, verify goal-backward) |
|
|
309
310
|
|
|
310
311
|
**Models.** Ogni chiave esegue la sua attività come **subagent** sul modello che nomini, così l'intera suddivisione per-attività si applica dentro una singola sessione di lavoro, qualunque modello quella sessione esegua:
|
|
311
312
|
|
|
312
313
|
- `plan` ed `execute` sono i passi del motore build/bug. Execute è anch'esso un subagent, e committa su `main`.
|
|
313
|
-
- `research`, `review`, `debug`, e il fan-out della codebase-map girano ciascuno sulla propria chiave.
|
|
314
|
+
- `research`, `review`, `debug`, `verify` (il controllo goal-backward in Verify), e il fan-out della codebase-map girano ciascuno sulla propria chiave.
|
|
314
315
|
|
|
315
316
|
Scegli `plan` ed `execute` separatamente, ciascuno per **id completo versionato** (mai un `opus` o `sonnet` nudo). Una chiave non impostata, o uguale al modello della sessione, gira semplicemente inline.
|
|
316
317
|
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
🇬🇧 **English** · 🇮🇹 [Italiano](README.it.md)
|
|
8
8
|
|
|
9
|
-
[](https://www.npmjs.com/package/@marcomigozzi/trailhead) [](LICENSE)   
|
|
9
|
+
[](https://trailhead.marcomigozzi.it) [](https://www.npmjs.com/package/@marcomigozzi/trailhead) [](LICENSE)   
|
|
10
10
|
|
|
11
11
|
</div>
|
|
12
12
|
|
|
@@ -296,7 +296,7 @@ The **📊 statusline** step offers to install trailhead's Claude Code status ba
|
|
|
296
296
|
| Key | Values (default **bold**) | Effect |
|
|
297
297
|
|---|---|---|
|
|
298
298
|
| `ticket.language` | an ISO 639-1 code (**`en`**) | the language trailhead **writes** its GitHub prose & commit descriptions in, decoupled from the language it converses in |
|
|
299
|
-
| `models.{plan,execute,research,review,debug}` | a full **versioned** model id (**inherit session**) | which model runs each activity; `plan` and `execute` are always set separately |
|
|
299
|
+
| `models.{plan,execute,research,review,debug,verify,codebase-map}` | a full **versioned** model id (**inherit session**) | which model runs each activity; `plan` and `execute` are always set separately |
|
|
300
300
|
| `design` | **`disk`** \| `claude.ai/design` \| `stitch` | where UI mockups go: local throwaway HTML, claude.ai/design via Anthropic's official Claude Design MCP, or Google Stitch (cross-host, via the Stitch MCP) |
|
|
301
301
|
| `design.surface` | **`canvas`** \| `design-system` | under `claude.ai/design` or `stitch`, which surface: a canvas project (default) or a design-system project via `/design-sync` |
|
|
302
302
|
| `design.approval` | **`explicit`** \| `auto` | wait for mockup approval before UI code, or proceed without blocking |
|
|
@@ -306,11 +306,12 @@ The **📊 statusline** step offers to install trailhead's Claude Code status ba
|
|
|
306
306
|
| `plan_review` | **`off`** \| `on` \| CLI list | send `build` PLANs to external AI CLIs (Gemini, Codex, …) for a second opinion and converge on their concerns |
|
|
307
307
|
| `plan_review.rounds` | integer (**`2`**) | max converge-and-re-review rounds |
|
|
308
308
|
| `code_review.rounds` | integer (**`2`**) | max fix-and-re-review rounds in Code review before its boundary HITL checkpoint |
|
|
309
|
+
| `effort` | **`standard`** \| `lean` | how hard the `build`/`bug`/`quick` engine works a change: `standard` runs every step, `lean` size-triages small changes and offers to skip the heavy ones (plan pass, TDD, cross-AI review, full code review, goal-backward verify) |
|
|
309
310
|
|
|
310
311
|
**Models.** Each key runs its activity as a **subagent** on the model you name, so the whole per-activity split applies within a single work session, whatever model that session runs on:
|
|
311
312
|
|
|
312
313
|
- `plan` and `execute` are the build/bug engine's steps. Execute is a subagent too, and it commits to `main`.
|
|
313
|
-
- `research`, `review`, `debug`, and the codebase-map fan-out each run on their own key.
|
|
314
|
+
- `research`, `review`, `debug`, `verify` (the goal-backward Verify check), and the codebase-map fan-out each run on their own key.
|
|
314
315
|
|
|
315
316
|
Choose `plan` and `execute` separately, each by **full versioned id** (never a bare `opus` or `sonnet`). A key that is unset, or equal to the session model, simply runs inline.
|
|
316
317
|
|
|
@@ -101,7 +101,7 @@ Codex has a subagent toolkit: the multi_agent tools (\`spawn_agent\`, \`send_inp
|
|
|
101
101
|
|
|
102
102
|
**Never surrender the session to one open-ended \`wait_agent\`; poll instead.** \`wait_agent\` blocks until the subagent returns, so a single unconditional \`wait_agent\` on a long or stalling subagent freezes the whole session with nothing to check: this is the review / plan-review hang. Collect every delegated subagent through a **bounded wait in a poll loop**, not one open-ended blocking call. Give \`wait_agent\` a short timeout (30 to 60s); on each timeout, report a one-line "still running" progress note and loop again, so the session stays responsive and can be checked or interrupted between polls. The two **review** steps, **Code review** and **Cross-AI plan review**, always run this way: backgrounding and polling them (never a blind \`wait_agent\`) is the engine's hard cross-host default, not merely the case where it matters most. They are also the longest quiet activities in the cycle (a full-diff adversarial review, or an external CLI) and the ones most often seen to hang. If your Codex build's \`wait_agent\` exposes no timeout, poll the agent's status/output at intervals instead of blocking, or run the review as a **background job** and check back on it at intervals; the rule is the same either way. When several subagents run in parallel, poll them as a set, a bounded wait cycling across the outstanding handles, reporting which are still running.
|
|
103
103
|
|
|
104
|
-
**Per-technique agent registry (\`agent_type\` dispatch).** The installer always projects all
|
|
104
|
+
**Per-technique agent registry (\`agent_type\` dispatch).** The installer always projects all 8 committed trailhead agents as \`~/.codex/agents/trailhead-<technique>.toml\` (one per technique) and enables \`features.multi_agent_v2\`, so Codex always exposes a per-technique agent registry. A TOML whose technique has a \`models.codex.<key>\` pin set carries that OpenAI model (plus its reasoning effort); every other TOML — an unpinned keyed technique, or the 1 keyless agent — is pin-less and its subagent inherits the session model. Detect the registry at runtime by introspecting the visible \`spawn_agent\` tool schema (GSD-style, never a config read): if its parameters expose an \`agent_type\` field, spawn each delegated activity as \`spawn_agent(agent_type="trailhead-<technique>", reasoning_effort=…)\` (\`trailhead-plan\` for the Plan step, \`trailhead-execute\` for Execute, likewise research/review/debug/verify and \`trailhead-codebase-map\` for the Codebase-map step, plus the 1 keyless \`trailhead-fix\` for the Fix step), which routes it onto that technique's pinned model when one is set, or the session model otherwise. If the \`spawn_agent\` schema exposes NO \`agent_type\` field at all (a Codex build with only base multi_agent v1), spawn with base \`spawn_agent\` and the subagents inherit the one session model. Trust the tool surface you actually see and degrade gracefully: \`multi_agent_v2\` is still under development upstream, so v1 stays a supported runtime.
|
|
105
105
|
|
|
106
106
|
\`config.models.*\` (Claude model ids) still cannot take effect here regardless: Codex's subagent model registry is OpenAI-only, so a Claude id cannot run and those keys collapse to the session model. The honoured namespace on Codex is \`models.codex.*\` (above); the one-time models-collapse notice still applies while a stray \`models.*\` is set and \`models.codex.*\` is empty.
|
|
107
107
|
|
|
@@ -166,7 +166,7 @@ policy:
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
// --- CODEX_AGENT_NAME_ALIASES / codexTechniqueKey ---------------------------
|
|
169
|
-
// The
|
|
169
|
+
// The 8 committed agent sources (plugins/trailhead/agents/trailhead-*.md) are
|
|
170
170
|
// keyed by their trailhead-<name>.md filename stem. Two of those stems don't
|
|
171
171
|
// match the models.codex.<key> pin namespace, so alias them; every other
|
|
172
172
|
// agent's key is just its name with the trailhead- prefix stripped.
|
|
@@ -180,10 +180,10 @@ function codexTechniqueKey(name) {
|
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
// --- MODEL_KEYS --------------------------------------------------------------
|
|
183
|
-
// The
|
|
184
|
-
// other
|
|
183
|
+
// The 7 pinnable technique keys, mirroring the models.codex.* namespace. The
|
|
184
|
+
// other committed agent (fix) has no pin key and is always
|
|
185
185
|
// projected pin-less (session model).
|
|
186
|
-
const MODEL_KEYS = new Set(['plan', 'execute', 'research', 'review', 'debug']);
|
|
186
|
+
const MODEL_KEYS = new Set(['plan', 'execute', 'research', 'review', 'debug', 'verify', 'codebase-map']);
|
|
187
187
|
|
|
188
188
|
// --- codexAgentToml ---------------------------------------------------------
|
|
189
189
|
// Render one Codex custom-agent TOML. When `model` is empty/absent, the TOML
|
|
@@ -227,9 +227,9 @@ function codexAgentToml({ name, description, developerInstructions, model, effor
|
|
|
227
227
|
// readAgentDefs(): every committed plugins/trailhead/agents/trailhead-*.md,
|
|
228
228
|
// each { name, description, tools, body }), return the set of
|
|
229
229
|
// trailhead-<technique>.toml writes the installer should make: ONE per agent,
|
|
230
|
-
// uniformly (all
|
|
231
|
-
// review/debug) carries its models.codex.<key> pin when set, else is
|
|
232
|
-
// pin-less; the
|
|
230
|
+
// uniformly (all 8 project, always). A keyed agent (plan/execute/research/
|
|
231
|
+
// review/debug/verify/codebase-map) carries its models.codex.<key> pin when set, else is
|
|
232
|
+
// pin-less; the 1 keyless agent (fix) is always pin-less.
|
|
233
233
|
// Iterates agentDefs in the given order (readAgentDefs sorts by name) so the
|
|
234
234
|
// write order is deterministic.
|
|
235
235
|
function codexAgentTomlPlan(codexHome, codexModels, agentDefs) {
|
|
@@ -231,9 +231,9 @@ ok('codexAgentToml escapes a " and \\ in name/description so the TOML stays vali
|
|
|
231
231
|
|
|
232
232
|
// --- codexAgentTomlPlan ---
|
|
233
233
|
// 7-entry stub agentDefs mirroring the real plugins/trailhead/agents/*.md
|
|
234
|
-
// sources:
|
|
235
|
-
// filenames derive from trailhead-executor -> execute and
|
|
236
|
-
// trailhead-code-review -> review) +
|
|
234
|
+
// sources: 6 keyed techniques (plan/execute/research/review/debug/codebase-map,
|
|
235
|
+
// whose filenames derive from trailhead-executor -> execute and
|
|
236
|
+
// trailhead-code-review -> review) + 1 always-keyless agent (fix).
|
|
237
237
|
const STUB_AGENT_DEFS = [
|
|
238
238
|
{ name: 'trailhead-plan', description: 'plan desc', tools: 'Read', body: 'Plan body.' },
|
|
239
239
|
{ name: 'trailhead-executor', description: 'execute desc', tools: 'Read, Write', body: 'Execute body.' },
|
|
@@ -255,14 +255,14 @@ ok('codexAgentTomlPlan: 7 agentDefs -> 7 writes with only the pinned key carryin
|
|
|
255
255
|
return others.length === 6 && others.every((w) => !/^model = /m.test(w.content));
|
|
256
256
|
})());
|
|
257
257
|
|
|
258
|
-
ok('codexAgentTomlPlan: keyless trailhead-fix
|
|
258
|
+
ok('codexAgentTomlPlan: keyless trailhead-fix stays pin-less; trailhead-codebase-map now pins on codebase-map', (() => {
|
|
259
259
|
const plan = codexAgentTomlPlan('/c', { fix: 'gpt-5.6-terra', 'codebase-map': 'gpt-5.6-terra' }, STUB_AGENT_DEFS);
|
|
260
260
|
const byName = Object.fromEntries(plan.writes.map((w) => [w.name, w]));
|
|
261
261
|
const fix = byName['trailhead-fix'];
|
|
262
262
|
const map = byName['trailhead-codebase-map'];
|
|
263
263
|
return fix && map &&
|
|
264
264
|
fix.path === '/c/agents/trailhead-fix.toml' && !/^model = /m.test(fix.content) &&
|
|
265
|
-
map.path === '/c/agents/trailhead-codebase-map.toml' &&
|
|
265
|
+
map.path === '/c/agents/trailhead-codebase-map.toml' && map.content.includes('model = "gpt-5.6-terra"');
|
|
266
266
|
})());
|
|
267
267
|
|
|
268
268
|
ok('codexAgentTomlPlan: empty models object -> 7 pin-less writes (not zero)', (() => {
|
package/bin/trailhead.js
CHANGED
|
@@ -409,7 +409,7 @@ function unquoteYamlScalar(v) {
|
|
|
409
409
|
return v.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
|
410
410
|
}
|
|
411
411
|
|
|
412
|
-
// Read the
|
|
412
|
+
// Read the 8 committed agent sources from plugins/trailhead/agents/*.md: each
|
|
413
413
|
// file's basename is the agent's name; frontmatter description/tools ride
|
|
414
414
|
// along, and `body` is the markdown after the closing frontmatter `---`.
|
|
415
415
|
// Single-sources the Codex agent-TOML projection so it never drifts from the
|
|
@@ -549,12 +549,12 @@ function installCodex(configDir, { useSymlink }) {
|
|
|
549
549
|
featureManual = true;
|
|
550
550
|
}
|
|
551
551
|
|
|
552
|
-
// Per-technique agent registry: project ALL
|
|
552
|
+
// Per-technique agent registry: project ALL 8 committed agents
|
|
553
553
|
// (readAgentDefs, sourced from plugins/trailhead/agents/*.md) as
|
|
554
554
|
// trailhead-<technique>.toml under the real ~/.codex/agents/ dir, uniformly.
|
|
555
|
-
// A keyed technique (plan/execute/research/review/debug)
|
|
556
|
-
// models.codex.<key> pin (merged project + global config) when
|
|
557
|
-
// pin-less; the
|
|
555
|
+
// A keyed technique (plan/execute/research/review/debug/verify/codebase-map)
|
|
556
|
+
// carries its models.codex.<key> pin (merged project + global config) when
|
|
557
|
+
// set, else is pin-less; the 1 keyless agent (fix) is always pin-less.
|
|
558
558
|
// Sweep only trailhead's own files first, so a stale pin from a prior
|
|
559
559
|
// install (or a removed technique) never lingers, without touching the
|
|
560
560
|
// user's other custom-agent TOMLs.
|
package/bin/trailhead.test.js
CHANGED
|
@@ -131,7 +131,7 @@ ok('codex: skills/trailhead/hooks/lib/commit-message-check.js exists (commit-gua
|
|
|
131
131
|
const codexConfigTomlPath = path.join(codexDir, 'config.toml');
|
|
132
132
|
ok('codex: config.toml exists', fs.existsSync(codexConfigTomlPath));
|
|
133
133
|
ok('codex: config.toml enables hooks feature', fs.readFileSync(codexConfigTomlPath, 'utf8').includes('hooks = true'));
|
|
134
|
-
// All
|
|
134
|
+
// All 8 agents are always projected uniformly (pinned or pin-less), so
|
|
135
135
|
// multi_agent_v2 is ON even with no models.codex.* set (this install,
|
|
136
136
|
// repoRoot, sets none): the adapter §D registry is always present.
|
|
137
137
|
ok('codex: config.toml enables multi_agent_v2 even with no models.codex.* pins projected',
|
|
@@ -170,17 +170,27 @@ ok('codex copy: templates is a regular dir (not a symlink)',
|
|
|
170
170
|
!fs.lstatSync(path.join(codexDir, 'skills', 'trailhead', 'templates')).isSymbolicLink());
|
|
171
171
|
|
|
172
172
|
// --- codex agent TOML projection (#38, #88) -------------------------------------
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
173
|
+
// With NO models.codex.* set, a codex install still projects all 8 agents
|
|
174
|
+
// uniformly, pin-less (uniform projection, #88), never zero. Install from an
|
|
175
|
+
// isolated cwd whose .trailhead/config.json sets no models.codex.*, not
|
|
176
|
+
// repoRoot (whose own config now pins models.codex.*, so it would project
|
|
177
|
+
// pinned TOMLs rather than pin-less ones).
|
|
178
|
+
const unsetProjectDir = mktmp();
|
|
179
|
+
fs.mkdirSync(path.join(unsetProjectDir, '.trailhead'), { recursive: true });
|
|
180
|
+
fs.writeFileSync(
|
|
181
|
+
path.join(unsetProjectDir, '.trailhead', 'config.json'),
|
|
182
|
+
JSON.stringify({ models: {} }, null, 2) + '\n'
|
|
183
|
+
);
|
|
184
|
+
const unsetCodexHomeDir = mktmp();
|
|
185
|
+
runInstaller([`--codex`, `--dir=${unsetCodexHomeDir}`], { cwd: unsetProjectDir });
|
|
186
|
+
ok('codex: all 8 trailhead-*.toml exist under agents/ even with models.codex.* unset', (() => {
|
|
187
|
+
const dir = path.join(unsetCodexHomeDir, 'agents');
|
|
178
188
|
if (!fs.existsSync(dir)) return false;
|
|
179
189
|
const tomls = fs.readdirSync(dir).filter((f) => /^trailhead-.*\.toml$/.test(f));
|
|
180
|
-
return tomls.length ===
|
|
190
|
+
return tomls.length === 8;
|
|
181
191
|
})());
|
|
182
192
|
ok('codex: every trailhead-*.toml is pin-less when models.codex.* is unset', (() => {
|
|
183
|
-
const dir = path.join(
|
|
193
|
+
const dir = path.join(unsetCodexHomeDir, 'agents');
|
|
184
194
|
const tomls = fs.readdirSync(dir).filter((f) => /^trailhead-.*\.toml$/.test(f));
|
|
185
195
|
return tomls.every((f) => !/^model = /m.test(fs.readFileSync(path.join(dir, f), 'utf8')));
|
|
186
196
|
})());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marcomigozzi/trailhead",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Start and drive large projects as a map of tickets on GitHub Issues. Map-based onboarding + a discuss→plan→execute→verify engine, all on the Issues. Installs the trailhead skill, commands, and hooks into your agent's config dir.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"trailhead": "bin/trailhead.js"
|
|
@@ -30,5 +30,5 @@
|
|
|
30
30
|
"type": "git",
|
|
31
31
|
"url": "git+https://github.com/ToRvaLDz/trailhead.git"
|
|
32
32
|
},
|
|
33
|
-
"homepage": "https://
|
|
33
|
+
"homepage": "https://trailhead.marcomigozzi.it"
|
|
34
34
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trailhead",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Start and drive large projects as a map of tickets on GitHub Issues, resolving one at a time. Map-based onboarding + a discuss→plan→execute→verify engine with atomic commits, all on the Issues. Ships as a dispatcher plus cohesion-clustered skills over a shared core; depends on nothing beyond an authenticated gh CLI.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Marco Migozzi",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: trailhead-executor
|
|
3
|
+
color: orange
|
|
3
4
|
description: "trailhead execute subagent: implements an approved PLAN with atomic conventional commits (each carrying a Refs: #<n> trailer)."
|
|
4
5
|
tools: Read, Write, Edit, Grep, Glob, Bash, WebFetch, WebSearch, TodoWrite
|
|
5
6
|
---
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: trailhead-plan
|
|
3
|
+
color: blue
|
|
3
4
|
description: "trailhead planning subagent: produces a build ticket's PLAN (steps, seams, files touched, verification criteria). Read-only, never implements."
|
|
4
5
|
tools: Read, Grep, Glob, Bash, WebFetch, WebSearch, TodoWrite
|
|
5
6
|
---
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: trailhead-verify
|
|
3
|
+
color: pink
|
|
4
|
+
description: "trailhead verify subagent: goal-backward check that a ticket's change delivers what it promised. Read-only, never edits or commits."
|
|
5
|
+
tools: Read, Grep, Glob, Bash, WebFetch, WebSearch, TodoWrite
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
trailhead's verification subagent. Run the Goal-backward verification: check that the ticket's implemented change actually delivers what the ticket promised (its `Question` and the `PLAN`'s verification criteria), not merely that steps ran or the tests are green. Report the verdict into trailhead's `VERIFY` comment with a leading `status:` tag.
|
|
9
|
+
|
|
10
|
+
The full protocol is injected at spawn from the trailhead skill's single source (`_shared/techniques/verify.md`); follow that, not any embedded copy. Read-only by policy: it verifies and reports, it must never Edit/Write or commit. Model-agnostic by design: the engine passes the `config.models.verify` model as the spawn-time override, so config stays authoritative.
|
|
@@ -4,8 +4,8 @@ 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 (**Balanced-profile default**) | model for that activity; an unset key resolves to its default **Balanced** tier, see note |
|
|
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 |
|
|
7
|
+
| `models.{plan,execute,research,review,debug,verify,codebase-map}` | a model id (**Balanced-profile default**) | model for that activity; an unset key resolves to its default **Balanced** tier, see note |
|
|
8
|
+
| `models.codex.{plan,execute,research,review,debug,verify,codebase-map}` | 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 |
|
|
11
11
|
| `design.surface` | **`canvas`** \| `design-system` | under a hosted `design` mode (`claude.ai/design` or `stitch`), which surface the mockup lives on. `canvas` = a single surface of screens to react to (under `claude.ai/design`: a `/design` **Artifact** or a hand-made web-app project, **not** MCP-created; under `stitch`: a Stitch project), editable visually and exportable as a live prototype. `design-system` = push components into a shared design-system project (a `PROJECT_TYPE_DESIGN_SYSTEM` synced via DesignSync `/design-sync`, or a Stitch design system), for a shared component library. **Back-compat:** unset, a cached `design.project` infers `design-system` (that's what the key meant before this key existed); with no cached project, `canvas`. See Prototype |
|
|
@@ -16,17 +16,20 @@ Effective config = built-in defaults ← global `~/.claude/trailhead/config.json
|
|
|
16
16
|
| `plan_review` | **`off`** \| `on` \| CLI list (`gemini,codex`) | send `build` PLANs to external AI CLIs for review (**Cross-AI plan review**) |
|
|
17
17
|
| `plan_review.rounds` | integer (**`2`**) | max converge-and-re-review rounds |
|
|
18
18
|
| `code_review.rounds` | integer (**`2`**) | max fix-and-re-review rounds in **Code review** before the boundary HITL checkpoint |
|
|
19
|
+
| `effort` | **`standard`** \| `lean` | how hard the `build`/`bug`/`quick` engine works a change: `standard` runs every step; `lean` size-triages and offers to skip heavy steps (plan pass, TDD, cross-AI review, full code review, goal-backward verify) on small changes |
|
|
19
20
|
|
|
20
21
|
**Models.** Each key targets a specific activity, so you can send the heavy reasoning to a strong model and the cheap legwork to a fast one:
|
|
21
22
|
- `plan` / `execute`: the `build`/`bug` engine's **Plan** and **Execute** steps (see below).
|
|
22
|
-
- `research`: the **Research** subagents
|
|
23
|
+
- `research`: the **Research** subagents.
|
|
24
|
+
- `codebase-map`: the **Codebase map** reader fan-out (the read-only `trailhead-codebase-map` subagent), split from `research` so the mapper can run on its own tier.
|
|
23
25
|
- `review`: the **Code review** subagent.
|
|
26
|
+
- `verify`: the **Goal-backward verification** subagent (`trailhead-verify`) in the `build`/`bug` **Verify** step: does the change deliver what the ticket promised?
|
|
24
27
|
- `debug`: the **Debug** technique's diagnosis in the `bug` engine.
|
|
25
28
|
|
|
26
29
|
**Every model key runs as a subagent, so all of them apply in one session whatever model it runs on.** The main session is the **orchestrator**: it holds the genuinely interactive moments, the `build`/`bug` **Discuss** (the "stop and ask" on blocking ambiguity, *before* implementing) and **Verify**'s acceptance/UAT (*after*), and fans the rest out:
|
|
27
30
|
- `plan` → a *planner subagent* on `models.plan` (inline when it's unset or equals the session model).
|
|
28
31
|
- `execute` → an **executor subagent** that implements the plan with atomic commits on `models.execute`. Execute itself is autonomous (no live user exchange happens *during* implementation; new scope surfacing is captured/split, not grilled), so it fans out cleanly; the commits still land on `main`, visible. Run it **inline in the session instead when `models.execute` is unset or equals the session model** (nothing to gain from spawning).
|
|
29
|
-
- `research`/`review`/`debug
|
|
32
|
+
- `research`/`review`/`debug`/`verify`/`codebase-map` → their own subagents.
|
|
30
33
|
|
|
31
34
|
**Two independent host facts govern this, keyed on the descriptor (`bin/lib/host-descriptor.js`), not on a host name.** (1) A **subagent-less host** (`subagentToolkit == none`) has nothing to fan out into, so **every activity runs inline and sequentially** in the one session. (2) A host that **can't honour the model keys** (`honorsModelKeys == false`) can't run a subagent on a trailhead `models.*` value, so **every `models.*` key collapses to the single session model**. The two are independent: **Codex** is `subagentToolkit == full` (it fans out, via the native multi_agent toolkit) yet `honorsModelKeys == false` (its subagent model registry is OpenAI-only, so a Claude id like `claude-sonnet-5` can't run there and every subagent inherits the one session model). So on Codex the techniques *do* fan out; only the per-activity model *choice* is lost. The keys **stay in `.trailhead/config.json` untouched**: Claude Code honours them, a host that can't simply ignores them, and they apply again the next time the project runs on Claude Code. Nothing is stripped from the Claude `models.*` keys: they carry no `agents/*.toml` of their own and simply stay in config, ignored on Codex. The per-subagent OpenAI-model pinning once described here as a future upgrade has since shipped for `models.codex.*`: when those keys are set the installer emits one `~/.codex/agents/trailhead-<technique>.toml` per key and enables `features.multi_agent_v2`, and the runtime dispatches each activity by `agent_type` (adapter §D). When `models.*` is set while running on a host that can't honour it, the engine surfaces a **one-time notice** at session start (co-located with the inline check-update), so the user knows their per-activity split is not taking effect here; it fires once per project, tracked by a gitignored `.trailhead/models-collapse-notice` marker. On Codex the per-activity split is not lost outright, it moves to a parallel `models.codex.*` namespace holding OpenAI model ids (each key a bare model id, or a `{model, effort}` pair projected as `model_reasoning_effort`); `models.*` (Claude ids) still collapses to the session model as described above. The one-time collapse notice now points the user at `models.codex.*` as the fix, and stays silent once any `models.codex.*` key is set, since that means the user has already opted into the honoured namespace there.
|
|
32
35
|
|
|
@@ -34,15 +37,15 @@ Effective config = built-in defaults ← global `~/.claude/trailhead/config.json
|
|
|
34
37
|
|
|
35
38
|
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
39
|
|
|
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.
|
|
40
|
+
**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. (On Claude Code the `Agent` tool takes only a family alias, so at subagent spawn the stored id is mapped to its alias, see the dispatch rule in `techniques.md`; the full id is still what gets stored, so the exact version stays on record and drives the inline-vs-subagent decision, even though the alias-only spawn cannot convey that version to the subagent.) **`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
41
|
|
|
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
|
|
42
|
+
**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 seven 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 seven 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`, so it needs no profile entry of its own.
|
|
40
43
|
|
|
41
|
-
| profile | plan | execute | research | review | debug |
|
|
42
|
-
|
|
43
|
-
| **High** | strong | strong | standard | strong | strong |
|
|
44
|
-
| **Balanced** | strong | standard |
|
|
45
|
-
| **Low** | standard | standard | fast | fast | standard |
|
|
44
|
+
| profile | plan | execute | research | review | debug | verify | codebase-map |
|
|
45
|
+
|---|---|---|---|---|---|---|---|
|
|
46
|
+
| **High** | strong | strong | standard | strong | strong | strong | standard |
|
|
47
|
+
| **Balanced** | strong | standard | standard | standard | standard | standard | fast |
|
|
48
|
+
| **Low** | standard | standard | fast | fast | standard | fast | fast |
|
|
46
49
|
|
|
47
50
|
**Tier-class -> id**, resolved from the lineup actually reachable at setup time (verify, don't trust these verbatim):
|
|
48
51
|
- **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.
|
|
@@ -62,6 +65,15 @@ If a value is unset, it resolves to its **Balanced**-profile default for that ac
|
|
|
62
65
|
|
|
63
66
|
**code_review.rounds.** Integer, default 2. The max fix-and-re-review rounds the **Code review** technique spends chasing **Critical** (blocking) findings during the `build`/`bug` Verify step. When the budget is exhausted with Critical findings still open, the technique never resolves or continues silently: it surfaces an explicit HITL checkpoint (run another 2 rounds vs proceed with the blockers recorded). Mirrors `plan_review.rounds`.
|
|
64
67
|
|
|
68
|
+
**effort (cost-aware gating).** `standard` (default) = the full flow: every `build`/`bug`/`quick` step runs at full cost, no triage (today's behaviour, backward-compatible). `lean` = **cost-aware**, defined **canonically here**; the `build`/`bug` engine and `quick` reference this section and add only their own timing. Under `lean` the engine runs a **size triage** in the orchestrator (main session, inline, no subagent, so the triage itself costs almost nothing), classifying the change predictively from the ticket scope and a quick glance at the target files (the real diff is unknown before Execute):
|
|
69
|
+
- **trivial**: a single file, a handful of lines, no new seam, no new dependency, no architectural change (a copy/CSS/doc/constant edit).
|
|
70
|
+
- **small**: localized, one or two files, within existing seams, no new dependency, no architectural change.
|
|
71
|
+
- **normal**: anything else. **Risk floor:** a change is **never** trivial/small, however few its lines, when it touches security or auth, a data migration, a public API or contract, money or permissions, or concurrency, or when it adds or updates a dependency; such a change is always `normal`. When in doubt, classify up.
|
|
72
|
+
|
|
73
|
+
On **trivial/small** the engine surfaces **one consolidated skip offer** and acts only on an **explicit opt-in**: it names the concrete heavy steps proposed to skip for *this* change and the safety trade-off, then goes lean **only on a clear yes**. On a no, on silence, or on any ambiguity, it keeps the **full flow** (the fail-safe is always toward more checking, never less). The heavy steps it may offer to skip are the **plan subagent** (replaced by a short inline plan, never zero plan), **TDD**, **Cross-AI plan review**, the full adversarial **Code review** subagent (replaced by a lighter inline pass), and the **Goal-backward verification** subagent (replaced by a quick inline goal check); the two inline replacements are dropped entirely only when the change is truly cosmetic. The offer lists **only steps actually in play** for this ticket and config (it will not mention Cross-AI plan review when `plan_review` is off, or TDD when `tdd: off`). On **normal**, and always under `effort: standard`, there is no offer and the full flow runs.
|
|
74
|
+
|
|
75
|
+
**Never in the offer** (always kept, whatever the size or the answer): atomic `Refs:` commits, running the tests / the plan's verification criterion, the post-commit self-check, `bug`'s Diagnose (a confirmed root cause is mandatory before Fix), and Resolve. `effort` scales *redundant heavy* work to the change; it never drops a correctness check.
|
|
76
|
+
|
|
65
77
|
Example `config.json` (same shape for the project `.trailhead/config.json` and the global `~/.claude/trailhead/config.json`):
|
|
66
78
|
```json
|
|
67
79
|
{ "models": { "plan": "claude-opus-4-8", "execute": "claude-sonnet-5" }, "tdd": "seams", "acceptance": { "browser": "auto" } }
|
|
@@ -76,20 +88,21 @@ Example `config.json` (same shape for the project `.trailhead/config.json` and t
|
|
|
76
88
|
1. **Scope**: 🌍 Global default (`~/.claude/trailhead/config.json`) · 📁 This project (`.trailhead/config.json` at the repo root). Skip to global outside a repo.
|
|
77
89
|
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.
|
|
78
90
|
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
|
|
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.
|
|
91
|
+
- **🎚️ 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 seven 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`.
|
|
92
|
+
- **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`/`verify`/`codebase-map` sit behind an "Advanced" option, each its own separate choice, same versioned rule.
|
|
81
93
|
- **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`.
|
|
94
|
+
- **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`/`verify`/`codebase-map` 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`.
|
|
83
95
|
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).
|
|
84
96
|
5. **🧪 TDD**: 🎯 Seams only · ✅ On (all definable behaviour) · ⛔ Off.
|
|
85
|
-
6.
|
|
86
|
-
7.
|
|
97
|
+
6. **⚖️ Effort**: how hard the build engine works each change (`effort`). 🛠️ **Standard** (default; every step runs) · 🍃 **Lean** (size-triage small changes and offer to skip heavy steps, on an explicit yes only: the plan pass, TDD, cross-AI review, the full code review, the goal-backward verify; correctness checks always stay).
|
|
98
|
+
7. **🖥️ Acceptance testing**: 🤖 Auto · ▶️ Always browser · 🙋 Guided UAT only. If browser is enabled, ask **🔗 base URL** and confirm it's a web app.
|
|
99
|
+
8. **🧑⚖️ 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:
|
|
87
100
|
- **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).
|
|
88
101
|
- **One CLI found**: ⛔ Off · 🤝 Use `<name>`. (With a single CLI, "all" and "pick specific" collapse to the same thing, so don't show both.)
|
|
89
102
|
- **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.
|
|
90
103
|
|
|
91
104
|
**Anything but Off has mandatory follow-ups, never skip them:** after the CLIs are settled (whether via *All detected* or *Pick specific*), ask **🔁 rounds** (default 2). Off is the only branch with no follow-up; every other path asks rounds explicitly rather than taking the default.
|
|
92
|
-
|
|
105
|
+
9. **📊 Statusline**: offer trailhead's **Claude Code status bar**, and **say in the prompt what it shows** so the choice is informed. It renders one line: **model · project · branch · plan usage (`5h %` · `⏳ reset` · `7d %`) · a context-window bar (`362k/1000k (45%)`)**, plus a **second line with the active ticket** (`▸ #N Title`) whenever one is in play (from the `trailhead/t<n>` branch or the `.trailhead/session-ticket` marker). The plan-usage % comes from Anthropic's own usage endpoint (the same source the Claude UI uses); the context bar and ticket are computed locally. Options: ⛔ **Off** (default; leave your statusline untouched) · ✅ **Enable**.
|
|
93
106
|
- On **Enable**, install it: copy the shipped script (`${CLAUDE_PLUGIN_ROOT}/templates/statusline-trailhead.sh` for a plugin install, or `~/.claude/trailhead/templates/statusline-trailhead.sh` for an npm install, whichever exists) to `~/.claude/statusline-trailhead.sh` (executable), then set `statusLine` in `~/.claude/settings.json` to `{ "type": "command", "command": "bash \"$HOME/.claude/statusline-trailhead.sh\"" }`. **This is a global Claude Code setting, not per-project**, so it always lands in `~/.claude/settings.json` regardless of the scope chosen in step 1.
|
|
94
107
|
- **If a `statusLine` is already configured** (e.g. `ccstatusline`), **do not clobber it silently**: show what it is and ask whether to **replace** it with trailhead's, or **keep** the current one (in which case, mention that the script also supports single-segment widgets, `--ticket-only` / `--context-only` / `--usage-only`, to add into an existing statusline tool). Needs `jq` and `python3` on the PATH; if either is missing, say so and skip the enable rather than wiring a broken command.
|
|
95
108
|
|
|
@@ -5,4 +5,4 @@ Three layers, **nearest wins**, key by key; a key unset at one layer inherits th
|
|
|
5
5
|
- **Global**: `~/.claude/trailhead/config.json` (honour `$CLAUDE_CONFIG_DIR`): your standing defaults across every project.
|
|
6
6
|
- **Defaults**: the built-in values documented in `configuration-reference.md`.
|
|
7
7
|
|
|
8
|
-
Load the effective config at the start of a work session, **from the map's project root** (`.trailhead/config.json` at the superproject repo root) plus the global file, **before** any isolation workspace is set up. **Config is a property of the project/map, not the working directory**: an `isolation: worktree`/`clone` workspace **inherits** the config loaded here and must **never re-resolve it from the isolated copy's own root**, because a per-ticket clone (a submodule clone especially) has no `.trailhead/` of its own and would silently fall back to defaults (this is exactly how a `design: claude.ai/design` project ends up wrongly using `disk` in a clone). Config **writes** too (e.g. caching `design.project`) target the project's `.trailhead/config.json` in the original checkout, never the clone. **The main session orchestrates and holds the HITL moments** (the `build`/`bug` Discuss's "stop and ask", Verify's acceptance/UAT); **every model key runs as a subagent** (`plan`, `execute` (an *executor subagent* implementing with atomic commits on `models.execute`), `research`, `review`, `debug`) so the whole per-activity model split applies in one session whatever model it runs on. Run Execute inline only when `models.execute` is unset or equals the session model. **On a subagent-less host** (`subagentToolkit == none`) there is nothing to fan out into: every activity runs inline. **Separately, on a host that can't honour the model keys** (`honorsModelKeys == false`, e.g. Codex, whose subagents run on its own OpenAI models) every `models.*` key collapses to the single session model even though the activities still fan out; when any `models.*` is set there the engine surfaces a **one-time** session-start notice that the split is not taking effect (the keys stay in `.trailhead/config.json` for when the project next runs on Claude Code). On Codex the per-activity split is available through a parallel `models.codex.*` namespace (OpenAI model ids), the honoured namespace there (projected into per-technique `~/.codex/agents/*.toml` and dispatched at runtime by `agent_type`), while `models.*` (Claude ids) still collapse to the single session model; the collapse notice points at `models.codex.*` and goes silent once it is set. A host without a hook bus (`hookBus == none`) also runs a lightweight inline update check at session start (no SessionStart hook, no statusline). See `configuration-reference.md`. The **keys, their values, the model/tdd/acceptance semantics, and the guided menu setup live in `configuration-reference.md`**: read it when running `/trailhead:config` or when you need a key's exact meaning. `/trailhead:config` with no args runs the guided setup; `config get` prints the merged config; `config set <key> <value>` writes one key.
|
|
8
|
+
Load the effective config at the start of a work session, **from the map's project root** (`.trailhead/config.json` at the superproject repo root) plus the global file, **before** any isolation workspace is set up. **Config is a property of the project/map, not the working directory**: an `isolation: worktree`/`clone` workspace **inherits** the config loaded here and must **never re-resolve it from the isolated copy's own root**, because a per-ticket clone (a submodule clone especially) has no `.trailhead/` of its own and would silently fall back to defaults (this is exactly how a `design: claude.ai/design` project ends up wrongly using `disk` in a clone). Config **writes** too (e.g. caching `design.project`) target the project's `.trailhead/config.json` in the original checkout, never the clone. **The main session orchestrates and holds the HITL moments** (the `build`/`bug` Discuss's "stop and ask", Verify's acceptance/UAT); **every model key runs as a subagent** (`plan`, `execute` (an *executor subagent* implementing with atomic commits on `models.execute`), `research`, `review`, `debug`, `verify`, `codebase-map`) so the whole per-activity model split applies in one session whatever model it runs on. Run Execute inline only when `models.execute` is unset or equals the session model. **On a subagent-less host** (`subagentToolkit == none`) there is nothing to fan out into: every activity runs inline. **Separately, on a host that can't honour the model keys** (`honorsModelKeys == false`, e.g. Codex, whose subagents run on its own OpenAI models) every `models.*` key collapses to the single session model even though the activities still fan out; when any `models.*` is set there the engine surfaces a **one-time** session-start notice that the split is not taking effect (the keys stay in `.trailhead/config.json` for when the project next runs on Claude Code). On Codex the per-activity split is available through a parallel `models.codex.*` namespace (OpenAI model ids), the honoured namespace there (projected into per-technique `~/.codex/agents/*.toml` and dispatched at runtime by `agent_type`), while `models.*` (Claude ids) still collapse to the single session model; the collapse notice points at `models.codex.*` and goes silent once it is set. A host without a hook bus (`hookBus == none`) also runs a lightweight inline update check at session start (no SessionStart hook, no statusline). See `configuration-reference.md`. The **keys, their values, the model/tdd/acceptance semantics, and the guided menu setup live in `configuration-reference.md`**: read it when running `/trailhead:config` or when you need a key's exact meaning. `/trailhead:config` with no args runs the guided setup; `config get` prints the merged config; `config set <key> <value>` writes one key.
|
|
@@ -13,7 +13,7 @@ Resolving a ticket **ends the session**: one ticket per session is a hard rule (
|
|
|
13
13
|
|
|
14
14
|
Name the next frontier ticket so the user knows what they're taking; put its **number** in the command (never the title). **When the frontier holds several mutually independent tickets, don't crown one: present them as a set to choose from**, naming each with its number (see **Frontier order carries no priority**). Bare `/trailhead:work` still acts on one, but the block keeps the choice visible. The block is the list of things to do **after the `/clear`** (it leads with it) and stays on screen in the terminal to read from, so each command must be either self-sufficient (a number already known) or safe to type by hand. The commands a handoff block may contain are `/clear`, `/trailhead:work` (bare or `<number>`), `/trailhead:map`, `/trailhead:dashboard`, and `/trailhead:quick`; **in a whiteboard session** (the resolved ticket was `trailhead:whiteboard`), also `/trailhead:whiteboard`. **Always offer the read-only views alongside the next action**: `/trailhead:map` for the low-res map and `/trailhead:dashboard` for the repo-wide index (all maps + the whiteboard), so the user can look before choosing. **`/trailhead:quick` is the one command that may carry a free-text argument here**: `/trailhead:quick "<short seed>"` when a concrete off-map piece is actually at hand (its argument is forgiving freeform seed text, not an exact title to match, so pre-filling it is helpful, not error-prone), or **bare `/trailhead:quick`** as the generic "if you want to work a piece off the map" offer, which prompts for the piece itself; keep any seed short and quote-safe. **`/trailhead:work` always takes the number, never the title** of an existing ticket, and **never hand the user a `/trailhead:ticket <type> "<title>"`** (or any other title-bearing command): adding a map ticket needs the diverge-first framing (Mode 3), so it is never pre-filled.
|
|
15
15
|
|
|
16
|
-
**If the frontier is empty**, that usually means fog that this resolution made specifiable should have been **graduated into a ticket already** (Mode 2 step 5), so the frontier isn't actually empty. If it genuinely is (nothing ticketable yet, or the user must choose which fog item to take next), point the block to **`/trailhead:map`** and offer to open the next ticket *yourself* once they say which one, rather than pre-filling a `/trailhead:ticket` command for them. **Fog still on the map takes priority over loose whiteboard work.** If the map still has fog (unspecified areas in *Not yet specified*), lead with **`/trailhead:map`** to dissolve it: that fog is the map's own remaining work, so graduating it comes before any off-map ticket. **Only when the map is genuinely complete** (frontier empty *and* no fog left) and the whiteboard still holds open loose tickets, do not sign off as if nothing remains: check for them and, if any exist, **surface them**, pointing the block at **`/trailhead:whiteboard`** (or **`/trailhead:dashboard`** for the full index) and naming the loose tickets so the user can pick one up with `/trailhead:quick <n>`. **In a whiteboard session** the fog-graduation reasoning does not apply (the whiteboard has no fog): point the block at **`/trailhead:whiteboard`** (and `/trailhead:quick <n>` for the next loose ticket), not the map. (The example is Italian; match whatever language you're conversing in.)
|
|
16
|
+
**If the frontier is empty**, that usually means fog that this resolution made specifiable should have been **graduated into a ticket already** (Mode 2 step 5), so the frontier isn't actually empty. If it genuinely is (nothing ticketable yet, or the user must choose which fog item to take next), point the block to **`/trailhead:map`** and offer to open the next ticket *yourself* once they say which one (named by its number in the numbered `/trailhead:map` fog list), rather than pre-filling a `/trailhead:ticket` command for them. **Fog still on the map takes priority over loose whiteboard work.** If the map still has fog (unspecified areas in *Not yet specified*), lead with **`/trailhead:map`** to dissolve it (its fog list numbers each entry, so a specific point can be named by number): that fog is the map's own remaining work, so graduating it comes before any off-map ticket. **Only when the map is genuinely complete** (frontier empty *and* no fog left) and the whiteboard still holds open loose tickets, do not sign off as if nothing remains: check for them and, if any exist, **surface them**, pointing the block at **`/trailhead:whiteboard`** (or **`/trailhead:dashboard`** for the full index) and naming the loose tickets so the user can pick one up with `/trailhead:quick <n>`. **In a whiteboard session** the fog-graduation reasoning does not apply (the whiteboard has no fog): point the block at **`/trailhead:whiteboard`** (and `/trailhead:quick <n>` for the next loose ticket), not the map. (The example is Italian; match whatever language you're conversing in.)
|
|
17
17
|
|
|
18
18
|
**Pending codebase offer (greenfield): surface it here, exactly once.** If this session's Work-the-map step 1 marked a greenfield codebase-issue offer **pending** (the repo has no `trailhead:codebase` issue but now holds substantial code), surface it **as a distinct, prominent line inside this block**, never as trailing prose after it. It is **an offer the user accepts with a yes, not a command they type** (there is no `/trailhead:codebase` command), so give it its own bullet phrased as a yes/no offer, along the lines of *"vuoi che generi l'issue codebase adesso? (una mappa del repo, fissata tra i pin)"* (in the user's conversation language). Show it **exactly once**: on a yes, generate and pin it via the **Codebase map** technique; on a no, don't nag again this session. This is the one non-command item the block may carry, and a whiteboard handoff never carries it (even if `work <n>` ran step 1 against a background map, the offer belongs to that map, not this loose ticket).
|
|
19
19
|
|
|
@@ -10,8 +10,9 @@ gh issue create --label "trailhead:map" --title "<destination>" --body-file <bod
|
|
|
10
10
|
gh issue create --label "trailhead:ticket,trailhead:build,trailhead:map-<map>" --title "<question/goal>" --body-file <body>
|
|
11
11
|
# link the ticket to its map: native sub-issue (structure/UI) + the map label above (the query)
|
|
12
12
|
gh api --method POST repos/{owner}/{repo}/issues/<map>/sub_issues -F sub_issue_id=$(gh api repos/{owner}/{repo}/issues/<ticket> --jq .id)
|
|
13
|
-
# wire a blocker: native dependency (visual frontier in the UI) + the label (the query)
|
|
13
|
+
# wire a blocker: native dependency (visual frontier in the UI) + the trailhead:blocked label (the query)
|
|
14
14
|
gh api --method POST repos/{owner}/{repo}/issues/<blocked>/dependencies/blocked_by -F issue_id=$(gh api repos/{owner}/{repo}/issues/<blocker> --jq .id)
|
|
15
|
+
# the label rides in the --label list at creation for a ticket already known-blocked (fires no issues.labeled event); use --add-label ONLY when a ticket becomes blocked LATER (a single event, never a bulk burst):
|
|
15
16
|
gh issue edit <blocked> --add-label "trailhead:blocked"
|
|
16
17
|
# the frontier: open, unassigned, not blocked, not unverified, one query (add --label trailhead:map-<map> to scope to one map when several are live; add -label:trailhead:whiteboard to a single-map repo-wide query to keep loose tickets off it)
|
|
17
18
|
gh issue list --label "trailhead:ticket" --state open --search "no:assignee -label:trailhead:blocked -label:trailhead:unverified -label:trailhead:whiteboard"
|
|
@@ -47,10 +48,11 @@ Wire all three in the same pass; a ticket carrying only one or two has drifted.
|
|
|
47
48
|
```bash
|
|
48
49
|
gh api --method POST repos/{owner}/{repo}/issues/<blocked>/dependencies/blocked_by -F issue_id=$(gh api repos/{owner}/{repo}/issues/<blocker> --jq .id)
|
|
49
50
|
```
|
|
50
|
-
3. The `trailhead:blocked` label:
|
|
51
|
+
3. The `trailhead:blocked` label. **Set it at creation** (include it in the `gh issue create --label` list) for any ticket known-blocked up front, which is every bulk path (the chart/adopt wiring second pass, split children, an on-the-fly ticket with a known blocker): the label then rides in the ticket's `issues.opened` event and fires **no** `issues.labeled` event. Reserve the post-creation `--add-label` form for a ticket that becomes blocked **later**, after it already exists (a single, non-bursty event):
|
|
51
52
|
```bash
|
|
52
53
|
gh issue edit <blocked> --add-label "trailhead:blocked"
|
|
53
54
|
```
|
|
55
|
+
**Why at creation.** The repo-side label guard (`.github/workflows/trailhead-label-guard.yml`) fires on `issues.labeled`. A setup pass that adds `trailhead:blocked` to many freshly-created tickets via `--add-label` fires a burst of `issues.labeled` events, one guard run each; GitHub sheds a fraction of that concurrent burst as `startup_failure`/`cancelled`, reddening the Actions history with no-op runs (the labeler is the authorized maintainer). Riding the label in `issues.opened` avoids the burst; moves 1-2 (the `## Blocked by` prose and the native dependency edge) still wire together in the second pass and fire no label event.
|
|
54
56
|
|
|
55
57
|
Same-blocker rule: all three must name the SAME blocker ticket, never a superseded parent, a split-origin, or a by-role placeholder ("child A"). Write the prose line and the native dependency from the same id, in the same pass, so they cannot point at different tickets.
|
|
56
58
|
|
|
@@ -11,7 +11,7 @@ The tracker is GitHub via the `gh` CLI (already authenticated). **Never post a s
|
|
|
11
11
|
| Ticket | a child issue with label `trailhead:ticket` + exactly one **type** label: `trailhead:decision` / `trailhead:research` / `trailhead:prototype` / `trailhead:build` / `trailhead:bug` / `trailhead:task`, plus its map's `trailhead:map-<n>` label and native sub-issue edge (see Child→map link). **A `trailhead:whiteboard` ticket carries none of the map wiring** (no `trailhead:map-<n>`, no sub-issue edge, no `Parent:`); see [The whiteboard](../trailhead-view/SKILL.md#render-the-whiteboard-whiteboard). |
|
|
12
12
|
| Child→map link | three views of one fact, wired in the same pass (like a blocker): a `Parent: <map name>(link)` line in the ticket body (human-readable); a **native sub-issue** edge to the map (`gh api --method POST repos/{owner}/{repo}/issues/<map>/sub_issues -F sub_issue_id=<ticket .id>`, the structure GitHub renders); and the map's **`trailhead:map-<n>` label** on the ticket (the queryable key that scopes its map's frontier). See [Multiple maps on one repo](multi-map.md#multiple-maps-on-one-repo-parking--concurrency). **A whiteboard ticket has none of the three** (it belongs to no map). |
|
|
13
13
|
| Path scope (monorepo) | an optional `Scope: <path>[, <path>…]` line in the ticket body naming the package/dir(s), or **submodule path(s)**, the ticket works in. It sharpens the [undeclared-coupling check](teamwork.md#working-as-a-team) (disjoint scopes parallelize safely), scopes build/test to the affected package, and scopes an `isolation: worktree` worktree's commits. **List every one it touches** (a ticket spanning two submodules names both). **Omit it in a single-package repo** (the whole tree is the scope); add it only where a repo has independent packages/subtrees or submodules. |
|
|
14
|
-
| Blocking | **Find every blocker first: from the ticket's own Question.** Each still-open ticket whose output this ticket *consumes as an input* is a blocker: a decision that needs another's answer, a build that needs a decision, a formula that needs the very thing another ticket defines. Read the Question and wire **all** of them: miss one and the ticket surfaces on the frontier prematurely and gets worked before its prerequisite. Then wiring a blocker is **three moves**: (1) list *which* tickets block in the `## Blocked by` body section (name+link); (2) create the **native GitHub dependency** so the frontier renders visually in GitHub's own UI: `gh api --method POST repos/{owner}/{repo}/issues/<blocked>/dependencies/blocked_by -F issue_id=<blocker's internal .id>`; (3)
|
|
14
|
+
| Blocking | **Find every blocker first: from the ticket's own Question.** Each still-open ticket whose output this ticket *consumes as an input* is a blocker: a decision that needs another's answer, a build that needs a decision, a formula that needs the very thing another ticket defines. Read the Question and wire **all** of them: miss one and the ticket surfaces on the frontier prematurely and gets worked before its prerequisite. Then wiring a blocker is **three moves**: (1) list *which* tickets block in the `## Blocked by` body section (name+link); (2) create the **native GitHub dependency** so the frontier renders visually in GitHub's own UI: `gh api --method POST repos/{owner}/{repo}/issues/<blocked>/dependencies/blocked_by -F issue_id=<blocker's internal .id>`; (3) apply the **`trailhead:blocked`** label, **at creation** (in the `gh issue create --label` list) for any ticket known-blocked up front, which is every bulk path (chart/adopt, split, an on-the-fly ticket with a known blocker): the label rides in `issues.opened` and fires **no** `issues.labeled` event, reserving the post-creation `--add-label` form for a ticket that becomes blocked *later* (a single event). This matters because the repo-side label guard fires on `issues.labeled`: a setup pass that `--add-label`s many fresh tickets spawns a burst of guard runs GitHub sheds as `startup_failure`/`cancelled` (all no-ops, the labeler is the maintainer), so riding the label in `issues.opened` keeps the Actions history clean. The **label is the frontier's source of truth**: native dependencies aren't cheaply queryable in one search, so the label is what keeps the frontier **one query**. On unblock, **remove the label the moment the last blocker closes** (that's what graduates the ticket onto the frontier); the native edge needs no cleanup: GitHub auto-reflects a closed blocker. **The three moves are one fact in three views: they must name the *same* blocker.** The `## Blocked by` line must link the **real blocker ticket** the native edge targets (its actual number), never a superseded parent, a split-origin, or a by-role placeholder like "child A"; wire the prose line and the native dependency in the same pass, from the same id, so they can't drift. |
|
|
15
15
|
| Claim | assign the ticket to yourself: `gh issue edit <n> --add-assignee @me`: the assignee *is* the claim; the claimer owns it end to end and closes it (no approver). Re-check before starting; on a collision, stop and ask the user. See [Working as a team](teamwork.md#working-as-a-team). |
|
|
16
16
|
| Resolution | comment with the answer → `gh issue close` → update `Decisions so far` on the map |
|
|
17
17
|
|
|
@@ -26,7 +26,7 @@ If genuinely ambiguous and the user's unreachable, default by context (backend m
|
|
|
26
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.
|
|
27
27
|
|
|
28
28
|
**Two DesignSync mechanics the publish must get right (DesignSync surface).** Specific to a DesignSync design system, alongside the shared index rule above:
|
|
29
|
-
- **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/
|
|
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`.
|
|
30
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.
|
|
31
31
|
|
|
32
32
|
Either way the rough screen is **reacted to before real UI code**. How approval gates the build depends on `config.design.approval`:
|
|
@@ -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.
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
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
|
-
**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.)
|
|
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
|
|
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`) |
|
|
@@ -37,7 +37,7 @@ Read **`references/charting.md`** and follow it. It holds both protocols: **Mode
|
|
|
37
37
|
|
|
38
38
|
## Open a ticket on the fly: `ticket <type> <title>`
|
|
39
39
|
|
|
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
|
|
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.)*
|
|
41
41
|
|
|
42
42
|
## Triage the inbox: `inbox [issue]`
|
|
43
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)
|
|
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
|
|
|
@@ -42,7 +42,7 @@ Render, by name:
|
|
|
42
42
|
- **In progress**: claimed tickets, with who holds each.
|
|
43
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.
|
|
44
44
|
- **Decisions so far**: the index of what's settled.
|
|
45
|
-
- **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.
|
|
46
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).
|
|
47
47
|
- **Inbox**: a count of untriaged inbound issues, as a nudge.
|
|
48
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).
|
|
@@ -55,6 +55,7 @@ It runs the **full engine** (the `build`/`bug` cycle in `references/ticket-engin
|
|
|
55
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.
|
|
56
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.
|
|
57
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.
|
|
58
59
|
|
|
59
60
|
Type follows the ticket: `quick "<text>"` defaults to `build` (`bug` if a defect); `quick <n>` uses the existing type and its matching engine.
|
|
60
61
|
|
|
@@ -23,10 +23,12 @@ 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` (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.
|
|
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.
|
|
28
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.
|
|
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).
|
|
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.
|
|
30
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).
|
|
@@ -40,10 +42,12 @@ 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
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`).
|
|
45
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.
|
|
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.
|
|
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.
|
|
47
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**).
|