@marcomigozzi/trailhead 0.8.1 → 0.10.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 +20 -17
- package/README.md +20 -17
- package/bin/lib/codex-projection.js +14 -6
- package/bin/lib/codex-projection.test.js +46 -2
- package/bin/trailhead.js +21 -10
- package/bin/trailhead.test.js +326 -0
- package/package.json +1 -1
- package/plugins/trailhead/.claude-plugin/plugin.json +1 -1
- package/plugins/trailhead/commands/prune.md +6 -0
- package/plugins/trailhead/commands/quick.md +1 -1
- package/plugins/trailhead/hooks/hooks.json +4 -0
- package/plugins/trailhead/hooks/lib/gh-subcommand.js +102 -0
- package/plugins/trailhead/hooks/lib/gh-subcommand.test.js +99 -0
- package/plugins/trailhead/hooks/trailhead-body-guard.js +221 -0
- package/plugins/trailhead/hooks/trailhead-body-guard.test.js +222 -0
- package/plugins/trailhead/hooks/trailhead-issue-injection-scanner.js +61 -35
- package/plugins/trailhead/hooks/trailhead-issue-injection-scanner.test.js +76 -0
- package/plugins/trailhead/hooks/trailhead-secret-guard.js +4 -15
- package/plugins/trailhead/hooks/trailhead-secret-guard.test.js +31 -0
- package/plugins/trailhead/skills/_shared/choices.md +5 -1
- package/plugins/trailhead/skills/_shared/configuration-reference.md +23 -16
- package/plugins/trailhead/skills/_shared/configuration.md +1 -1
- package/plugins/trailhead/skills/_shared/load-first.md +2 -0
- package/plugins/trailhead/skills/_shared/multi-map.md +1 -1
- package/plugins/trailhead/skills/_shared/principles.md +1 -1
- package/plugins/trailhead/skills/_shared/session-handoff.md +9 -7
- package/plugins/trailhead/skills/_shared/substrate-commands.md +41 -5
- package/plugins/trailhead/skills/_shared/substrate.md +10 -6
- package/plugins/trailhead/skills/_shared/teamwork.md +19 -12
- package/plugins/trailhead/skills/_shared/techniques/acceptance-testing.md +13 -4
- package/plugins/trailhead/skills/_shared/techniques/grilling.md +1 -1
- package/plugins/trailhead/skills/_shared/techniques/prototype.md +1 -1
- package/plugins/trailhead/skills/_shared/techniques/verify.md +1 -1
- package/plugins/trailhead/skills/_shared/techniques.md +1 -1
- package/plugins/trailhead/skills/trailhead/SKILL.md +3 -3
- package/plugins/trailhead/skills/trailhead-capture/references/capture.md +1 -1
- package/plugins/trailhead/skills/trailhead-chart/SKILL.md +3 -1
- package/plugins/trailhead/skills/trailhead-chart/references/charting.md +12 -2
- package/plugins/trailhead/skills/trailhead-chart/references/inbox.md +2 -2
- package/plugins/trailhead/skills/trailhead-manage/SKILL.md +5 -4
- package/plugins/trailhead/skills/trailhead-manage/references/pruning.md +48 -0
- package/plugins/trailhead/skills/trailhead-view/SKILL.md +4 -2
- package/plugins/trailhead/skills/trailhead-work/SKILL.md +18 -16
- package/plugins/trailhead/skills/trailhead-work/references/auto.md +4 -2
- package/plugins/trailhead/skills/trailhead-work/references/ticket-engines.md +12 -9
|
@@ -69,7 +69,7 @@ So `Decisions so far` / `Not yet specified` / `Out of scope` refer to these bloc
|
|
|
69
69
|
A repo's trailhead surface is spread across issues: one or more maps, a whiteboard, an inbox. The **dashboard** is the single pinned entry point that indexes all of it: a permanent, repo-scoped issue labelled **`trailhead:dashboard`**, created once at the first chart/adopt and occupying the **fixed 3rd pinned slot** alongside `trailhead:codebase` and `trailhead:conventions` (see `../_shared/substrate.md`). Maps themselves are **never pinned**: a pin is server-side and global to the repo, but the active map is client-side per-checkout state (`.trailhead/active-map`, gitignored), so no single map is "the" map to pin. The dashboard gives one stable pinned index that scales to any number of maps and to the whiteboard.
|
|
70
70
|
|
|
71
71
|
It holds:
|
|
72
|
-
- a link to **every open `trailhead:map`** (found via the `trailhead:map` label); GitHub renders each map's sub-issue **progress bar natively**, so the dashboard only links, it never recomputes per-ticket progress. **Per open map it also runs one cheap `0 open scoped issues?` pre-filter** and, for a candidate confirmed to have no remaining fog (see the exhaustion check in `../_shared/substrate-commands.md`), flags an **exhausted-but-open** map (destination reached, map issue never closed) as `exhausted · closeable`, so the render can offer to close it (below);
|
|
72
|
+
- a link to **every open `trailhead:map`** (found via the `trailhead:map` label); GitHub renders each map's sub-issue **progress bar natively**, so the dashboard only links, it never recomputes per-ticket progress. **Per open map it also runs one cheap `0 open scoped issues?` pre-filter** and, for a candidate confirmed to have no remaining fog (see the exhaustion check in `../_shared/substrate-commands.md`), flags an **exhausted-but-open** map (destination reached, map issue never closed) as `exhausted · closeable`, so the render can offer to close it (below). It also flags each open map **`near cap · <total>/100`** (total >= 90) or **`at cap`** (total >= 100) from `sub_issues_summary.total`, appending **`· prunable`** when the map has closed edges to reclaim, so the render can offer `/trailhead:prune` (below);
|
|
73
73
|
- the **whiteboard** as its own section (frontier / in-progress / blocked, or a link to the `whiteboard` view);
|
|
74
74
|
- dynamic **counts**: untriaged inbox size, whiteboard frontier size.
|
|
75
75
|
|
|
@@ -77,6 +77,8 @@ It holds:
|
|
|
77
77
|
|
|
78
78
|
**Exhausted-but-open maps: flag and offer to close.** The one-shot "close this map?" prompt fires only at the exhaustion moment (a `work` handoff, or a `map` render on the exhausted map). If that moment was skipped, or the user chose to leave the map open, an exhausted map lingers with nothing re-surfacing it. So the dashboard is the recurring surface that catches it: for every open map flagged `exhausted · closeable` (the `0 open scoped issues?` pre-filter confirmed against remaining fog, see `../_shared/substrate-commands.md`), the render **offers to close it** (route any un-parked deferred *Out of scope* line first, exactly as the `map` exhaustion check does), `gh issue close` on a yes, leave it open on a no. Never auto-close: flag + offer only. The count runs per map, but the body read (fog confirm + deferred routing) is bounded to the exhausted candidates, so this stays within the dashboard-only-links spirit.
|
|
79
79
|
|
|
80
|
+
**Near/at-cap maps: flag and offer to prune.** The same advisory posture applies to GitHub's 100 native sub-issue cap: a map flagged `near cap · <total>/100` or `at cap · prunable` gets the render's offer to run `/trailhead:prune`, never an auto-prune (see the cap-aware edge wiring and the `gh` mechanics in `../_shared/substrate-commands.md`).
|
|
81
|
+
|
|
80
82
|
**Freshness: structural events + on demand.** The pinned issue is rewritten when the surface changes **structurally** and **on demand** via `dashboard`. The structural events are: a map is **charted** or **exhausted** (a map appears or disappears), and a **whiteboard ticket is born or resolved**. It is **not** rewritten on every *map* ticket resolve: that would churn a pinned issue (and its notifications) constantly, and each map's native sub-issue progress bar already tracks its per-ticket progress. **The whiteboard has no such native progress bar** (its tickets are sub-issues of nothing), so the dashboard's whiteboard section and count are the only place whiteboard state shows: a whiteboard ticket appearing or being resolved *is* a structural change to the indexed surface, and refreshes the dashboard. **One timing exception for `quick`:** a ticket born by `quick "<text>"` is born *and* resolved in the same session, so refreshing at its birth would only be duplicated (and superseded) by the resolution refresh a moment later. So a `quick`-born ticket refreshes at the session **handoff** (Resolve), or at **`/trailhead:pause`** if the session pauses before Resolve, **not** at the birth instant; a ticket born by a **terminal capture** (a `bug`/`todo`/`idea` routed to the whiteboard, whose session ends at creation) still refreshes at birth, since that is its only refresh point. Links + counts stay good enough refreshed at these events and on demand.
|
|
81
83
|
|
|
82
84
|
**Self-heal (read-only entry points).** A repo that adopted trailhead before the dashboard existed, or one whose pin was later dropped, has no pinned index until the next chart/adopt or an explicit `dashboard`. To close that gap, the frequent **read-only entry points self-heal the pin**: the bare `/trailhead` smart entry, the `map` render, and the `whiteboard` render each ensure the dashboard exists and is pinned before rendering, idempotently and cheaply, mirroring the "ensure the label exists before applying it" pattern: ensure the `trailhead:dashboard` label, then
|
|
@@ -90,7 +92,7 @@ It holds:
|
|
|
90
92
|
|
|
91
93
|
## Render the whiteboard: `whiteboard`
|
|
92
94
|
|
|
93
|
-
Not all work belongs to a map. A **bug**, a **todo**, or a one-off **task** may be about something else entirely, or simply not worth charting a map for. The **whiteboard** is where that loose work lives: tickets labelled **`trailhead:whiteboard`**, map-less (no `trailhead:map-<n>` label, no `Parent:` line, no native sub-issue edge). A ticket is on a map **or** on the whiteboard, never both. Loose tickets are created there by a capture routed to the whiteboard or born there by `quick`, and worked with `
|
|
95
|
+
Not all work belongs to a map. A **bug**, a **todo**, or a one-off **task** may be about something else entirely, or simply not worth charting a map for. The **whiteboard** is where that loose work lives: tickets labelled **`trailhead:whiteboard`**, map-less (no `trailhead:map-<n>` label, no `Parent:` line, no native sub-issue edge). A ticket is on a map **or** on the whiteboard, never both. Loose tickets are created there by a capture routed to the whiteboard or born there by `quick`, and worked with `work <n>` (the anchor: works the ticket whole and can split) or `quick <n>` (the no-split variant); the capture and work clusters own those paths. This engine only **renders** the whiteboard.
|
|
94
96
|
|
|
95
97
|
It has its **own frontier**, off every map's (see **Map frontier vs whiteboard frontier** in `../_shared/substrate.md`):
|
|
96
98
|
```bash
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: trailhead-work
|
|
3
3
|
user-invocable: false
|
|
4
|
-
description: "trailhead work cluster: the verbs that resolve tickets on the map and manage a ticket's lifecycle. Work the next frontier ticket or the one you name (work), work one ticket whole
|
|
4
|
+
description: "trailhead work cluster: the verbs that resolve tickets on the map and manage a ticket's lifecycle. Work the next frontier ticket or the one you name (work), work one ticket whole without splitting, a new whiteboard ticket or an existing one (quick), checkpoint the ticket in play (pause), resume it from its checkpoint (resume), split an oversized ticket into children (split), and run the whole frontier autonomously until a stop condition or exhaustion (auto). A cohesion cluster of the trailhead skill split, loading the shared `_shared/` core; reached through the `/trailhead:<verb>` command wrappers. Not auto-invoked: it runs only when one of these verbs is dispatched."
|
|
5
5
|
argument-hint: "[work|quick|pause|resume|split|auto] [ticket | \"text\" | note]"
|
|
6
6
|
---
|
|
7
7
|
|
|
8
|
-
`trailhead-work` is the **work cluster** of the trailhead skill: the verbs that resolve tickets and manage a ticket's lifecycle. `work` takes the next frontier ticket (or the one you name) and resolves it with its type's engine; `quick` works one ticket whole,
|
|
8
|
+
`trailhead-work` is the **work cluster** of the trailhead skill: the verbs that resolve tickets and manage a ticket's lifecycle. `work` takes the next frontier ticket (or the one you name) and resolves it with its type's engine; `quick` works one ticket whole without splitting (a new whiteboard ticket from text, or an existing ticket, which stays where it is); `pause` checkpoints the ticket in play; `resume` picks it back up; `split` breaks an oversized ticket into children; `auto` runs the map's frontier autonomously, ticket by ticket, suspending the one-ticket-per-session rule until a stop condition or exhaustion. Everything lives on the GitHub Issues; the repo holds code only.
|
|
9
9
|
|
|
10
10
|
## Load first, in order
|
|
11
11
|
|
|
@@ -18,7 +18,7 @@ Config is a property of the project/map, not the working directory: an isolation
|
|
|
18
18
|
The **first word** of the arguments is the verb; the rest is the text (a ticket number, a `"text"` seed, or a pause note).
|
|
19
19
|
|
|
20
20
|
- **`work [ticket]`** to the **Work the map** engine below (Mode 2). It dispatches the chosen ticket to its type's engine in **`references/ticket-engines.md`** (`decision` / `research` / `prototype` / `build` / `bug` / `task`). Read that file when you resolve the ticket.
|
|
21
|
-
- **`quick [ticket | "text"]`** to the **Work one ticket whole** engine below. It runs the full `build`/`bug` cycle from `references/ticket-engines.md`, off the map
|
|
21
|
+
- **`quick [ticket | "text"]`** to the **Work one ticket whole** engine below. It runs the full `build`/`bug` cycle from `references/ticket-engines.md`, without splitting (off the map only for a new `"text"` ticket; a map ticket stays on its map).
|
|
22
22
|
- **`pause [note]`** / **`resume [ticket]`** / **`split [ticket]`** to the teamwork lifecycle: `../_shared/teamwork.md` (multi-cluster, so it lives in `_shared/`). Read it and follow it.
|
|
23
23
|
- **`auto [map]`** to the **autonomous run** engine in **`references/auto.md`**: it runs the map's frontier ticket-by-ticket, suspending one-ticket-per-session, taking every advisory choice as delegate without the confirm gate and stopping only at the safety rail, fog, human-necessary decisions, or human interrupt. Read that file when you engage a run.
|
|
24
24
|
|
|
@@ -37,28 +37,30 @@ This guard is the **explicit-number paths only**. Bare `work` selects from the l
|
|
|
37
37
|
|
|
38
38
|
The user invokes with a map (URL or number). A ticket is optional: without one, you pick the next decision.
|
|
39
39
|
|
|
40
|
-
1. Load the **map** (the low-res view, not every ticket body). **Pick which map** if the repo has several open: use the one the user named, else the active map (`.trailhead/active-map`); if neither and more than one is open, list them and ask. Set/refresh the active-map marker to the one you're working. See **Multiple maps on one repo** in `../_shared/multi-map.md`. **Codebase issue check (greenfield, offer once):** if the repo has **no `trailhead:codebase` issue** (a greenfield map never got one) and now holds **substantial code**, **don't interrupt the work to offer now**: mark the offer **pending** and surface it **at the handoff**,
|
|
40
|
+
1. Load the **map** (the low-res view, not every ticket body). **Pick which map** if the repo has several open: use the one the user named, else the active map (`.trailhead/active-map`); if neither and more than one is open, list them and ask. Set/refresh the active-map marker to the one you're working. See **Multiple maps on one repo** in `../_shared/multi-map.md`. **Codebase issue check (greenfield, offer once):** if the repo has **no `trailhead:codebase` issue** (a greenfield map never got one) and now holds **substantial code**, **don't interrupt the work to offer now**: mark the offer **pending** and surface it **at the handoff**, just before the `/clear`-first next-step block (not as a line inside it: see the **pending codebase offer** rule in `../_shared/session-handoff.md`), exactly once. On the user's yes, generate it via the **Codebase map** technique, then pin it (one of the three permanent repo pins: dashboard, codebase, conventions) and link it from the map's Notes; if the user declines, don't nag again this session. A repo that already has the issue, or is still too thin to map, skips this.
|
|
41
41
|
2. Choose the ticket. **If the user named a ticket, the Preflight above already confirmed it is open** (a closed, superseded, out-of-scope, or nonexistent number stopped there, before any setup ran). If the user names one, use it (a **named** ticket may be a `trailhead:whiteboard` one: `work <n>` works it whole, off the map, with the normal cycle and split allowed; use `quick <n>` for the no-split variant). Otherwise take a frontier ticket to act on, **scoped to this map** (add its `trailhead:map-<n>` label to the frontier query when several maps are live) and **never a whiteboard ticket** (bare `work` stays on the map; the whiteboard frontier is reached via `/trailhead:whiteboard` and `quick`). Bare `work` may act on the first ticket the query returns, but that order is `gh`'s newest-first, not a priority: present several independent tickets as a **set to choose from**, not the graph's natural next step (see **Frontier order carries no priority** in `../_shared/substrate.md`). **Claim** it: assign it to yourself before any work, then re-read the assignee before starting; on a collision, stop and ask the user (see `../_shared/teamwork.md`). If the ticket proves too big once you're in it, propose a **split** rather than grind, but never split on your own: present the children you would create and wait for the user's go-ahead before creating anything (same file).
|
|
42
|
-
- **First,
|
|
43
|
-
- **
|
|
42
|
+
- **First, resolve the effective isolation mode.** The `isolation:` header defaults to **`auto`** (an absent key means `auto`; the engine never writes the key). Under **`auto`**, at work-start **before any file edit or diagnostic probe**, first run the read-only **assigned-ticket pre-check** (full rule in `../_shared/teamwork.md`): other open tickets already assigned to you **pre-bias** the ask toward `worktree`/`clone` (still asking, easy neutral), and the ask is **skipped** only when the session is already inside this ticket's own linked worktree (`trailhead/t<N>`, `N` the current ticket). Then ask the user once whether they will work more than one ticket at once on this machine this session. **No** (a lone ticket): the mode resolves to the current checkout, exactly like `none` (the scope-free check below applies). **Yes**: resolve by repo shape, a plain repo -> `worktree`, a **superrepo** (a `.gitmodules` at the checkout root declaring at least one submodule) -> `clone`; that "yes" **is** the clone gate's answer, so the resolved `clone` path proceeds without re-asking. Because trailhead resolves one ticket per session, this asks at most once per ticket work-start (so once per session in normal work; a within-session ticket switch re-asks at the new ticket's work-start), and an autonomous run (`/trailhead:auto`) never asks and never runs the pre-check (sequential work, no concurrency) and resolves to the current checkout. If the header carries an **explicit** `none`/`worktree`/`clone`, honour it as-is (never subject it to auto's ask): explicit `clone` keeps its own gate, explicit `worktree` is eager. See `../_shared/teamwork.md`.
|
|
43
|
+
- **When the resolved mode is the current checkout (`isolation: none`, or `auto` answered no), check the scope is free before starting** (this is how a submodule/package gets serialised when worktrees don't fit): scan the other in-progress tickets' `Scope:` lines, and if one **overlaps** this ticket's scope, **stop and tell the user** the scope is busy (name the ticket + holder) and offer a disjoint-scope frontier ticket or to wait. See `../_shared/teamwork.md`.
|
|
44
44
|
- **Write the session-ticket marker** (a cheap hint for tooling like a statusline): at the **working root** (the checkout, worktree, or clone you'll edit in), write `.trailhead/session-ticket` as **one line** `#<n> <ticket title>`. **Keep it gitignored** (add `.trailhead/session-ticket` to that repo's `.gitignore` if absent): it's per-session local state, **never committed**. It just lets an external tool show "what am I on" offline; the tracker stays the source of truth. Remove it at Resolve/handoff (see `../_shared/session-handoff.md`); on **Pause** it may stay (you'll return) or be cleared if you release the claim. The **`.trailhead/active-map`** marker (which map this session works) follows the same rules: gitignored, per-session, never committed; see **Multiple maps on one repo** in `../_shared/multi-map.md`.
|
|
45
|
-
- **
|
|
46
|
-
3.
|
|
47
|
-
4.
|
|
48
|
-
5.
|
|
49
|
-
6.
|
|
45
|
+
- **When the resolved mode is `worktree` or `clone` (explicit, or `auto` answered yes), set up the isolated workspace NOW, before any file edit or diagnostic probe** (not later at Execute/Fix): **if the repo has submodules (a `.gitmodules`), `isolation: worktree` is escalated to `clone`** (a superproject worktree cannot safely carry submodule state; see `../_shared/teamwork.md`). For `worktree` (no submodules), create/enter this ticket's `git worktree` on a `trailhead/t<n>` branch for the package/path the ticket's `Scope:` names (a submodule-bearing repo would have escalated to `clone` above), e.g. `git worktree add ../<repo>-t<n> -b trailhead/t<n>`; for `clone`, make an independent working copy at `../<repo>-t<n>` (branch `trailhead/t<n>`) by whichever is faster: `git clone` (add `--recurse-submodules` when the repo has submodules) + the install step, or a **folder copy that brings `node_modules`** (and the submodule working trees) to skip the reinstall (then verify the copy's git is independent, `git -C <copy> rev-parse --absolute-git-dir` resolves inside it, and the same inside every submodule, else commits leak back to the original); a clone with submodules is provisioned via `git submodule update --init --recursive` (nested included), each scoped submodule pinned at the superproject's recorded commit. **If this `clone` was reached via `auto`'s work-start ask (answered yes), that ask already served as the clone gate: do not re-ask, proceed to set up the clone. Under an explicit `isolation: clone`, run the gate as follows.** **Because `clone` is heavy (a full clone + an install), gate it, every time, before cloning: ask the user whether they expect to work more than one ticket at once on this machine this session.** If **no**, **skip the clone** and work this ticket in the current checkout (isolation only exists to stop concurrent workspaces from colliding; a lone ticket has nothing to collide with, so the shared-checkout caveats apply and that is fine, and `git:` still decides the branch as usual); if **yes**, clone as above. The gate is **`clone`-only**: `worktree` is cheap, so set it up without asking. Do every subsequent step in whatever workspace you ended up in. This is the whole point of isolation: if you diagnose and edit in the shared checkout "just to start" while a concurrent workspace exists, you are already colliding. Set the workspace up silently (the `clone` gate-question is the one exception you voice); it's undone at Resolve when the branch integrates and the worktree/clone is removed. See `../_shared/teamwork.md`.
|
|
46
|
+
3. **Brief the ticket and confirm scope, before the engine runs.** With the ticket claimed, read it and give the user a **brief, result-oriented summary of what resolving it will do** (its goal and the shape of the work, not a plan yet), then **ask whether they want to change anything**. This is a HITL confirm gate: on assent, proceed to the engine. **If the user asks for a change, hold it against the ticket.** A change **consistent** with the ticket is folded in and the engine proceeds. A change that **diverges** from the ticket is surfaced as a mismatch, and the user is **asked, never forced**, whether to (a) work the ticket **as-is**, or (b) open a **separate new ticket** for the divergent request. Offer both and decide neither: open the separate ticket **only if the user chooses it** (via **Scope that surfaces while working a ticket**, served by the capture cluster, the same capture-or-split path used for scope that surfaces mid-work), never automatically. **If the user defers the choice rather than settling it here, mark the divergent follow-up pending for the Pending follow-up guard (`../_shared/session-handoff.md`), so it is re-posed at the handoff rather than lost.** **Under an autonomous run (`auto`) this is a process confirm gate**, so it follows the advisory boundary in `references/auto.md`: `auto` proceeds (takes the delegate option) without stopping to brief.
|
|
47
|
+
4. Resolve it with its type's engine in **`references/ticket-engines.md`**, **zoom as needed**: fetch the full body of related/closed tickets on demand. If in doubt on a `decision` ticket, run **Grilling** + **Domain vocabulary**. If in doubt on a `build`, **stop and ask** (see the Discuss step): never auto-grill.
|
|
48
|
+
5. Record the resolution: a comment with the answer, `gh issue close`, add the pointer to *Decisions so far*. Then **unblock dependents**: for every ticket this one was blocking, if it was the last open blocker, remove its `trailhead:blocked` label so it graduates onto the frontier.
|
|
49
|
+
6. Add newly-surfaced tickets (create-then-wire, labelling blocked ones `trailhead:blocked`); graduate the fog that became specifiable, clearing the patch from *Not yet specified*. If the answer reveals a ticket sits beyond the destination, apply the **out-of-scope vs deferred** test (`../_shared/out-of-scope.md`): if it's really deferred on something outside the map, ask the user to route it to a `seed`/`idea`/`todo`; only if it's truly beyond the destination rule it **out of scope** (label, then **drop its native sub-issue edge** to reclaim a slot, then close: the eager drop in `../_shared/substrate-commands.md`, 404-tolerant, references intact) instead of resolving it. If the decision invalidates other parts of the map, update or delete them.
|
|
50
|
+
7. **Hand off.** Close the session with the **Session handoff** ritual (`../_shared/session-handoff.md`): confirm the ticket is resolved by name, then the scannable **next-step block with `/clear` first** (never the next command without it), naming the next frontier ticket and giving its number (or, when several mutually independent tickets sit on the frontier, presenting them as a **set to choose from** rather than crowning the first-listed), or `/trailhead:map` if the frontier is empty/ambiguous. This holds in any later follow-up too, not only at the moment of resolution. **If this resolution leaves the map exhausted** (no open tickets and no fog left: the destination is reached), say so; but **before closing it out, if *Out of scope* holds any deferred line** (apply the out-of-scope-vs-deferred tells in `../_shared/out-of-scope.md`), **stop and ask the user what to do with each** (a `seed` gated on its trigger, an `idea`, or a `todo`, in this or another map), so nothing wanted-later is lost when the map goes quiet. Only once the deferred items are routed (or the user says leave them), **ask the user whether to close the map issue** now that the destination is reached: `gh issue close` it on a yes (it stays the record), leave it open on a no; never close a map unprompted. Either way **refresh the dashboard** so an exhausted/closed map drops off (a structural event). Maps aren't pinned, so there's no pin to free; the dashboard, codebase, and conventions stay pinned regardless.
|
|
50
51
|
|
|
51
|
-
## Work one ticket whole,
|
|
52
|
+
## Work one ticket whole, without splitting: `quick`
|
|
52
53
|
|
|
53
54
|
A streamlined way to just get one ticket done, without map ceremony. `quick "<text>"` opens a **whiteboard** ticket (a `build`, or a `bug` when the text is clearly a defect) and works it end to end now; `quick <n>` works an **existing** ticket (whiteboard or map) the same way. For `quick <n>`, the **Preflight** above runs first: a closed, out-of-scope, superseded, or nonexistent ticket stops immediately, before config, claim, or isolation setup. Claim it first; still one ticket per session. **Bare `/trailhead:quick`** (no argument, e.g. offered in a Session handoff) asks for the piece to work, like a capture missing its text, then proceeds as `quick "<text>"`.
|
|
54
55
|
|
|
55
|
-
It runs the **full engine** (the `build`/`bug` cycle in `references/ticket-engines.md`: Discuss → Plan → Execute → Verify → Resolve), differing from `work` on
|
|
56
|
+
It runs the **full engine** (the `build`/`bug` cycle in `references/ticket-engines.md`: Discuss → Plan → Execute → Verify → Resolve), differing from `work` on these points:
|
|
57
|
+
- **Briefs and confirms scope first, exactly as Work the map step 3.** With the ticket claimed, summarise what resolving it will do and ask whether to change anything, applying the same consistency rule (a divergent request is offered as work-as-is vs a separate new ticket, opened only if the user chooses it, never forced; via **Scope that surfaces while working a ticket**), and, exactly as Work the map step 3, if the user defers that choice rather than settling it here, mark the divergent follow-up pending for the Pending follow-up guard (`../_shared/session-handoff.md`) so it is re-posed at the handoff. A just-written `quick "<text>"` needs only a one-line confirmation; `quick <n>` on an existing ticket gets the full brief.
|
|
56
58
|
- **Grill only if needed** (Discuss): never auto-grill; start from Plan and stop to ask only if a blocking ambiguity surfaces, exactly as the `build` Discuss. The user may ask to grill at any time.
|
|
57
59
|
- **Never splits.** If the ticket turns out large, work it whole anyway: that is the point of `quick`. Do not `split` it and do not spin children. (New scope that surfaces mid-work is still captured out, never folded in, see **Scope that surfaces while working a ticket**, served by the capture cluster.)
|
|
58
|
-
- **No map narrative book-keeping.** A whiteboard ticket has no map, so there is no `Decisions so far` update, no fog to graduate, no frontier re-scan. Everything else holds: atomic commits with `Refs: #<n>`, TDD / Code review / Acceptance testing per the cycle, the resolution comment + `gh issue close`, and the **Session handoff** (`../_shared/session-handoff.md`). **Exception, `quick <n>` on a *map* ticket: dependent-unblocking still runs** (remove `trailhead:blocked` from any dependent whose last blocker just closed, **Work the map** step
|
|
59
|
-
- **Same isolation setup as `work`.** `quick` **writes the `.trailhead/session-ticket` marker at the working root in every isolation mode** (exactly as **Work the map** step 2, so tooling like the statusline shows what's in play; no active-map marker, though: a whiteboard ticket has no map). Under `isolation: worktree`/`clone` it
|
|
60
|
+
- **No map narrative book-keeping.** A whiteboard ticket has no map, so there is no `Decisions so far` update, no fog to graduate, no frontier re-scan. Everything else holds: atomic commits with `Refs: #<n>`, TDD / Code review / Acceptance testing per the cycle, the resolution comment + `gh issue close`, and the **Session handoff** (`../_shared/session-handoff.md`). **Exception, `quick <n>` on a *map* ticket: dependent-unblocking still runs** (remove `trailhead:blocked` from any dependent whose last blocker just closed, **Work the map** step 5 above); skipping it would strand those dependents off the frontier for good. Only the *narrative* book-keeping above and the split are skipped, so prefer `work <n>` for a map ticket that has dependents or belongs in the map record.
|
|
61
|
+
- **Same isolation setup as `work`.** `quick` **writes the `.trailhead/session-ticket` marker at the working root in every isolation mode** (exactly as **Work the map** step 2, so tooling like the statusline shows what's in play; no active-map marker, though: a whiteboard ticket has no map). Under `auto` it runs the same work-start concurrency ask **before any edit** and resolves to current-checkout / worktree / clone by the same rule; under an explicit `isolation: worktree`/`clone` it sets up the ticket's `trailhead/t<n>` workspace before any edit (including the submodule-forces-`clone` escalation and submodule provisioning) and re-checks the claim; under `isolation: none` (or `auto` answered no), the scope-free check applies too.
|
|
60
62
|
- **Refreshes the dashboard at the handoff, not mid-work.** A `quick "<text>"` whiteboard ticket is born and resolved in the **same** session, so its dashboard refresh rides with the session's end, not with ticket creation: **do not refresh right after the ticket exists** (that lands a structural pinned-issue write ahead of the work `quick` exists to just-get-done, and only duplicates the resolution refresh a moment later). Refresh at **Resolve** (the handoff), as a **full regeneration from the live tracker** (never a partial patch that leaves other sections knowingly stale), so the whiteboard ticket lands on the dashboard as the session closes. **If the session pauses before Resolve** (`/trailhead:pause`), refresh there instead, so a deliberately-unfinished quick still shows (the read-only renders never rewrite the dashboard body, so nothing else would surface it). `quick <n>` on a *map* ticket does **not** refresh (map ticket progress shows via the native bar). See **The dashboard** and its freshness rule in `../_shared/substrate.md`.
|
|
61
|
-
- **
|
|
63
|
+
- **Always size-triages, independent of `config.effort`.** `quick` is the home of the cost-aware path: reaching for `quick` is itself the signal the piece is small, so 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`) **every time, at the start before planning, whatever `config.effort` is set to**. `quick` does not wait for `effort: lean`, and an explicit `effort: standard` does not suppress it: `effort` governs `work` / the map cycle, not `quick`. 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**; on a large ticket there is no offer and the full cycle runs. The correctness spine (atomic `Refs:` commits, tests/criterion, post-commit self-check, Resolve) is never in the offer, whatever the size or the answer.
|
|
62
64
|
|
|
63
65
|
Type follows the ticket: `quick "<text>"` defaults to `build` (`bug` if a defect); `quick <n>` uses the existing type and its matching engine.
|
|
64
66
|
|
|
@@ -19,13 +19,15 @@ Independent of and **above** the advisory boundary below, `auto` **never perform
|
|
|
19
19
|
|
|
20
20
|
When the next step of a ticket would trip the rail, auto **sets that ticket aside and continues** (it does not perform the action and does not block the whole run): it hands the outward action back to the human, recorded in the run-end summary. **Atomic local commits to `main` are not rail-tripping**: they are the normal Execute and continue automatically. The rail is **hardcoded**, not a disableable toggle.
|
|
21
21
|
|
|
22
|
-
**Under a `git: pr` convention the rail bites at integration (by design).** When the repo's `trailhead:conventions` sets `git: pr` (or a ticket works in an `isolation: worktree`/`clone` branch), finishing a `build`/`bug` ticket ends in a **branch push + opening a PR**, both rail actions. So on such a repo auto drives each ticket through its **whole local cycle** (discuss / plan / execute / verify, atomic commits on the ticket's branch) and then **sets the push / PR / integration step aside for the human**, listing every ready-to-integrate branch in the run-end summary. The dependents of a ticket whose PR is not yet merged stay blocked until the human integrates, so a run on a `git: pr` repo drains less of the frontier per invocation than one on `git: main` (where a resolved ticket closes and unblocks immediately). This is the rail working as intended, not a failure; auto is simply most productive under `git: main`.
|
|
22
|
+
**Under a `git: pr` convention the rail bites at integration (by design).** When the repo's `trailhead:conventions` sets `git: pr` (or a ticket works in an `isolation: worktree`/`clone` branch), finishing a `build`/`bug` ticket ends in a **branch push + opening a PR**, both rail actions. So on such a repo auto drives each ticket through its **whole local cycle** (discuss / plan / execute / verify, atomic commits on the ticket's branch) and then **sets the push / PR / integration step aside for the human**, listing every ready-to-integrate branch in the run-end summary. The dependents of a ticket whose PR is not yet merged stay blocked until the human integrates, so a run on a `git: pr` repo drains less of the frontier per invocation than one on `git: main` (where a resolved ticket closes and unblocks immediately). This is the rail working as intended, not a failure; auto is simply most productive under `git: main`. **Isolation under a run:** `isolation: auto` (the default) resolves to the **current checkout** during an autonomous run without asking (the run is sequential, one ticket at a time, so there is no concurrency and no process question to put to the human); an explicit `worktree`/`clone` still applies as the conventions set it, and its branch push/PR is a rail action set aside for the human exactly as above.
|
|
23
23
|
|
|
24
24
|
## The advisory boundary (the `choices.md` one-line test, no confirm gate)
|
|
25
25
|
Wherever the engine would put a **forced choice** to the human, apply the one-line test in `../../_shared/choices.md`:
|
|
26
26
|
- **Advisory / process choice** (the test's Positive side: the delegate option is offered): `auto` **takes the delegate option WITHOUT its confirm gate**. It picks the best option with the same honest rationale delegate would state, and proceeds. This is the whole of auto's autonomy: "take delegate, skip the confirm." No new confidence threshold, no separate allowlist, so the boundary can never drift from the tested convention.
|
|
27
27
|
- **Human-necessary decision** (the test's Negative side: delegate is withheld): a `decision`-ticket convergence, a destination or scope change, an out-of-scope ruling, routing a deferred line to seed/idea/todo, closing an exhausted map, a claim collision. `auto` **sets that ticket aside and continues** (it does not decide). Likewise **fog** that needs dissolving into tickets (a human grilling act) is set aside.
|
|
28
28
|
|
|
29
|
+
**The Acceptance testing drive-mode ask (AI-driven vs human-driven) is advisory too, and is taken without the confirm gate**: `auto` picks AI-driven whenever any step is agent-performable; it never fakes a human-only step, so when the UAT includes one it leaves that step incomplete and sets the ticket aside, listing the human-only steps in the run-end summary. Full protocol in `../../_shared/techniques/acceptance-testing.md`.
|
|
30
|
+
|
|
29
31
|
**A human-only step is also a stop, even though it is not a choice.** A **HITL `task`** whose next step is a checklist only the human can perform (manual plumbing the agent cannot do: signing up for a service, provisioning access, a physical action) is neither a forced choice nor fog nor a rail action, so the tests above do not catch it, but auto still **cannot execute it alone**. Auto **sets such a ticket aside and continues**, handing the precise checklist to the human in the run-end summary; it never fabricates the human's part. An **AFK `task`** the agent can drive alone chains normally.
|
|
30
32
|
|
|
31
33
|
## The run loop (a fixpoint, not one pass)
|
|
@@ -49,6 +51,6 @@ On the green path a ticket resolves and the loop continues. On a **Verify failur
|
|
|
49
51
|
Whatever ends the run (frontier drained or human interrupt), auto emits **one run-end summary**, in **two places**: a **chat block** for the human, and the **same summary posted as a comment on the map issue** (the durable tracker record). The summary states:
|
|
50
52
|
- **why it stopped** (auto-workable frontier exhausted, or human interrupt);
|
|
51
53
|
- **what resolved** (the tickets auto closed this run);
|
|
52
|
-
- **what remains**: the **set-aside** tickets (fog, `decision` convergences, out-of-scope rulings, HITL `task` checklists, and rail actions including any **ready-to-integrate branch / PR** under `git: pr`) and **why each needs the human**, the **paused / stuck** tickets from the failure budget, and any **blocked dependents** still waiting.
|
|
54
|
+
- **what remains**: the **set-aside** tickets (fog, `decision` convergences, out-of-scope rulings, HITL `task` checklists, human-only UAT steps, and rail actions including any **ready-to-integrate branch / PR** under `git: pr`) and **why each needs the human**, the **paused / stuck** tickets from the failure budget, and any **blocked dependents** still waiting.
|
|
53
55
|
|
|
54
56
|
Then hand back to the human with the normal session handoff framing (`../../_shared/session-handoff.md`): the human decides what to take next (dissolve fog, make a held decision, authorise a rail action, then re-invoke `/trailhead:auto` to drain more).
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
# Ticket types and their engine
|
|
2
2
|
|
|
3
|
-
The per-type engines the `work` verb dispatches to (Mode 2 step
|
|
3
|
+
The per-type engines the `work` verb dispatches to (Mode 2 step 4, in `../SKILL.md`). The `build`/`bug` engine is this cluster's single-cluster reference; `decision`/`research`/`prototype`/`task` round out the six types. `quick` (the no-split variant: a new `"text"` ticket off the map, or an existing ticket where it lives) lives inline in `../SKILL.md`.
|
|
4
4
|
|
|
5
5
|
Every ticket is **HITL** (human in the loop, speaking for themselves) or **AFK** (driven by the agent alone). A HITL ticket resolves only through the live exchange: the agent never stands in for the human's side.
|
|
6
6
|
|
|
7
7
|
Technique names below (**Grilling**, **Domain vocabulary**, **Prototype**, **Research**, **TDD**, **Code review**, **Acceptance testing**, **Debug**) are indexed in `../../_shared/techniques.md`; their full protocols live in `_shared/`, read from `../../_shared/techniques/<name>.md`.
|
|
8
8
|
|
|
9
|
+
## UAT follow-ups (any type)
|
|
10
|
+
A ticket whose body carries `UAT of: <parent>` is a UAT follow-up, **whatever its type label**, including a graduated seed (a `trailhead:seed` that dropped `trailhead:seed` + `trailhead:blocked` on graduating, keeping whatever type label it had, or none). `work`, `quick`, and `auto` all dispatch through this file, so this check runs **before** the per-type engines below. It resolves via the **Acceptance testing** technique (`../../_shared/techniques/acceptance-testing.md`), starting with the drive-mode ask; the record is a `VERIFY` comment on the follow-up itself, never on the parent. It closes only on a confirmed pass, with a resolution comment linking back to the parent.
|
|
11
|
+
|
|
9
12
|
## `decision`: HITL, *default for decision tickets*
|
|
10
13
|
Close a choice, but widen before you narrow:
|
|
11
14
|
|
|
@@ -27,11 +30,11 @@ The ticket that *builds*. Lean cycle, all in **ticket comments**:
|
|
|
27
30
|
|
|
28
31
|
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. Advisory choices raised here **offer** the delegate/defer options, per the injection recipe in `../../_shared/choices.md`.
|
|
29
32
|
2. **Plan**: a `PLAN` comment: the steps, the seams, the files touched, the verification criteria. Apply **TDD** per `config.tdd` (`seams`/`on`/`off`). Produce this plan via a *planner subagent* on `config.models.plan` (dispatch the **`trailhead-plan`** agent, read-only, passing `config.models.plan` as the model override; inline when the model is unset or equals the session model; Execute then runs as its own subagent, see below). If `config.plan_review` is on, run **Cross-AI plan review** before Execute: send the plan to external AI CLIs and converge on their concerns. Now that the files/seam are known, run the **undeclared-coupling check** (`../../_shared/teamwork.md`) against in-progress tickets. **Plan to sufficiency:** stop once you can name the steps, seams, and files with confidence, and never re-read a file already read in this planning pass; act when you have enough to plan, rather than re-deriving what the pass already established. **The single exception:** under an accepted `lean` skip (see the Size triage above), produce a short inline plan here instead of the planner subagent, and skip Cross-AI plan review; absent that accepted skip, this step runs in full as described.
|
|
30
|
-
3. **Execute**: **if this ticket changes user-facing UI (a new screen, a redesign, a changed layout), run the Prototype technique FIRST, before any real UI code**: surface a mockup to react to, routed by `config.design` (disk / claude.ai/design / stitch) and gated by `config.design.approval` (`explicit` waits for a go-ahead, `auto` proceeds after surfacing). **Route by `config.design` and never shortcut a hosted mode to a local disk mockup**: when `config.design` is `claude.ai/design` or `stitch`, the mockup is built there (reusing a cached `design.project`); `disk` is only for `config.design: disk` or the technique's explicit, announced fallback, never a convenience default. Read `config.design` before producing anything. For a redesign or new screen the default is to offer mockups; don't jump straight to code on a UI ticket. **Once the mockup is approved, reproduce it with 100% fidelity** (layout, spacing, sizing, colour, type, copy, and every state), never a loose approximation "in the spirit of" it; if part of the approved mockup is genuinely infeasible, stop and raise it rather than silently deviate (full statement in the **Prototype** technique, `../../_shared/techniques/prototype.md`). Then implement with **atomic commits** (conventional commits, each with a `Refs: #<n>` trailer for this ticket, see **Principles** in `../../_shared/principles.md`), following the repo's `trailhead:conventions` `git:`, straight to `main` by default, or a feature branch + PR when `git: pr`. **Under `isolation: worktree`, do the work in this ticket's `git worktree`** (`trailhead/t<n>` branch), integrating to the trunk at Resolve (see `../../_shared/teamwork.md`). One commit = one verifiable step. Run this via an **executor subagent on `config.models.execute`** (dispatch the **`trailhead-executor`** agent, full tools that can Edit/Write/commit, passing `config.models.execute` as the model override; inline when the model is unset or equals the session model); the commits land on `main`, visible. **Before installing any new dependency, run the verify-before-install gate** (slopsquatting barrier): check the package's legitimacy (name / typosquat similarity to popular packages, age / first-seen date, download counts, repository metadata present and matching) and **stop and ask the user on a suspicious result** (block-and-ask), never installing on your own judgement. On a host with a hook bus the `trailhead-install-guard` `PreToolUse` hook enforces this, blocking a named public-registry install (npm / PyPI / crates.io / Go / RubyGems) until it is vetted; once vetted, proceed by re-running the exact install command prefixed with `TRAILHEAD_VERIFIED_INSTALL=1`. New scope surfacing mid-execute is captured/split, not worked here.
|
|
31
|
-
4. **Verify**: a `VERIFY` comment: run the tests / the plan's criterion; then the **Code review** technique (its subagent reviews *adversarially*: it doesn't defer to a `CLAUDE.md` convention or a "settled decision" memory), **run in the background and collected by polling, never a blind blocking wait** (a hard engine default on every host, see *Reviews never block the session* in `../../_shared/techniques.md`); **read the review's leading `status:` tag to branch deterministically: `status: clean` -> proceed to Resolve, `status: issues_found` -> iterate the fix-and-re-review loop (or run the HITL checkpoint) before resolving**; then the **Goal-backward verification** technique on `config.models.verify` (does the change deliver what the ticket promised, backward from its `Question` and the `PLAN`'s criteria; the read-only `trailhead-verify` subagent), **run in the background and collected by polling** like Code review, branching on its leading `status:` tag (`status: met` -> proceed, `status: gaps_found` -> loop back to Execute for the gap or run the HITL checkpoint before resolving, `status: inconclusive` as the leading tag (all gaps cleared) -> a per-reason-class HITL checkpoint with no auto-retry, never auto-resolving over the unevaluated goal); then **Acceptance testing** if the change is user-facing (
|
|
32
|
-
5. **Resolve**: a resolution comment with what was done → `gh issue close` → update `Decisions so far` on the map. **Pending-UAT guard (before the close).** If Verify left acceptance testing **deferred** (the
|
|
33
|
+
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`). **Make fidelity verifiable, not a hope:** before finishing Execute, enumerate the approved mockup's elements and states, confirm each is reproduced, and **record that enumerated fidelity list as a ticket comment** (noting any element raised as infeasible with its resolution), so Verify works from a durable checklist instead of reconstructing intent. At Verify, **Acceptance testing** checks the built screen against the approved mockup (element by element, state by state) and **Goal-backward verification** carries fidelity as an explicit goal claim (see `../../_shared/techniques/acceptance-testing.md` and `../../_shared/techniques/verify.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.
|
|
34
|
+
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, `status: inconclusive` as the leading tag (all gaps cleared) -> a per-reason-class HITL checkpoint with no auto-retry, never auto-resolving over the unevaluated goal); then **Acceptance testing** if the change is user-facing, opening with the drive-mode ask (`../../_shared/techniques/acceptance-testing.md`): AI-driven, 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. At a HITL checkpoint here, an advisory sub-choice (which fix, how to proceed) **offers** the delegate option, per the injection recipe in `../../_shared/choices.md`; the accept/resolve decision stays the human's.
|
|
35
|
+
5. **Resolve**: a resolution comment with what was done → `gh issue close` → update `Decisions so far` on the map (a body read-modify-write: obey the **Fail-safe body rewrites** rule in `../../_shared/substrate.md`). **Pending-UAT guard (before the close).** If Verify left acceptance testing **not yet run** (it would otherwise be deferred), **pose the two-way choice to the user and wait for an explicit answer, never open a ticket unilaterally**: (a) run the UAT now, in this session, with no ticket (opens with the drive-mode ask, `../../_shared/techniques/acceptance-testing.md`), or (b) open a UAT follow-up ticket to run it later. On (a), run the guided walkthrough now and resolve **only on a confirmed pass** (a failing step becomes a fix or a `bug` follow-up and an incomplete/blocked step is marked pending and tracked, per the guided-UAT rules in `../../_shared/techniques/acceptance-testing.md`, not a silent close); on (b), **open the follow-up ticket carrying the owed UAT first**, linked back to this parent (a `task` on the frontier or a `trailhead:seed` gated on the run, per *Pending UAT must stay tracked* in `../../_shared/techniques/acceptance-testing.md`). **Never open a UAT ticket without asking, never `gh issue close` on a chosen-deferred UAT with nothing opened, and never let silence or an ambiguous reply default to either path.** When acceptance was already completed at Verify, this guard doesn't fire. **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 -c core.quotepath=false show --name-only --format= <hash>`, with `core.quotepath=false` so a non-ASCII filename compares plainly instead of in git's C-quoted form); 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.
|
|
33
36
|
|
|
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).
|
|
37
|
+
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). If the user defers that in-the-moment choice rather than settling it, mark the follow-up **pending** so the **Pending follow-up guard** re-poses it at handoff (`../../_shared/session-handoff.md`), instead of losing it to scrollback.
|
|
35
38
|
|
|
36
39
|
## `bug`: the fix with diagnosis first
|
|
37
40
|
A defect to correct. Unlike `build`, you don't start from implementation: first you **understand**.
|
|
@@ -46,11 +49,11 @@ Then run the cycle, all in ticket comments:
|
|
|
46
49
|
|
|
47
50
|
1. **Repro**: a `REPRO` comment: how to reproduce, expected vs observed behaviour.
|
|
48
51
|
2. **Diagnose**: run the **Debug** technique. A `DIAGNOSIS` comment with the root cause **stated as a falsifiable hypothesis plus the falsification test that confirmed it**. **Pre-fix checkpoint: only a confirmed cause proceeds to Fix** (a cause that has not survived its falsification test stays in diagnosis, looping the hypothesis steps); the full gate lives in the **Debug** technique (`../../_shared/techniques/debug.md`).
|
|
49
|
-
3. **Fix**: implement the correction with **atomic commits** (each with a `Refs: #<n>` trailer for this ticket, see **Principles** in `../../_shared/principles.md`; per the repo's `trailhead:conventions` `git:`, `main` by default, else a branch + PR; in this ticket's `git worktree` under `isolation: worktree`), via an **executor subagent on `config.models.execute`** (dispatch the **`trailhead-fix`** agent, full tools, passing `config.models.execute` as the model override; inline when unset/same as session). Where sensible, a test that fails before and passes after (**TDD**). The **verify-before-install gate** from the `build` Execute step applies here too: vet any new dependency (typosquat similarity, age, downloads, repository metadata) and stop and ask the user on a suspicious result, before installing.
|
|
50
|
-
4. **Verify**: a `VERIFY` comment: the repro now passes, no regressions; **Code review** on the fix, **run in the background and collected by polling, never a blind blocking wait** (a hard engine default on every host, see *Reviews never block the session* in `../../_shared/techniques.md`); **read the review's leading `status:` tag to branch deterministically (`status: clean` -> Resolve, `status: issues_found` -> iterate the loop or run the checkpoint first)**; then the **Goal-backward verification** technique on `config.models.verify` (does the fix deliver what the ticket promised, backward from its `Question`; the read-only `trailhead-verify` subagent), background and collected by polling, branching on its leading `status:` tag (`status: met` -> proceed, `status: gaps_found` -> loop back to Fix for the gap or run the checkpoint, `status: inconclusive` as the leading tag (all gaps cleared) -> a per-reason-class HITL checkpoint with no auto-retry, never auto-resolving over the unevaluated goal); **Acceptance testing** if the bug was user-facing
|
|
51
|
-
5. **Resolve**: a resolution comment (cause + fix) → `gh issue close` → update `Decisions so far
|
|
52
|
+
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. **For a user-facing fix that changes UI, the `build` Execute step's Prototype + approved-mockup fidelity rule applies here too:** where the fix changes a screen, surface a mockup, and once approved reproduce it with 100% fidelity with the same enumerated, Verify-checked fidelity list.
|
|
53
|
+
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, `status: inconclusive` as the leading tag (all gaps cleared) -> a per-reason-class HITL checkpoint with no auto-retry, never auto-resolving over the unevaluated goal); **Acceptance testing** if the bug was user-facing, opening with the drive-mode ask (`../../_shared/techniques/acceptance-testing.md`): AI-driven, 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. At a HITL checkpoint here, an advisory sub-choice (which fix, how to proceed) **offers** the delegate option, per the injection recipe in `../../_shared/choices.md`; the accept/resolve decision stays the human's.
|
|
54
|
+
5. **Resolve**: a resolution comment (cause + fix) → `gh issue close` → update `Decisions so far` (a body read-modify-write: obey the **Fail-safe body rewrites** rule in `../../_shared/substrate.md`). **Pending-UAT guard (before the close).** If Verify left acceptance testing **not yet run** (it would otherwise be deferred), **pose the two-way choice to the user and wait for an explicit answer, never open a ticket unilaterally**: (a) run the UAT now, in this session, with no ticket (opens with the drive-mode ask, `../../_shared/techniques/acceptance-testing.md`), or (b) open a UAT follow-up ticket to run it later. On (a), run the guided walkthrough now and resolve **only on a confirmed pass** (a failing step becomes a fix or a `bug` follow-up and an incomplete/blocked step is marked pending and tracked, per the guided-UAT rules in `../../_shared/techniques/acceptance-testing.md`, not a silent close); on (b), **open the follow-up ticket carrying the owed UAT first**, linked back to this parent (a `task` on the frontier or a `trailhead:seed` gated on the run, per *Pending UAT must stay tracked* in `../../_shared/techniques/acceptance-testing.md`). **Never open a UAT ticket without asking, never `gh issue close` on a chosen-deferred UAT with nothing opened, and never let silence or an ambiguous reply default to either path.** When acceptance was already completed at Verify, this guard doesn't fire. **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`; also a body read-modify-write, same **Fail-safe body rewrites** rule) in place (a line or two). Then close the session with the **Session handoff** ritual (`../../_shared/session-handoff.md`): `/clear` + next command.
|
|
52
55
|
|
|
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**).
|
|
56
|
+
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**). A follow-up raised but left unopened is carried to the **Pending follow-up guard** at handoff (`../../_shared/session-handoff.md`), re-posed at Resolve rather than dropped.
|
|
54
57
|
|
|
55
58
|
## `task`: HITL or AFK
|
|
56
59
|
Manual work that must happen before a *decision* can be made: signing up for a service to judge its API, provisioning access, moving data to see its shape. The agent drives it alone where it can (AFK); otherwise it hands the human a precise checklist (HITL). Resolved when the work is done; the answer records what was done and the resulting facts (where credentials live, new URLs, row counts) later tickets depend on.
|