@marcomigozzi/trailhead 0.14.0 → 0.16.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 +158 -34
- package/README.md +147 -33
- package/bin/auto-intro-docs.test.js +72 -0
- package/bin/auto-merge-docs.test.js +33 -0
- package/bin/auto-merge-ticket-docs.test.js +32 -0
- package/bin/auto-review-rounds-docs.test.js +45 -0
- package/bin/auto-run-docs.test.js +60 -0
- package/bin/auto-scope-docs.test.js +33 -0
- package/bin/auto-slots-docs.test.js +45 -0
- package/bin/auto-uat-docs.test.js +47 -0
- package/bin/base-preflight-docs.test.js +65 -0
- package/bin/execute-split-docs.test.js +146 -0
- package/bin/grilling-holistic-review-docs.test.js +90 -0
- package/bin/isolation-answer-docs.test.js +41 -0
- package/bin/isolation-copy-docs.test.js +28 -0
- package/bin/lib/codex-projection.js +6 -6
- package/bin/lib/codex-projection.test.js +43 -2
- package/bin/needs-you-docs.test.js +41 -0
- package/bin/pick-stress-test-docs.test.js +105 -0
- package/bin/report-status-docs.test.js +195 -0
- package/bin/resolve-helpers-docs.test.js +36 -0
- package/bin/scope-confirm-skip-docs.test.js +70 -0
- package/bin/trailhead.js +6 -6
- package/bin/trailhead.test.js +355 -16
- package/bin/uat-setup-docs.test.js +65 -0
- package/package.json +1 -1
- package/plugins/trailhead/.claude-plugin/plugin.json +1 -1
- package/plugins/trailhead/agents/trailhead-code-review.md +15 -2
- package/plugins/trailhead/agents/trailhead-codebase-map.md +20 -1
- package/plugins/trailhead/agents/trailhead-debug.md +20 -1
- package/plugins/trailhead/agents/trailhead-executor.md +29 -2
- package/plugins/trailhead/agents/trailhead-fix.md +29 -2
- package/plugins/trailhead/agents/trailhead-plan.md +20 -1
- package/plugins/trailhead/agents/trailhead-research.md +22 -3
- package/plugins/trailhead/agents/trailhead-verify.md +15 -2
- package/plugins/trailhead/commands/auto.md +2 -2
- package/plugins/trailhead/hooks/lib/auto-merge.test.js +467 -0
- package/plugins/trailhead/hooks/lib/auto-queue.test.js +245 -0
- package/plugins/trailhead/hooks/lib/auto-run.test.js +318 -0
- package/plugins/trailhead/hooks/lib/auto-slot.test.js +278 -0
- package/plugins/trailhead/hooks/lib/base-preflight.test.js +513 -0
- package/plugins/trailhead/hooks/lib/checkout-in.test.js +173 -0
- package/plugins/trailhead/hooks/lib/commit-message-check.js +1 -1
- package/plugins/trailhead/hooks/lib/commit-message-check.test.js +3 -2
- package/plugins/trailhead/hooks/lib/commit-msg-module-repo.test.js +116 -0
- package/plugins/trailhead/hooks/lib/commit-msg-sync.test.js +100 -9
- package/plugins/trailhead/hooks/lib/dashboard.test.js +472 -0
- package/plugins/trailhead/hooks/lib/fixtures/commit-msg-legacy-js +104 -0
- package/plugins/trailhead/hooks/lib/isolation-answer.test.js +258 -0
- package/plugins/trailhead/hooks/lib/isolation-copy.test.js +298 -0
- package/plugins/trailhead/hooks/lib/issue-body.test.js +470 -0
- package/plugins/trailhead/hooks/lib/merge-ticket.test.js +394 -0
- package/plugins/trailhead/hooks/lib/needs-you.test.js +177 -0
- package/plugins/trailhead/hooks/lib/plan-review-rounds.test.js +319 -0
- package/plugins/trailhead/hooks/lib/report-status.test.js +366 -0
- package/plugins/trailhead/hooks/lib/shell-scan.js +36 -1
- package/plugins/trailhead/hooks/lib/shell-scan.test.js +14 -0
- package/plugins/trailhead/hooks/lib/statusline-context.test.js +140 -0
- package/plugins/trailhead/hooks/lib/uat-backfill.test.js +150 -0
- package/plugins/trailhead/hooks/lib/uat-followup.test.js +169 -0
- package/plugins/trailhead/hooks/lib/uat-setup.test.js +340 -0
- package/plugins/trailhead/hooks/lib/unblock.test.js +207 -0
- package/plugins/trailhead/hooks/lib/work-state.test.js +45 -0
- package/plugins/trailhead/hooks/mod/README.md +209 -36
- package/plugins/trailhead/hooks/mod/auto-run-view.ts +93 -0
- package/plugins/trailhead/hooks/mod/auto-run.ts +165 -0
- package/plugins/trailhead/hooks/mod/map-pane-view.ts +304 -26
- package/plugins/trailhead/hooks/mod/map-pane.tsx +763 -160
- package/plugins/trailhead/hooks/mod/register.tsx +312 -40
- package/plugins/trailhead/hooks/mod/repo-pin.ts +167 -0
- package/plugins/trailhead/hooks/mod/session-registry.ts +65 -0
- package/plugins/trailhead/hooks/mod/turn-state.ts +14 -0
- package/plugins/trailhead/hooks/mod/whiteboard-follow.ts +98 -0
- package/plugins/trailhead/hooks/mod/work-state-locate.ts +191 -0
- package/plugins/trailhead/hooks/mod/work-state-prompt.ts +13 -15
- package/plugins/trailhead/hooks/mod/work-state-view.ts +38 -5
- package/plugins/trailhead/hooks/mod/worktree-porcelain.ts +17 -0
- package/plugins/trailhead/hooks/trailhead-bash-guards.js +8 -8
- package/plugins/trailhead/hooks/trailhead-bash-guards.test.js +22 -16
- package/plugins/trailhead/hooks/trailhead-install-guard.js +169 -34
- package/plugins/trailhead/hooks/trailhead-install-guard.test.js +180 -1
- package/plugins/trailhead/hooks/trailhead-labels-guard.js +231 -0
- package/plugins/trailhead/hooks/trailhead-labels-guard.test.js +212 -0
- package/plugins/trailhead/hooks/trailhead-search-guard.js +43 -7
- package/plugins/trailhead/hooks/trailhead-search-guard.test.js +66 -2
- package/plugins/trailhead/hooks/trailhead-secret-read-guard.js +296 -22
- package/plugins/trailhead/hooks/trailhead-secret-read-guard.test.js +253 -0
- package/plugins/trailhead/skills/_shared/choices.md +11 -4
- package/plugins/trailhead/skills/_shared/configuration-reference.md +17 -5
- package/plugins/trailhead/skills/_shared/load-first.md +3 -3
- package/plugins/trailhead/skills/_shared/multi-map.md +2 -2
- package/plugins/trailhead/skills/_shared/session-handoff.md +1 -1
- package/plugins/trailhead/skills/_shared/substrate-commands.md +47 -14
- package/plugins/trailhead/skills/_shared/substrate.md +9 -4
- package/plugins/trailhead/skills/_shared/teamwork.md +17 -8
- package/plugins/trailhead/skills/_shared/techniques/acceptance-testing.md +18 -10
- package/plugins/trailhead/skills/_shared/techniques/code-review.md +1 -1
- package/plugins/trailhead/skills/_shared/techniques/codebase-map.md +1 -1
- package/plugins/trailhead/skills/_shared/techniques/debug.md +2 -0
- package/plugins/trailhead/skills/_shared/techniques/grilling.md +4 -2
- package/plugins/trailhead/skills/_shared/techniques/plan-review.md +3 -1
- package/plugins/trailhead/skills/_shared/techniques/research.md +4 -2
- package/plugins/trailhead/skills/_shared/techniques/verify.md +1 -1
- package/plugins/trailhead/skills/_shared/techniques.md +12 -2
- package/plugins/trailhead/skills/trailhead/SKILL.md +1 -1
- package/plugins/trailhead/skills/trailhead-capture/SKILL.md +1 -1
- package/plugins/trailhead/skills/trailhead-capture/references/capture.md +3 -3
- package/plugins/trailhead/skills/trailhead-chart/SKILL.md +2 -2
- package/plugins/trailhead/skills/trailhead-chart/references/charting.md +5 -5
- package/plugins/trailhead/skills/trailhead-chart/references/inbox.md +2 -2
- package/plugins/trailhead/skills/trailhead-manage/SKILL.md +1 -1
- package/plugins/trailhead/skills/trailhead-manage/references/auditing.md +1 -1
- package/plugins/trailhead/skills/trailhead-view/SKILL.md +9 -6
- package/plugins/trailhead/skills/trailhead-work/SKILL.md +31 -18
- package/plugins/trailhead/skills/trailhead-work/references/auto.md +70 -22
- package/plugins/trailhead/skills/trailhead-work/references/executor-brief.md +29 -0
- package/plugins/trailhead/skills/trailhead-work/references/ticket-engines.md +29 -13
- package/plugins/trailhead/templates/statusline-trailhead.sh +77 -12
- package/plugins/trailhead/templates/trailhead-auto-merge.js +318 -0
- package/plugins/trailhead/templates/trailhead-auto-queue.js +186 -0
- package/plugins/trailhead/templates/trailhead-auto-run.js +294 -0
- package/plugins/trailhead/templates/trailhead-auto-slot.js +239 -0
- package/plugins/trailhead/templates/trailhead-base-preflight.js +305 -0
- package/plugins/trailhead/templates/trailhead-checkout-in.js +88 -0
- package/plugins/trailhead/templates/trailhead-commit-msg +38 -102
- package/plugins/trailhead/templates/trailhead-commit-msg-sync.js +58 -27
- package/plugins/trailhead/templates/trailhead-commit-msg.cjs +114 -0
- package/plugins/trailhead/templates/trailhead-dashboard.js +360 -0
- package/plugins/trailhead/templates/trailhead-isolation-answer.js +227 -0
- package/plugins/trailhead/templates/trailhead-isolation-copy.js +239 -0
- package/plugins/trailhead/templates/trailhead-issue-body.js +459 -0
- package/plugins/trailhead/templates/trailhead-merge-ticket.js +366 -0
- package/plugins/trailhead/templates/trailhead-needs-you.js +193 -0
- package/plugins/trailhead/templates/trailhead-plan-review-rounds.js +289 -0
- package/plugins/trailhead/templates/trailhead-report-status.js +274 -0
- package/plugins/trailhead/templates/trailhead-uat-backfill.js +114 -0
- package/plugins/trailhead/templates/trailhead-uat-followup.js +128 -0
- package/plugins/trailhead/templates/trailhead-uat-setup.js +385 -0
- package/plugins/trailhead/templates/trailhead-unblock.js +111 -0
- package/plugins/trailhead/templates/trailhead-work-state.js +25 -7
- package/plugins/trailhead/tests/auto-run-ui.test.ts +444 -0
- package/plugins/trailhead/tests/auto-run-view.test.ts +111 -0
- package/plugins/trailhead/tests/map-pane-ui.test.ts +2519 -382
- package/plugins/trailhead/tests/map-pane-view.test.ts +600 -18
- package/plugins/trailhead/tests/map-pane-write.test.ts +51 -0
- package/plugins/trailhead/tests/repo-pin-late-repo.test.ts +77 -0
- package/plugins/trailhead/tests/repo-pin.test.ts +123 -0
- package/plugins/trailhead/tests/session-registry.test.ts +50 -0
- package/plugins/trailhead/tests/whiteboard-follow.test.ts +90 -0
- package/plugins/trailhead/tests/work-state-locate.test.ts +33 -0
- package/plugins/trailhead/tests/work-state-prompt-hook.test.ts +276 -2
- package/plugins/trailhead/tests/work-state-prompt.test.ts +29 -35
- package/plugins/trailhead/tests/work-state-ui.test.ts +73 -1
- package/plugins/trailhead/tests/work-state-view.test.ts +44 -1
- package/plugins/trailhead/tests/work-state-worktree.test.ts +310 -0
- package/plugins/trailhead/types/index.d.ts +62 -3
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
# The autonomous run: `auto`
|
|
2
2
|
|
|
3
|
-
`auto` runs a
|
|
3
|
+
`auto` runs a chosen **run scope** autonomously (one map, the whiteboard, or everything), ticket by ticket, until a stop condition fires or the scope's frontier is exhausted. It is the one verb that **suspends the one-ticket-per-session rule**: while engaged it chains `build` / `bug` / `task` tickets in a single run. It is invoked explicitly, per run, as `/trailhead:auto [map]` (the optional `<n>` is the scope shortcut below); it **never re-engages on its own**. The run itself is an **autonomous run** (the noun); `auto` is the verb. Everything else about the engine (the substrate, the per-type engines, claiming, the session handoff) is unchanged: `auto` orchestrates the existing cycle, it does not replace it.
|
|
4
4
|
|
|
5
5
|
Load the same shared core the cluster loads (`../../_shared/principles.md`, `substrate.md`, `session-handoff.md`, `configuration.md`, `techniques.md`, `ticket-language.md`), then this file.
|
|
6
6
|
|
|
7
7
|
## Preconditions and engagement
|
|
8
|
-
1. **Pick the
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
1. **Pick the run scope.** The queue is built by `node <templates>/trailhead-auto-queue.js` (self-contained helper, one JSON line on stdout; `<templates>` uses the same three-dir lookup as `trailhead-work-state.js`).
|
|
9
|
+
- **Read the run record first.** Run `node <templates>/trailhead-auto-run.js read --repo <main-checkout>` before any ask (one JSON line). The record `<main-checkout>/.trailhead/auto-run.json` holds the run's start-up choices (scope, slots, review) and its skip and unmerged lists, so a compaction or a crash does not lose them (`sameSession` is always false on a host with no session id (Codex): there a re-invocation after a crash always gets the resume-or-fresh ask below, the accepted fallback, while a mid-run compaction never does, per the compaction rule in "The run loop"); it is written at the main checkout only and is gitignored (add `.trailhead/auto-run.json` to the repo's `.gitignore` if absent).
|
|
10
|
+
- `found` with `sameSession: true`: the record is this session's own run seen again after a compaction, so reuse it without asking: no scope, slot or review ask. The reuse is announced in one chat line naming the reused scope, slots and review choice, so the human sees what is being reused. An Esc/Stop interrupt does not clear the record (nothing is written at interrupt time, see "Reporting, interruption, resume"), so re-invoking `/trailhead:auto` in the same session after an Esc reuses the record with its choices, which is expected; to be asked again, re-invoke from a new session (the record then reads as another session's, which gets the resume-or-fresh ask below) or give an explicit `/trailhead:auto <n>` for a different map.
|
|
11
|
+
- `found` with `sameSession: false`: the record belongs to another session, either a crashed run or **a run still active elsewhere**, which the helper cannot tell apart, so ask once (`AskUserQuestion`, a numbered list without a structured question tool; no delegate option, the human's like the start-up asks) whether to resume that run with its recorded choices (only if no other session is still running it) or to start fresh (`start --replace` after the usual asks). This is the one start-up question a resumed run may pose, and it replaces the three asks. On resume, reload the record's skip and unmerged lists.
|
|
12
|
+
- An explicit `/trailhead:auto <n>` whose map differs from a found `map:<n>` scope (or a found non-map scope) starts a fresh run: ask as usual, then `start --replace`.
|
|
13
|
+
- `absent`: ask as below, then run `node <templates>/trailhead-auto-run.js start --repo <main-checkout> --scope <s> --slots <n> --review <r>` right after the last start-up ask and before anything is claimed (`<s>` is `map:<n>`, `whiteboard` or `everything`; `<r>` is the review choice token, or `skip` when the ask was skipped). `corrupt` (reported in one line) is treated like `absent`, but the write is `start --replace`, since a plain `start` would answer `exists` over the corrupt file.
|
|
14
|
+
- Any `skipped` result (an I/O error on any subcommand) is reported in one chat line and the run continues: a skipped `read` is treated as `absent` (ask, then `start --replace`), and a skipped `start`, `skip`, `unmerged` or `clear` leaves the record stale or missing, nothing else changes. The record is a safety net, not a gate.
|
|
15
|
+
- `/trailhead:auto <n>` runs map `<n>` **without asking** (the helper's `queue` validates it: a closed or non-map `<n>` is reported, not run).
|
|
16
|
+
- With no argument, ask **once** via `AskUserQuestion`: one map, the whiteboard, or everything, with the options and their frontier counts from `node <templates>/trailhead-auto-queue.js scopes`. When one map is chosen, then ask which map (skipped when only one map is open; the active map, if open, is the recommended option). This start-up ask is the human's: the scope is not advisory, so there is no delegate option, and it is asked before anything is claimed. On a host without a structured question tool, the same options go as a numbered list.
|
|
17
|
+
- **Parallel slots: 1, 2, 3 or 4.** Right after the run scope, ask **once** via `AskUserQuestion` how many tickets the run keeps in flight at once: 1, 2, 3 or 4, with **2 proposed** as the Recommended first option (a numbered list on a host without a structured question tool). `/trailhead:auto <n>` skips only the scope ask: it **still asks the slot count**. Like the scope, this ask is the human's: there is no delegate option, and it is asked before anything is claimed. With **1 slot** the run is the sequential run in the current checkout; above 1 it follows "Parallel slots" below.
|
|
18
|
+
- **External review rounds.** Right after the slot ask, run `node <templates>/trailhead-plan-review-rounds.js ask --repo <project-root>` (add `--host codex` on Codex; one JSON line). Any non-empty `warnings` (an `unknown` reviewer name, a bad `plan_review.rounds`) is printed in chat first, before the ask or the skip. On `skip` (plan review off, or no independent reviewer CLI installed, the host's own CLI not counting) there is no ask. On `ask`, pose one `AskUserQuestion` with the helper's `options` (at most four, **as in config** Recommended first; the full five as a numbered list, the helper's `fallback`, on a host without a structured question tool): as in config, 1, 2, 3, or until convergence (stop on a round with no progress, hard ceiling 5). On `error` (an unreadable or invalid config, a failed probe) the run reports it and hands back before any claim, like a queue helper error. Like the scope and the slot count, this ask is the human's: there is no delegate option, and it is asked before any claim, also under `/trailhead:auto <n>`. The choice holds for every `build` ticket of the run (each slot's plan step included) and overrides `plan_review.rounds` for that run; after each review round the plan step calls `node <templates>/trailhead-plan-review-rounds.js round --choice <choice> --configured <n> --history <file>` (see `../../_shared/techniques/plan-review.md`).
|
|
19
|
+
- **Order.** Under everything the run works one map at a time by ascending map number, the whiteboard last; inside a scope the oldest workable ticket first (lowest number), blockers respected through the frontier (`trailhead:blocked` tickets are never queued). The queue also skips every `trailhead:needs-you` ticket (one the run parked for the human, see "Parking a ticket for the human" below); the run only ever takes `next` and re-queries with `--skip` as it marks tickets needs-you or stops on them.
|
|
20
|
+
- **A helper error stops the run** (non-zero exit, `status: error`, unparseable output, including a truncated read): before any claim, the run reports it and hands back; mid-run, it stops taking new tickets and goes to the run-end summary. It never treats an error as a drained scope. Tickets the helper lists under `drift` (on more than one open map) are never worked and are listed in the run-end summary; `drift` can include a paused ticket you own, which is then neither resumed nor claimed, so the summary names it as your own paused work to fix by hand.
|
|
21
|
+
2. **Resume an interrupted ticket first.** Before taking anything new, resume **each** ticket you own that carries `trailhead:paused` **within the scope** (the `resume` lists of `queue`'s groups), in queue order, plus any owned ticket in the scope whose latest checkpoint is a `PAUSED` comment (a ticket paused before the label existed), from a prior interrupted or stuck run. A paused ticket is off the frontier, so this step is the only way a run picks one back up, never the frontier scan. For each one, pick it back up via the **`resume`** engine (`../../_shared/teamwork.md`) and carry it to resolution first, then continue into the loop below. An owned, open, in-scope ticket named by a leftover work state file (`node <templates>/trailhead-work-state.js`) whose work state has `auto: true` (an interactive session writes `auto: false`, and a handback resets it, so `auto: true` left behind is the crash signature) and is not `trailhead:paused`, and whose latest checkpoint is not a `PAUSED` comment (no `PAUSED` at all, or engine comments after the last one, as for a ticket interrupted twice): the in-flight ticket of a crash or of a host interrupt, which leave the same trace, is resumed first too, from its latest engine comment. **Reconstruct the missing checkpoint first.** Before the normal resume, the run posts a `PAUSED` checkpoint comment on that ticket (the contents of the pause checkpoint in `../../_shared/teamwork.md`, "Pausing & resuming": what is done, the exact next step, the local state, naming the slot workspace path and branch when it has one) that notes it was reconstructed at resume after an interrupt or crash, then adds `trailhead:paused` (after the comment, so a failed comment never leaves a label without a checkpoint), and then resumes the ticket through the `resume` engine, which drops the label. It is idempotent: reconstruction is skipped only when the latest checkpoint is already a `PAUSED` comment or the label is present, so no second checkpoint is posted. Reconstruction happens when the ticket is actually resumed (when it takes a slot), so an extra waiting for a free slot is untouched until then. A slot ticket whose work state step is `resolve` is exempt from reconstruction: it is re-driven through Resolve directly (below). **Known limit:** a crash in the instant between the claim and the work-state write leaves the ticket claimed with no local trace; it stays visible as your own open ticket and is not resumed automatically (resuming every owned open ticket would grab work done by hand in another session). **Where it resumes.** Also run `node <templates>/trailhead-auto-slot.js list --repo <main-checkout>` (the slot workspaces, worktrees and sibling clones, each with its own work state). A ticket that meets the conditions above (the resume conditions are unchanged: owned, in scope, not needs-you, and paused / latest checkpoint `PAUSED`, or crashed with `auto: true` and a latest checkpoint that is not a `PAUSED` comment) and that has a slot workspace (its own work state found there, or the path its `PAUSED` checkpoint names) is resumed **in that workspace**, whatever the slot count chosen for this run. A workspace whose ticket fails the conditions (a manual branch, a needs-you ticket) is left alone. Resumed tickets take slots first; extras wait for a free slot. **An interrupted merge first.** Before resuming anything, run `node <templates>/trailhead-auto-merge.js recover --repo <main-checkout>`: `clean` means nothing was under way; `aborted` (it names the ticket) means a run's merge was cut short and has been aborted, so that ticket's merge is retried (its Resolve re-runs from the preflight); `foreign` (a human's merge, a rebase) hands the run back without touching it. **A slot ticket whose work state step is `resolve`** is re-driven through Resolve idempotently, whatever its branch state: `merge` answers `already-merged` when the merge landed before a crash, and every later Resolve move checks before acting (the resolution comment is posted only if none exists, the close only if the ticket is open, the `Decisions so far` pointer appended only if absent, and `trailhead-unblock.js` and `remove` are idempotent). This is how an interrupted run is continued: the human re-invokes `/trailhead:auto`, and auto resumes the in-flight ticket before taking anything new.
|
|
22
|
+
3. **Load config once** from the project root, as every session does. `auto` reads no config of its own (there is none, per the decisions).
|
|
11
23
|
|
|
12
24
|
## The safety rail (always on, checked FIRST)
|
|
13
25
|
Independent of and **above** the advisory boundary below, `auto` **never performs an irreversible or outward-facing action without an explicit human go-ahead**. Before any automatic choice handling, check the rail. The rail stops on:
|
|
@@ -17,42 +29,78 @@ Independent of and **above** the advisory boundary below, `auto` **never perform
|
|
|
17
29
|
- a **delete / force** (`git push --force`, `rm -rf` of tracked work, a force-delete of a branch or remote ref);
|
|
18
30
|
- any other **outward-facing** action (posting outside the tracker, calling an external service that mutates state).
|
|
19
31
|
|
|
20
|
-
When the next step of a ticket would trip the rail, auto **
|
|
32
|
+
When the next step of a ticket would trip the rail, auto **marks that ticket needs-you and continues** (it does not perform the action and does not block the whole run): it hands the outward action back to the human with a `NEEDS YOU` comment naming it, recorded in the run-end summary. **Atomic local commits to `main` are not rail-tripping**: they are the normal Execute and continue automatically. **A local merge of a verified slot branch into the trunk is not rail-tripping either** (like atomic local commits, see "Integration under `git: main`"); the push stays the human's. The rail is **hardcoded**, not a disableable toggle.
|
|
21
33
|
|
|
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 **
|
|
34
|
+
**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 **marks the ticket needs-you for the push / PR / integration step**, 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`. **The stale-base preflight never auto-resolves.** A `conflict`, `trunk-diverged`, `unverified`, `dirty` or `sync-failed` status, or a failing preflight check, is a stop for that ticket (marked needs-you with the status named in the comment and the run-end summary); auto never resolves a merge conflict itself. **Isolation under a run:** with **1 slot** (the sequential run), `isolation: auto` (the default) and `none` resolve to the **current checkout** during an autonomous run without asking (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 marked needs-you exactly as above. **Above 1 slot** the run gives each ticket **a worktree whatever `isolation:` says** (`none` and `auto` included), `clone` stays `clone`, and a superrepo (a `.gitmodules` declaring a submodule) escalates to `clone`; see "Parallel slots".
|
|
23
35
|
|
|
24
36
|
## The advisory boundary (the `choices.md` one-line test, no confirm gate)
|
|
25
37
|
Wherever the engine would put a **forced choice** to the human, apply the one-line test in `../../_shared/choices.md`:
|
|
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
|
-
- **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` **
|
|
38
|
+
- **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, stress-tested first per **Stress-test the pick before offering it** in `../../_shared/choices.md` (no human checks it here, so the pass is never skipped on a non-trivial choice), records the pick with its `Weak point:` line in the ticket comment of the step the choice belongs to (the run-end summary when it belongs to no ticket), 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.
|
|
39
|
+
- **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` **marks that ticket needs-you and continues** (it does not decide). Likewise **fog** that needs dissolving into tickets (a human grilling act) is marked needs-you.
|
|
28
40
|
|
|
29
|
-
**The Acceptance testing drive-mode ask (AI-driven
|
|
41
|
+
**The Acceptance testing drive-mode ask (AI-driven, human-driven, or skip) is advisory too, and is taken without the confirm gate**: `auto` picks AI-driven whenever any step is agent-performable, runs those steps and records them in the round's `VERIFY`, and never fakes a human-only step. A failing agent-performable step is still an acceptance finding (fix it or open a `bug`), never waved through. `auto` never picks skip (waiving acceptance is the human's call). **Human-only UAT steps no longer stall the ticket.** When human-only steps remain (or the whole UAT is human-only), the run takes the Pending-UAT guard's option (b) as its own choice: the run itself is the human's go-ahead, decided in the map's design ticket, and it is the one exception to "never open a UAT ticket without asking". First `node <templates>/trailhead-uat-followup.js find --parent <n> --repo <owner/name>`; when `reuse` names an open follow-up, reuse it and open nothing (a resume never opens a second one). Otherwise open one (a `task` on the same map, or on the whiteboard for a whiteboard ticket, or a `trailhead:seed` when the human steps are gated on an external trigger, per the routing in `acceptance-testing.md`), carrying `trailhead:uat`, the `UAT of: [<parent name>](<parent url>)` line and the human-only steps as the full numbered checklist. THEN Resolve the parent (its resolution comment names the follow-up), unblock dependents and continue. Only an open follow-up is reused (the lowest-numbered when several); closed ones never count; the run never closes duplicates. find-then-create is not atomic, which is safe because the run claims the parent before its UAT and one orchestrator makes every tracker call of the run, so two follow-ups are never opened at once. **A helper error is not a "none found"**: the run does not open a follow-up blind, it marks the parent needs-you (naming the helper error) and continues. **UAT setup in `auto`** runs the configured `uat.setup` only: no probe, no proposal, no save offer. A failing setup (or an `invalid` config) marks the ticket needs-you and lists the failure in the run-end summary. Full protocol in `../../_shared/techniques/acceptance-testing.md`.
|
|
30
42
|
|
|
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 **
|
|
43
|
+
**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 **marks such a ticket needs-you and continues**, handing the precise checklist to the human in the `NEEDS YOU` comment and the run-end summary; it never fabricates the human's part. A UAT follow-up (`trailhead:uat`) whose remaining steps are human-only is exactly such a HITL `task`: the run never works it and it joins the skip list. An **AFK `task`** the agent can drive alone chains normally.
|
|
32
44
|
|
|
33
45
|
## The run loop (a fixpoint, not one pass)
|
|
34
|
-
Repeat until
|
|
35
|
-
1. **
|
|
36
|
-
2. **Take
|
|
37
|
-
3. **On resolution**, do the normal Resolve book-keeping, then **unblock dependents** (
|
|
46
|
+
Repeat until the queue's `next` is null (nothing auto-workable left in the scope):
|
|
47
|
+
1. **Query the queue**: `node <templates>/trailhead-auto-queue.js queue --scope <scope> --skip <csv>` with the run's skip list (the tickets already marked needs-you or stopped on this run), and take `next` (the lowest-numbered ticket of the first group that still has one). Do not take a ticket whose immediate next step is a human-necessary decision, fog, a HITL `task`'s human-only step, or a rail action (those are marked needs-you for the human).
|
|
48
|
+
2. **Take `next`** if it is auto-workable, claim it, and resolve it with its type's engine in `ticket-engines.md`. The types the agent can drive alone chain automatically: `build`, `bug`, an **AFK** `task`, and an AFK `research` (already exempt from one-ticket-per-session, so it needs no special suspension). Mark needs-you a `decision` or `prototype` that needs the human, a **HITL** `task` whose next step is a human-only checklist, and a UAT follow-up (`trailhead:uat`) whose remaining steps are human-only: a ticket judged not auto-workable joins the skip list (and is marked needs-you for the human) and the loop re-queries, so the run leaves a map only once every frontier ticket of that map is resolved or skipped. With 1 slot the active-map marker follows the map in play (above 1 no slot writes it, see "Parallel slots"); a whiteboard ticket writes work state with `--map none` and leaves the marker alone. The per-ticket engine runs its **normal configured cycle** (TDD, Code review, Goal-backward verification per config): auto is **orthogonal to `effort` and `models.*`** and changes neither. The run loop is orchestrated by the session model.
|
|
49
|
+
3. **On resolution**, do the normal Resolve book-keeping, then **unblock dependents** with `node <templates>/trailhead-unblock.js --issue <n> --repo <owner/name>` (it removes `trailhead:blocked` from any dependent whose last blocker just closed) and **re-query**. A ticket that graduates onto the frontier this way comes back through the next `queue`, so a map drains before the run moves on to the next one: this is why the loop is a fixpoint, not a single sweep.
|
|
38
50
|
4. **Emit a live chat progress line** as the run advances (below).
|
|
39
51
|
|
|
40
|
-
|
|
52
|
+
**After a context compaction mid-run, whatever the slot count** (a sequential run included), the orchestrator re-reads the record with `node <templates>/trailhead-auto-run.js read --repo <main-checkout>` and reuses its scope, slots, review choice, skip and unmerged lists whatever `sameSession` says, without asking. It knows it is mid-run from its own context (the compaction summary of an active run). The `sameSession` ask applies only at engagement, that is, a fresh `/trailhead:auto` invocation. With slots it also rebuilds the slot table (see "Parallel slots").
|
|
53
|
+
|
|
54
|
+
**Persisting the skip list.** Every time a ticket joins the run's skip list (marked needs-you, paused by the failure budget, stopped on), run `node <templates>/trailhead-auto-run.js skip --repo <main-checkout> --ticket <n>` (idempotent), so the list survives a compaction.
|
|
55
|
+
|
|
56
|
+
The run ends only when the queue finds **nothing auto-workable** (the auto-workable frontier is exhausted), or on **human interrupt** (below). There is **no cap** on tickets, wall-clock, or tokens: a human wall never ends the run, it only shrinks the auto-workable set (auto **drains** rather than halts).
|
|
57
|
+
|
|
58
|
+
## Parallel slots
|
|
59
|
+
With more than one slot the run keeps up to that many tickets in flight at once, each in its own workspace on a `trailhead/t<n>` branch. The run stays **one orchestrating session**: the only one that talks to the tracker for the run, claims, and fills slots.
|
|
60
|
+
|
|
61
|
+
- **Background dispatch, never inline.** Every engine step that has a technique agent (plan, execute, fix, debug, code review, goal-backward verify) is dispatched as a **background subagent** with the slot's **absolute workspace path as its working root**, even when its model equals the session model: with more than one slot nothing runs inline, or one slot would freeze the others. The orchestrator advances whichever slot's step finished: that slot's tracker moves (comments, labels, close), its preflight and its acceptance steps, then it dispatches the slot's next step.
|
|
62
|
+
- **Filling a free slot.** A slot frees when its ticket resolves, is parked needs-you, or is paused by the failure budget. The orchestrator then runs `queue --scope <scope> --skip <csv>` with the run's skip list **plus every in-flight ticket**, and takes `next`. Tickets of different maps can therefore run together while oldest-first holds. Under the plan-time coupling check (advisory), worktrees isolate the tickets and a conflict surfaces at integration, not before.
|
|
63
|
+
- **Opening a slot.** At claim, capture the trunk SHA (the local trunk HEAD at that moment), then run `node <templates>/trailhead-auto-slot.js open --repo <main-checkout> --ticket <n> --base <sha>`. The workspace is `<parent of the main checkout>/<repo>-t<n>` on `trailhead/t<n>`, created at exactly `--base`. The output carries a `kind` (`worktree` or `clone`). The status is `created`, `reentered` (the branch already has a workspace: a worktree, or an existing clone at the slot path; a `reentered` clone is used as is, nothing is cloned over it) or `reattached` (the branch existed without a worktree). `error` is a helper error for that ticket: the slot path exists and is not a checkout of the branch, the base does not resolve, or the branch's worktree is a stale worktree (git still lists it but its directory was deleted by hand; the reason names it and says to run `git worktree prune`, which the helper never does itself). The ticket is not worked: park it needs-you with `trailhead-needs-you.js park` naming the reason, and it joins the skip list and the run-end summary. `list` reports a stale worktree as a warning, not as a slot. On `clone-required` (a superrepo, and only when the slot path does not exist yet) nothing was created: set the clone up at the same sibling path per the `clone` setup in `../../_shared/teamwork.md` (`git clone --recurse-submodules`, or the folder copy, git-independence checked), then `git -C <clone> checkout -b trailhead/t<n> <base>` and `git -C <clone> submodule update --init --recursive`, so a clone slot carries the same path, branch and base as a worktree slot (and `list` finds it).
|
|
64
|
+
- **Per-workspace state.** Right after `open`, write the slot's work state: `env -u CLAUDE_CODE_SESSION_ID node <templates>/trailhead-work-state.js set --repo <workspace> --map <map|none> ... --auto true --auto-done <n>`, and the `.trailhead/session-ticket` marker at the workspace root; only then the `trailhead-isolation-copy.js` copy and the commit-msg sync (so a crash after setup still leaves the `auto: true` resume signal; the gap between the claim and `open` stays the known limit). The commit-msg hook (shared `.git/hooks`) reads the marker of the workspace it commits in. **The slot work states are sessionless on every write**: every `set` on a slot workspace (the first one, each per-step update with `--auto true --auto-done <n>`, and the run-end reset) uses the same `env -u CLAUDE_CODE_SESSION_ID` form (`CLAUDE_CODE_SESSION_ID` unset for that call), because one session-bearing write would let the mod adopt that slot as the ticket in play: the mod's prompt hint adopts only a state naming this session, so several slot states of one session would be ambiguous and never pick one slot as "the ticket in play"; crash detection keys on `auto: true`, not on the session.
|
|
65
|
+
- **Active map.** A slot never writes the `.trailhead/active-map` marker of the main checkout (slots of different maps would overwrite each other). Each slot's map lives in its own work state (`--map`) and in the orchestrator's slot table, and every map-scoped call (frontier `queue`, `Decisions so far`, unblock) names the map explicitly. With 1 slot the marker follows the map in play, as before.
|
|
66
|
+
- **The slot table.** The orchestrator keeps the table (ticket, map, workspace, branch, current step) in its own context. After a context compaction it rebuilds the slot table from `trailhead-auto-slot.js list` and the slot work states, on top of the run's choices and lists restored by the general compaction rule in "The run loop".
|
|
67
|
+
- **Integration under `git: main`.** After Verify passes in a worktree slot, the orchestrator merges the slot branch into the local trunk (never a push), one merge at a time. First the Resolve stale-base preflight, unchanged from `../../_shared/teamwork.md`: the main checkout `--sync`, then the ticket branch `--sync` against it, with a re-review when a rebase rewrote the ticket's commits. Then `node <templates>/trailhead-auto-merge.js merge --repo <main-checkout> --ticket <n>` (one JSON line; it merges `trailhead/t<n>` into the trunk as a fast-forward when possible, else as a merge commit `chore(auto): merge trailhead/t<n>` carrying `Refs: #<n>`; another ticket's `.trailhead/session-ticket` marker in the main checkout never blocks it: a fast-forward creates no commit, and a true merge commit skips the commit-msg hook with `--no-verify` only when the marker names another ticket). The result decides the move:
|
|
68
|
+
- `merged` or `already-merged`: post the resolution comment naming the merge, close the ticket, append the `Decisions so far` pointer, **unblock its dependents** with `node <templates>/trailhead-unblock.js --issue <n> --repo <owner/name>`, then `node <templates>/trailhead-auto-merge.js remove --repo <main-checkout> --ticket <n>` and free the slot. Dependents that graduate onto the frontier open their slot at the new trunk SHA. The `behind` list in the result names the other slots now behind the trunk (informational).
|
|
69
|
+
- `conflict`: the branch stays **separate** and the workspace is kept (the helper already aborted the merge, the trunk is untouched). Right after the abort, **before any tracker call**, persist the branch: `node <templates>/trailhead-auto-run.js unmerged --repo <main-checkout> --ticket <n> --branch trailhead/t<n> --file <path> [--file <path> ...]` (one `--file` per conflicting path). Next, right after the abort, before parking, record the branch on the **merge ticket**: `node <templates>/trailhead-merge-ticket.js add --scope <map-<n>|whiteboard> --ticket <n> --file <path> [--file <path> ...] --workspace <path> --repo <owner/name>` (one `--file` per path in the merge result's `conflicts`; `--scope` is the ticket's map, or `whiteboard` for a loose ticket). Then park the ticket needs-you: the park reason names the branch, the workspace, the conflicting files and the merge ticket. Then, after the merge-ticket `add` succeeds, repeat the same `unmerged` call with `--merge-ticket <n>` (an upsert). A crash or a failed `add` in between still leaves the branch on the persisted unmerged list, with no merge ticket, which the run-end `add` retry reads. Add the branch to the run's in-memory **unmerged list** (branch, files), which the run-end summary reads, and keep the merge tickets the run touched (per scope) for the summary. The ticket stays open, so its dependents stay blocked. A helper error is reported in chat and named in the park reason; the branch stays on the unmerged list and the `add` is retried once at run end, and a retry that still fails is listed in the summary as no merge ticket written (never read an error as a success).
|
|
70
|
+
- `dirty`, `not-on-trunk`, `slot-dirty`, `in-progress` and `merge-failed`: park the ticket needs-you naming the status; the branch and the workspace are kept.
|
|
71
|
+
- `error`: hand back at once, with no further merge (an `error` can mean the main checkout was left mid-merge).
|
|
72
|
+
**The merge ticket.** Each unmerged branch is recorded on one merge ticket per map (or the whiteboard), opened at the first unmerged branch of that scope and updated after each later one, right when the `conflict` arises, never only at run end. It is a needs-you `task` (labels `trailhead:ticket`, `trailhead:task`, `trailhead:needs-you` and the scope label; a sub-issue of the map), so no run takes it, and it is closed by the human once every listed branch is merged or dropped. It lists each branch with its ticket, workspace and conflicting files, and suggests a merge order (oldest ticket first). The helper finds an existing one itself and reuses it; a map or the whiteboard with no unmerged branch gets no merge ticket. Held branches (`git: pr`, clone slots) and the other parked merge statuses are not on the unmerged list and open none.
|
|
73
|
+
Other in-flight slots are never rebased mid-step: each picks up the new trunk at its next stale-base preflight (Verify start, Resolve), which for an unpublished ticket branch syncs onto the local trunk once it is strictly ahead of `origin`.
|
|
74
|
+
- **A `remove` failure after the close is a cleanup error, not a park.** Any `remove` result other than `removed` (`error`, `not-merged`, `kept`) comes after the ticket is already resolved: never park or reopen it; free the slot, list the workspace and the branch in the run-end summary for the human to remove, and a later `remove` retry finishes it (`remove` is idempotent).
|
|
75
|
+
- **Integration stays held under `git: pr`, and for a clone slot.** Under `git: pr`, or for a clone slot (a superrepo: publishing a gitlink is a rail action), the run does not merge. A slot ticket that completes its whole local cycle on its branch is parked needs-you for integration with `node <templates>/trailhead-needs-you.js park`, the reason naming its branch and its workspace path; the branch and the workspace are **kept**, and both are listed in the run-end summary.
|
|
76
|
+
|
|
77
|
+
## Parking a ticket for the human (the needs-you mark)
|
|
78
|
+
Every path above that hands a ticket to the human parks it **at once** with `node <templates>/trailhead-needs-you.js park --issue <n> --reason <text> --repo <owner/name>` (see `../../_shared/substrate-commands.md`): a human-necessary decision, fog, a HITL human-only checklist, a rail action, a stale-base stop, a failing UAT setup, a helper error on the UAT follow-up find, and a surviving blocking review concern (the same call). The trigger is the plan review not converging: when `trailhead-plan-review-rounds.js round` returns `exhausted`, `no-progress` or `ceiling`, the blocking concerns survive the rounds, and the ticket is parked after the `PLAN` and `PLAN-REVIEW` comments are posted, the reason quoting the full `text` of each concern in `surviving` (never just its key); the ticket joins the skip list (its slot frees) and the run continues. The helper secret-scans the reason, writes the `NEEDS YOU` comment (first line `NEEDS YOU`, then what the human must do), unassigns the run and adds `trailhead:needs-you` last. There are no new parking triggers: only the paths auto already hands back. The ticket joins the run's skip list.
|
|
79
|
+
|
|
80
|
+
A needs-you ticket is a mark for autonomous runs only: it stays on the frontier and the map for the human, and `/trailhead:work` still offers it. The queue skips it, and a re-invocation of `/trailhead:auto` does not reconsider it; it comes back to a run only after the human clears the label (a `work`/`quick` claim clears it, or `gh issue edit <n> --remove-label trailhead:needs-you`). A helper error from `park` is reported in chat and the ticket still joins the skip list for this run; never read it as a success.
|
|
41
81
|
|
|
42
82
|
## Per-ticket failure budget (a ticket that will not verify)
|
|
43
83
|
On the green path a ticket resolves and the loop continues. On a **Verify failure**, auto does not hand back at the would-be checkpoint. Instead it **extends past the engine's normal bounded fix-and-re-review rounds**, taking the advisory "which fix" sub-choice as **delegate** (the accept / resolve-anyway decision still stays the human's, so at true exhaustion it does not silently resolve a red ticket). The per-ticket **failure budget** is: a **no-progress rule** (stop iterating once a round makes no measurable progress) **plus a hardcoded ceiling of 3 extra rounds** (tunable only in code; there is no config surface). At budget exhaustion, still red, auto **pauses the stuck ticket keeping its claim** (a `PAUSED` checkpoint) and **skips to the next frontier ticket** (skip-and-continue, not stop-the-run). A single stuck ticket never ends the run; its dependents stay blocked naturally. Skipped / stuck tickets are reported at run end. The accepted cost is stranded dependents and accumulated paused tickets.
|
|
44
84
|
|
|
85
|
+
## Records as it goes
|
|
86
|
+
Each record the human needs is written on the tracker **when it arises**, never held for the run end: the per-ticket engine comments and resolution, the UAT follow-up (opened before the parent closes), the merge ticket (opened at the first unmerged branch of a scope and updated after each later one), the `PAUSED` checkpoint of a stuck ticket (written at budget exhaustion), and a `NEEDS YOU` comment on every needs-you ticket naming what the human must do (written the moment it is marked: for a rail action, the action to authorise; for a stale-base stop, the status), unless the ticket's latest `NEEDS YOU` comment already names the same action (the helper posts nothing new for it); a UAT follow-up (`trailhead:uat`) is exempt, since its own checklist is already the record. The chat progress line is a mirror, not a record. The run-end summary therefore only summarises records that already exist.
|
|
87
|
+
|
|
88
|
+
**Hard stop.** Every record that has arisen is already on the tracker (engine comments as each step posts them, atomic commits, the UAT follow-up, `NEEDS YOU` comments, `PAUSED` checkpoints), so a hard stop (closed terminal, crash) loses nothing that has arisen. The in-flight ticket of a crash has no `PAUSED` checkpoint, but it keeps its claim, its posted comments, its commits and the leftover work state (`auto: true`), which engagement step 2 resumes from.
|
|
89
|
+
|
|
45
90
|
## Reporting, interruption, resume
|
|
46
|
-
- **In-run reporting = both.** The per-ticket engine comments (`DISCUSS`/`PLAN`/`VERIFY`/resolution, etc.) land on the Issues **as usual**, and auto **also emits a live chat progress line** as it advances (e.g. which ticket it just resolved and what it is taking next), so the human can watch a run without reading the tracker.
|
|
47
|
-
- **Interruption = the host interrupt (Stop/Esc), the only mid-run halt.** There is no separate "stop after this ticket". Atomic commits plus ticket-boundary checkpoints make interruption **safe by construction**:
|
|
48
|
-
- **Resume = the human re-invokes.** `auto` never resumes on its own. The human runs `/trailhead:auto` again; engagement step 2 above picks the interrupted ticket back up via `/trailhead:resume` before scanning, and the run's earlier
|
|
91
|
+
- **In-run reporting = both.** The per-ticket engine comments (`DISCUSS`/`PLAN`/`VERIFY`/resolution, etc.) land on the Issues **as usual**, and auto **also emits a live chat progress line** as it advances (e.g. which ticket it just resolved and what it is taking next), so the human can watch a run without reading the tracker. With parallel slots the line names the tickets in the slots.
|
|
92
|
+
- **Interruption = the host interrupt (Stop/Esc), the only mid-run halt.** There is no separate "stop after this ticket". Atomic commits plus ticket-boundary checkpoints make interruption **safe by construction**: the host ends the turn at once and fires no hook, so **nothing is written at interrupt time**: no `PAUSED` checkpoint, no run-record clear, no run-end summary. On disk an Esc is the same as a crash: the in-flight tickets (every slot ticket, with parallel slots) keep their claim, their posted comments and commits, the leftover run record and their `auto: true` work states, which are the signature of both an Esc and a crash. The next `/trailhead:auto` writes the missing `PAUSED` checkpoint at resume (engagement step 2, naming the slot workspace path and branch) and resumes the ticket. A hard stop (closed terminal, crash) is covered by "Records as it goes": nothing that arose is lost, and the in-flight ticket is resumed from its work state (with slots, each slot's own work state survives in its workspace). `PAUSED` checkpoints the run itself decides (the failure budget, a stuck ticket) are still written when they arise.
|
|
93
|
+
- **Resume = the human re-invokes.** `auto` never resumes on its own. The human runs `/trailhead:auto` again; engagement step 2 above picks the interrupted ticket back up via `/trailhead:resume` before scanning, and the run's earlier needs-you tickets are not reconsidered: they stay marked, and the fresh run skips them until the human clears the label (a `work`/`quick` claim clears it). Paused or stuck tickets are not: they stay off the frontier until resumed (owned ones through step 2, the rest by `/trailhead:resume`).
|
|
49
94
|
|
|
50
95
|
## Run-end summary (every terminus)
|
|
51
|
-
Whatever ends the run (frontier drained
|
|
52
|
-
- **why it stopped** (auto-workable frontier exhausted,
|
|
96
|
+
Whatever ends the run by itself (frontier drained), auto emits **one run-end summary**, **in full in chat** for the human, as a summary of records already on the tracker, never the only record (each line links the record it summarises: the ticket, its UAT follow-up, its checkpoint or `NEEDS YOU` comment). It is posted to the tracker too: under a one-map scope, the summary is posted as a comment on the map issue; under everything, each touched map gets a comment with **only its own part** (no other map's part, no whiteboard part); the whiteboard part stays in chat only (it has no issue), and a map the run never touched gets no comment. The summary states:
|
|
97
|
+
- **why it stopped** (auto-workable frontier exhausted; after a host interrupt the summary is not emitted at the interrupt, the resumed run's own summary covers it);
|
|
53
98
|
- **what resolved** (the tickets auto closed this run);
|
|
54
|
-
- **what
|
|
99
|
+
- **what merged**: the tickets merged into the local trunk (under `git: main` with parallel slots), the branches **left unmerged** with their conflicting files (the run's unmerged list), the **merge tickets** the run opened or updated (one per map or whiteboard that had an unmerged branch, each linked; a branch whose `add` failed is listed as no merge ticket written), and that the local trunk is ahead of `origin` by N commits (pushing is the human's);
|
|
100
|
+
- **what remains**: the **needs-you 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. Human-only UAT steps are not a needs-you reason: list instead the **UAT follow-ups opened this run** (what resolved can note them).
|
|
101
|
+
|
|
102
|
+
**The run record at the end.** The summary's skip and unmerged lists come from the run's in-memory skip and unmerged lists as the primary source, united with the record's (`read`), so entries lost to a compaction come back and entries whose write was `skipped` are not lost. Once the summary is posted, run `node <templates>/trailhead-auto-run.js clear --repo <main-checkout>`, then reset the work states (`--auto false`, below). Clearing first means a crash between the two leaves `auto: true` work states and no record (the next run asks again and still resumes the tickets), never a stale record with no crash evidence. A host interrupt clears nothing (it is the same on disk as a crash), so a record left behind is the signature of both an Esc and a crash mid-run, and the next `/trailhead:auto` resumes with its choices. A crash after the summary but before the `clear` leaves a record of a drained run: reusing it only re-drains the same scope (nothing left, the run ends again), announced by the reuse line.
|
|
55
103
|
|
|
56
|
-
Before handing back, if a work state file remains (an interrupted or paused ticket), run `node <templates>/trailhead-work-state.js set --repo <working-root> --auto false`. During the run every work-state `set` adds `--auto true --auto-done <n>`, `n` being the tickets resolved so far in this run (`autoDone` in the file).
|
|
104
|
+
Before handing back, if a work state file remains (an interrupted or paused ticket), run `node <templates>/trailhead-work-state.js set --repo <working-root> --auto false`; with parallel slots, do it on each remaining workspace's work state: `env -u CLAUDE_CODE_SESSION_ID node <templates>/trailhead-work-state.js set --repo <workspace> --auto false`. During the run every work-state `set` adds `--auto true --auto-done <n>`, `n` being the tickets resolved so far in this run (`autoDone` in the file).
|
|
57
105
|
|
|
58
106
|
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).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Executor brief
|
|
2
|
+
|
|
3
|
+
The single protocol text injected at spawn into `trailhead-executor` (build Execute) and `trailhead-fix` (bug Fix): implement the approved PLAN (or the confirmed fix) with atomic commits and report back. You cannot interact with the user; anything that needs a decision goes into your report, never into a guess.
|
|
4
|
+
|
|
5
|
+
## Inputs
|
|
6
|
+
The orchestrator passes: the ticket number `<n>`, the PLAN (build) or the confirmed `DIAGNOSIS` (bug), which is your spec, the base SHA (your baseline, see the Baseline rule), the `config.tdd` value, the approved mockup link if any, the accepted mockup deviations (elements the user agreed to build differently), and, on a resumed spawn, the packages the user approved. Work only from these; the repo's `trailhead:conventions` apply.
|
|
7
|
+
|
|
8
|
+
**Resumed spawn.** When commits carrying `Refs: #<n>` already exist since the base SHA, continue from them: never redo or revert work already committed.
|
|
9
|
+
|
|
10
|
+
**Stop and report** means: leave the blocked piece undone, finish the work it does not affect, then return your report.
|
|
11
|
+
|
|
12
|
+
## Implement
|
|
13
|
+
**Atomic commits.** Conventional commits, each ending with the ticket's `Refs: #<n>` trailer (it is also what scopes Verify's review and goal check). One commit = one verifiable step. You only commit: the branch is already set up for you.
|
|
14
|
+
|
|
15
|
+
**TDD per the `config.tdd` value passed.** `seams`: test-first at the seams the PLAN names; `on`: test-first throughout; `off`: no test-first requirement. For a fix, where sensible, a test that fails before and passes after.
|
|
16
|
+
|
|
17
|
+
## New dependencies
|
|
18
|
+
**Vet before installing any new dependency** (slopsquatting barrier): name and typosquat similarity to popular packages, age / first-seen date, downloads (counts), repository metadata present and matching. On a host with a hook bus the `trailhead-install-guard` `PreToolUse` hook blocks a named public-registry install (npm / PyPI / crates.io / Go / RubyGems) until it is vetted.
|
|
19
|
+
- **Clean result, or the orchestrator passed that exact package as user-approved:** install by re-running the exact install command prefixed with `TRAILHEAD_VERIFIED_INSTALL=1`.
|
|
20
|
+
- **Suspicious result on a package the orchestrator did not pass as user-approved:** stop and report. Never install it, never set the sentinel for it, and do not substitute another package on your own judgement.
|
|
21
|
+
|
|
22
|
+
## Approved mockup
|
|
23
|
+
When an approved mockup is passed, reproduce it with 100% fidelity (layout, spacing, sizing, colour, type, copy, and every state), never a loose approximation "in the spirit of" it; an accepted mockup deviation passed to you is the only permitted difference. If an element is infeasible, stop and report it: never silently deviate. Before finishing, enumerate the approved mockup's elements and states, confirm each is reproduced, and return that enumerated fidelity list in your report.
|
|
24
|
+
|
|
25
|
+
## Surfaced scope
|
|
26
|
+
New scope that surfaces while you work (a bug found, a missing capability, a refactor worth doing) is reported, not worked: do not expand the ticket in flight.
|
|
27
|
+
|
|
28
|
+
## Report
|
|
29
|
+
Return the report in the shape of the implementing `Return contract` in your agent definition: `status: done | partial | blocked` on line 1, then `commits:`, `tests:`, `deviations:`, `blockers:`, and `fidelity:` and `surfaced scope:` where they apply. This brief names what must be in it: each flagged package with its exact install command and the evidence that flagged it, each infeasible mockup element and each foreign change in the way, as `blockers:` entries (status `partial` or `blocked`); the enumerated fidelity list, element/state -> reproduced or infeasible with why, as `fidelity:`; and any surfaced scope, as `surfaced scope:`.
|
|
@@ -9,18 +9,18 @@ Technique names below (**Grilling**, **Domain vocabulary**, **Prototype**, **Res
|
|
|
9
9
|
**Work state (every type).** Keep `.trailhead/work-state.json` current with the `set --step` call in the **Work state file** paragraph of `../../_shared/teamwork.md`: `build` Discuss/Plan/Execute/Verify/Resolve set `discuss`/`plan`/`execute`/`verify`/`resolve`; `bug` Repro and Diagnose set `discuss`, Fix `execute`, Verify `verify`, Resolve `resolve`; `decision`, `prototype` and `task` stay `discuss` until `resolve`. A loop back from Verify to Execute (or Fix) sets `execute` again, and a UAT follow-up (`UAT of:`) sets `verify`. `research` tickets never write it: they run AFK and possibly in parallel, and the single file tracks only the one ticket in play.
|
|
10
10
|
|
|
11
11
|
## UAT follow-ups (any type)
|
|
12
|
-
A ticket whose body carries `UAT of: <parent
|
|
12
|
+
A ticket whose body carries a `UAT of: [<parent name>](<parent url>)` line is a UAT follow-up (it also carries the `trailhead:uat` label, a marker that never changes the frontier; the body line stays the authority, since a ticket opened before the label existed is tagged by the backfill), **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 stale-base preflight (`trailhead-base-preflight.js`, **Stale-base preflight** in `../../_shared/teamwork.md`), then the UAT setup (`trailhead-uat-setup.js`, **UAT setup comes next** in the technique), and then 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 or an explicit waiver (the user's skip at the drive-mode ask, recorded in the `VERIFY` comment as unconfirmed; the follow-up closes as waived), with a resolution comment linking back to the parent and stating the waiver when there was one.
|
|
13
13
|
|
|
14
14
|
## `decision`: HITL, *default for decision tickets*
|
|
15
15
|
Close a choice, but widen before you narrow:
|
|
16
16
|
|
|
17
|
-
1. **Diverge (only if the option space isn't already clear).** Mapping the frontier usually framed the options already;
|
|
18
|
-
2. **Converge.** Run the **Grilling** + **Domain vocabulary** techniques over the options to pick one. Record the decision and the why. The delegate option (agent picks) is never offered here, the choice stays the human's, but **defer is offered** (in prose, per the recipe in `../../_shared/choices.md`) when the choice is genuinely blocked on a prerequisite.
|
|
17
|
+
1. **Diverge (only if the option space isn't already clear).** Mapping the frontier usually framed the options already; when it did, check that the framed set is still current and complete before grilling: re-read it against the ticket and against what has landed since (the map's `Decisions so far`, tickets closed after it was framed), add any missing candidate (the status quo, "leave it as it is", included when it is a real option), post the additions as an `OPTIONS` comment, then grill. When it didn't (the ticket names a question but the candidate answers aren't laid out) open the space *first*: brainstorm the plausible options, name each concretely, and post them as an `OPTIONS` comment. Divergence has no judgement yet: surface possibilities, don't rank them. If a missing **fact** is what's blocking clarity, spin a `research` ticket and block on it; if the question is "how could it look/behave", spin a `prototype`. Don't grill a space you haven't opened.
|
|
18
|
+
2. **Converge.** Run the **Grilling** + **Domain vocabulary** techniques over the options to pick one. Before the final confirmation, run the **Holistic review before the final confirmation** (Grilling technique). Record the decision and the why. The delegate option (agent picks) is never offered here, the choice stays the human's, but **defer is offered** (in prose, per the recipe in `../../_shared/choices.md`) when the choice is genuinely blocked on a prerequisite.
|
|
19
19
|
|
|
20
20
|
The two phases are distinct on purpose: divergence generates, grilling chooses. Never let the agent invent the options *and* pick for the human: the choice stays theirs.
|
|
21
21
|
|
|
22
22
|
## `research`: AFK
|
|
23
|
-
Reading docs, third-party APIs, or local resources to surface a fact a decision waits on. Resolved by the **Research** technique. The only type that runs in parallel and more than one per session.
|
|
23
|
+
Reading docs, third-party APIs, or local resources to surface a fact a decision waits on. Resolved by the **Research** technique, whose `trailhead-research` report the orchestrator checks and posts as a `RESEARCH` comment (see the technique). The only type that runs in parallel and more than one per session.
|
|
24
24
|
|
|
25
25
|
## `prototype`: HITL
|
|
26
26
|
Raise the fidelity of the discussion with a rough, concrete artifact to react to: the **Prototype** technique. Link the prototype as an asset.
|
|
@@ -30,11 +30,19 @@ The ticket that *builds*. Lean cycle, all in **ticket comments**:
|
|
|
30
30
|
|
|
31
31
|
**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.
|
|
32
32
|
|
|
33
|
-
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`.
|
|
34
|
-
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.
|
|
35
|
-
3. **Execute**:
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
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 (ending with the **Holistic review before the final confirmation** of the Grilling technique) and post a `DISCUSS` comment with the closed choices, recorded after the review. 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`.
|
|
34
|
+
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). **Report (orchestrator).** Run the report-status check on the planner's report (`--technique plan`, the *Malformed review report* protocol in `../../_shared/techniques.md`) and branch on the checker's `verdict`: `status: ready` -> post the checker's `body` verbatim as the `PLAN` comment; `status: needs_input` -> ask the user the `questions:` (the checker's `fields`), then re-spawn the planner with the answers; never answer them yourself or guess. 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. **Coverage check before `ready`.** Before the PLAN is returned (the short inline plan too), map every `Done when` criterion of the ticket (its `Question` when it has none) to the step(s) and the verification criterion that deliver it, and keep that mapping in the PLAN's verification criteria: a criterion no step delivers is a gap, so add the step, or return `needs_input` when closing it needs a decision. Where the PLAN picks between approaches, the pick is stress-tested per **Stress-test the pick before offering it** in `../../_shared/choices.md`, its `Weak point:` line kept in the PLAN. **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.
|
|
35
|
+
3. **Execute**: the orchestrator runs the HITL and tracker moves around an **executor subagent on `config.models.execute`**; the implementing protocol itself lives in `executor-brief.md` (this directory), the only protocol text the agent receives from this step. The agent cannot ask the user and never writes to the tracker, pushes or creates a branch.
|
|
36
|
+
- **Prototype FIRST (orchestrator).** **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 (its clickable link always in the same message as the approval question, per the Prototype technique; the guard also accepts a link inside the approval question itself, the prose stays preferred), routed by `config.design` (disk / artifacts / 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 `artifacts` (or the legacy `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 (full statement in the **Prototype** technique, `../../_shared/techniques/prototype.md`).
|
|
37
|
+
- **Branch and worktree (orchestrator).** Following the repo's `trailhead:conventions` `git:`, straight to `main` by default, or a feature branch + PR when `git: pr` (the orchestrator creates the branch before the spawn and opens the PR; the executor only commits, never pushes, switches branch or writes to the tracker). **Under `isolation: worktree`, the work happens in this ticket's `git worktree`** (`trailhead/t<n>` branch), integrating to the trunk at Resolve (see `../../_shared/teamwork.md`).
|
|
38
|
+
- **Dispatch.** Spawn the **`trailhead-executor`** agent (full tools that can Edit/Write/commit), passing `config.models.execute` as the model override and `executor-brief.md` as the only protocol text, plus the runtime inputs: the ticket number, the PLAN, the base SHA, the `config.tdd` value, the approved mockup link if any, the accepted mockup deviations, and, for a resumed spawn, the packages the user approved. Inline when the model is unset or equals the session model: the session then does both parts. Inline, the session reads `executor-brief.md` and applies it, asking the user directly at the points the brief says to report. The agent implements with **atomic commits** (conventional commits, each with a `Refs: #<n>` trailer for this ticket, see **Principles** in `../../_shared/principles.md`; the trailer is also what scopes Verify's review and goal check, per **Ticket diff** in `../../_shared/techniques/code-review.md`; one commit = one verifiable step); the commits land on `main`, visible.
|
|
39
|
+
- **Fidelity (orchestrator).** The approved mockup is reproduced with **100% fidelity** (layout, spacing, sizing, colour, type, copy, and every state), never a loose approximation. **Make fidelity verifiable, not a hope:** the executor returns the enumerated fidelity list in its report and the orchestrator posts it 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`).
|
|
40
|
+
- **Infeasible element (orchestrator).** When the report marks an element infeasible, raise it with the user and settle it: revise and re-approve the mockup, or the user accepts a named deviation. Then re-spawn the executor with the settled mockup and the accepted deviations; never let it deviate silently.
|
|
41
|
+
- **Flagged package (orchestrator).** The **verify-before-install gate** (slopsquatting barrier) runs in the executor (legitimacy, typosquat similarity, age, downloads, repository metadata). On a flagged package in the report, block-and-ask: stop and ask the user, never installing on your own judgement. Only on an explicit yes, re-spawn the executor with that package passed as user-approved (it then re-runs the exact install command prefixed with `TRAILHEAD_VERIFIED_INSTALL=1`); on a no it is not installed and the plan is revisited with the user. On a host with a hook bus the `trailhead-install-guard` `PreToolUse` hook enforces the gate, blocking a named public-registry install (npm / PyPI / crates.io / Go / RubyGems) until it is vetted.
|
|
42
|
+
- **Surfaced scope (orchestrator).** New scope surfacing mid-execute, reported by the executor in `surfaced scope:`, is captured/split, not worked here.
|
|
43
|
+
- **Report (orchestrator).** Run the report-status check on the agent's report (`--technique executor`, the *Malformed review report* protocol in `../../_shared/techniques.md`) and branch on the checker's `verdict`, reading its `fields`: never reconstruct a result from the prose. `status: done` -> Verify. `status: partial` or `blocked` -> act on each `blockers:` entry with the moves above (flagged package, infeasible element; a foreign change in the way is raised with the user), then re-spawn. Post `fidelity:` as the fidelity comment and route `surfaced scope:` as below. Check `commits:` against the repo: it must equal the `Refs: #<n>` commit set since the base SHA, in both directions, and every SHA must resolve (`git cat-file -e <sha>^{commit}`); a mismatch is surfaced to the user, never patched over.
|
|
44
|
+
4. **Verify**: a `VERIFY` comment: **start with the Stale-base preflight** (`trailhead-base-preflight.js --sync`, **Stale-base preflight** in `../../_shared/teamwork.md`): preflight, then sync, then re-run the tests, all before anything is reviewed (a rebase rewrites SHAs the review ledger cites, and the Code review and goal check must see the synced tree); then run the tests / the plan's criterion; then the **Code review** technique, scoped to the ticket's own commits per its **Ticket diff** rule (`../../_shared/techniques/code-review.md`: the `Refs: #<n>` set, read per commit, recomputed each round, an empty set surfaced as an error, never a `<base>...HEAD` range) (its subagent reviews *adversarially*: it doesn't defer to a `CLAUDE.md` convention or a "settled decision" memory), and carries the review's counts line and findings ledger, per code-review.md, **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`); run the report-status check before branching on status (each report, from the subagent or inline, goes through the *Malformed review report* protocol in `../../_shared/techniques.md`, which also fixes the layout of the round's single `VERIFY` comment); **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, on the same **Ticket diff** set), **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 UAT setup (`trailhead-uat-setup.js`, after the preflight) and then 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 (or skip (waive acceptance), recorded as waived, if the user so chooses): 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 lighter inline review still uses the same finding IDs, counts line, and ledger, and the same no-`open` gate, per code-review.md; 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.
|
|
45
|
+
5. **Resolve**: a resolution comment with what was done → `gh issue close` → update `Decisions so far` on the map (default path: `node <templates>/trailhead-issue-body.js append --issue <map> --section "Decisions so far" --line "<pointer>"`, a body read-modify-write that obeys the **Fail-safe body rewrites** rule in `../../_shared/substrate.md` for you; `<templates>` is the same three-dir lookup as for `trailhead-work-state.js` (**Work state file** in `../../_shared/teamwork.md`); when the helper is missing, do the read-modify-write by hand per that rule). **Disposition gate (before the close)**: the latest `VERIFY` comment's findings ledger covers every finding the counts line counts and none of them is `open`; the resolution comment links that ledger rather than duplicating it; otherwise do not `gh issue close` (per code-review.md's Disposition gate). **Stale-base preflight (before integrating).** Run the preflight per **Stale-base preflight** in `../../_shared/teamwork.md` (`git: main`: right before the trunk push; `git: pr` without isolation: on the ticket branch, immediately before the push and PR; `worktree`/`clone`: on the main checkout, then on the ticket branch) and integrate only on `current`/`synced` with the tests green and `checksOk`. **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 (the stale-base preflight first, then the UAT setup, then the drive-mode ask, `../../_shared/techniques/acceptance-testing.md`), or (b) open a UAT follow-up ticket to run it later. (Under an autonomous run the guard takes option (b) itself for the human-only steps, reusing an open follow-up instead of opening a second one: see `auto.md`.) On (a), run the guided walkthrough now and resolve on a confirmed pass, or on an explicit waiver if the user skips at the ask (recorded as unconfirmed; the resolution comment states it) (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, or was **explicitly waived** by the user at the drive-mode ask (a waiver is not a deferral: nothing is owed, so nothing is opened; the resolution comment states the waiver), 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`; look it up per its singleton fail-closed rule) in place (a line or two, not a re-map). When a structural event is due (a whiteboard ticket resolved, a map exhausted), refresh the dashboard with `node <templates>/trailhead-dashboard.js` (one call, whole body from the live tracker; by hand per `../../_shared/substrate-commands.md` when the helper is missing). Then close the session with the **Session handoff** ritual (`../../_shared/session-handoff.md`): one `node <templates>/trailhead-work-state.js clear --repo <working-root>` removes the work state and the session-ticket marker, then `/clear` + next command.
|
|
38
46
|
|
|
39
47
|
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.
|
|
40
48
|
|
|
@@ -50,10 +58,18 @@ Then run the cycle, all in ticket comments:
|
|
|
50
58
|
**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.
|
|
51
59
|
|
|
52
60
|
1. **Repro**: a `REPRO` comment: how to reproduce, expected vs observed behaviour.
|
|
53
|
-
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`).
|
|
54
|
-
3. **Fix**:
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
2. **Diagnose**: run the **Debug** technique. Dispatch `trailhead-debug` and run the report-status check on its report (`--technique debug`, the *Malformed review report* protocol in `../../_shared/techniques.md`); branch on the checker's `verdict`, reading its `fields`: `status: found` -> its `answer:` (the cause) and `evidence:` (the falsification test and its result) feed the `DIAGNOSIS`; `status: inconclusive` -> the ticket stays in diagnosis, next hypotheses, never on to Fix; `status: needs_input` -> ask the user the `open questions:`, then re-spawn with the answers. 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`).
|
|
62
|
+
3. **Fix**: the orchestrator runs the HITL and tracker moves around an **executor subagent on `config.models.execute`**; the implementing protocol itself lives in `executor-brief.md` (this directory), the only protocol text the agent receives from this step. The agent cannot ask the user and never writes to the tracker, pushes or creates a branch.
|
|
63
|
+
- **Dispatch.** Spawn the **`trailhead-fix`** agent (full tools), passing `config.models.execute` as the model override and `executor-brief.md` as the only protocol text, plus the runtime inputs: the ticket number, the base SHA, the confirmed `DIAGNOSIS`, the `config.tdd` value, the approved mockup link if any, the accepted mockup deviations, and, for a resumed spawn, the packages the user approved. Inline when unset/same as session: the session then does both parts. Inline, the session reads `executor-brief.md` and applies it, asking the user directly at the points the brief says to report. The agent implements the correction with **atomic commits** (each with a `Refs: #<n>` trailer for this ticket, see **Principles** in `../../_shared/principles.md`; the trailer is also what scopes Verify's review and goal check, per **Ticket diff** in `../../_shared/techniques/code-review.md`), and where sensible a test that fails before and passes after (**TDD**).
|
|
64
|
+
- **Branch and worktree (orchestrator).** Per the repo's `trailhead:conventions` `git:`, `main` by default, else a branch + PR: the orchestrator creates the branch before the spawn and opens the PR under `git: pr`; the agent only commits. Under `isolation: worktree`, the work happens in this ticket's `git worktree`.
|
|
65
|
+
- **UI fix: Prototype and approval (orchestrator).** Where the fix changes a screen (a user-facing UI fix), run the Prototype technique FIRST, before any real UI code: surface a mockup, routed by `config.design` (never shortcut a hosted mode to a local disk mockup) and gated by `config.design.approval`, per the technique (`../../_shared/techniques/prototype.md`). Once approved, the executor reproduces it with 100% fidelity.
|
|
66
|
+
- **Fidelity (orchestrator).** The executor returns the enumerated fidelity list in its report and the orchestrator posts it as a ticket comment, so Verify (Acceptance testing and Goal-backward verification) checks against a durable list.
|
|
67
|
+
- **Infeasible element (orchestrator).** When the report marks an element infeasible, raise it with the user and settle it: revise and re-approve the mockup, or the user accepts a named deviation; then re-spawn the executor with the settled mockup and the accepted deviations.
|
|
68
|
+
- **Flagged package (orchestrator).** The **verify-before-install gate** runs in the executor (typosquat similarity, age, downloads, repository metadata). On a flagged package in the report, block-and-ask: stop and ask the user before anything is installed. Only on an explicit yes, re-spawn the executor with that package passed as user-approved; on a no it is not installed and the plan is revisited with the user.
|
|
69
|
+
- **Surfaced scope (orchestrator).** New scope the executor reports in `surfaced scope:` is captured/split, never worked in this ticket.
|
|
70
|
+
- **Report (orchestrator).** Run the report-status check on the agent's report (`--technique fix`, the *Malformed review report* protocol in `../../_shared/techniques.md`) and branch on the checker's `verdict`, reading its `fields`: never reconstruct a result from the prose. `status: done` -> Verify. `status: partial` or `blocked` -> act on each `blockers:` entry with the moves above (flagged package, infeasible element; a foreign change in the way is raised with the user), then re-spawn. Post `fidelity:` as the fidelity comment and route `surfaced scope:` as below. Check `commits:` against the repo: it must equal the `Refs: #<n>` commit set since the base SHA, in both directions, and every SHA must resolve (`git cat-file -e <sha>^{commit}`); a mismatch is surfaced to the user, never patched over.
|
|
71
|
+
4. **Verify**: a `VERIFY` comment: **start with the Stale-base preflight** (`trailhead-base-preflight.js --sync`, **Stale-base preflight** in `../../_shared/teamwork.md`): preflight, then sync, then re-run the tests, all before anything is reviewed (a rebase rewrites SHAs the review ledger cites, and the Code review and goal check must see the synced tree); then the repro now passes, no regressions; **Code review** on the fix, scoped to the ticket's own commits per its **Ticket diff** rule (`../../_shared/techniques/code-review.md`: the `Refs: #<n>` set, read per commit, recomputed each round, an empty set surfaced as an error, never a `<base>...HEAD` range), and carries the review's counts line and findings ledger, per code-review.md, **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`); run the report-status check before branching on status (each report, from the subagent or inline, goes through the *Malformed review report* protocol in `../../_shared/techniques.md`, which also fixes the layout of the round's single `VERIFY` comment); **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, on the same **Ticket diff** set), 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 UAT setup (`trailhead-uat-setup.js`, after the preflight) and then 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 (or skip (waive acceptance), recorded as waived, if the user so chooses): 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.
|
|
72
|
+
5. **Resolve**: a resolution comment (cause + fix) → `gh issue close` → update `Decisions so far` (default path: `node <templates>/trailhead-issue-body.js append --issue <map> --section "Decisions so far" --line "<pointer>"`, same helper and fallback as the `build` Resolve step; a body read-modify-write that obeys the **Fail-safe body rewrites** rule in `../../_shared/substrate.md`). **Disposition gate (before the close)**: the latest `VERIFY` comment's findings ledger covers every finding the counts line counts and none of them is `open`; the resolution comment links that ledger rather than duplicating it; otherwise do not `gh issue close` (per code-review.md's Disposition gate). **Stale-base preflight (before integrating).** Run the preflight per **Stale-base preflight** in `../../_shared/teamwork.md` (`git: main`: right before the trunk push; `git: pr` without isolation: on the ticket branch, immediately before the push and PR; `worktree`/`clone`: on the main checkout, then on the ticket branch) and integrate only on `current`/`synced` with the tests green and `checksOk`. **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 (the stale-base preflight first, then the UAT setup, then the drive-mode ask, `../../_shared/techniques/acceptance-testing.md`), or (b) open a UAT follow-up ticket to run it later. (Under an autonomous run the guard takes option (b) itself for the human-only steps, reusing an open follow-up instead of opening a second one: see `auto.md`.) On (a), run the guided walkthrough now and resolve on a confirmed pass, or on an explicit waiver if the user skips at the ask (recorded as unconfirmed; the resolution comment states it) (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, or was **explicitly waived** by the user at the drive-mode ask (a waiver is not a deferral: nothing is owed, so nothing is opened; the resolution comment states the waiver), 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`, looked up per its singleton fail-closed rule; also a body read-modify-write, same **Fail-safe body rewrites** rule) in place (a line or two; the same `trailhead-issue-body.js` helper does it with `--section "<facet>"`). When a structural event is due, refresh the dashboard with `node <templates>/trailhead-dashboard.js`. Then close the session with the **Session handoff** ritual (`../../_shared/session-handoff.md`): one `node <templates>/trailhead-work-state.js clear --repo <working-root>` removes the work state and the session-ticket marker, then `/clear` + next command.
|
|
57
73
|
|
|
58
74
|
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.
|
|
59
75
|
|
|
@@ -11,8 +11,12 @@
|
|
|
11
11
|
# trailhead al work-start, porta anche il titolo) -> numero dal branch
|
|
12
12
|
# `trailhead/t<N>` -> titolo via `gh` in cache/background (solo se origin GitHub).
|
|
13
13
|
#
|
|
14
|
-
# Contesto: barra `[BARRA] Nk/Mk (P%)`, dall'ultimo usage del transcript. Tetto
|
|
15
|
-
# $CONTEXT_LIMIT -> .
|
|
14
|
+
# Contesto: barra `[BARRA] Nk/Mk (P%)`, dall'ultimo usage del transcript. Tetto
|
|
15
|
+
# (finestra del modello): $CONTEXT_LIMIT -> .context_window.context_window_size ->
|
|
16
|
+
# tabella modello -> auto-bump 1M. La % e' contro la soglia di auto-compact di
|
|
17
|
+
# Claude Code (finestra di compattazione - 20000 - 13000), non contro un 80% fisso:
|
|
18
|
+
# vedi il commento sopra il blocco CTX per le regole. CONTEXT_USABLE_RATIO resta
|
|
19
|
+
# solo come override manuale (0 < ratio <= 1: denominatore = tetto * ratio).
|
|
16
20
|
#
|
|
17
21
|
# Usage: % REALE del piano via l'API OAuth di Anthropic (come ccstatusline):
|
|
18
22
|
# GET https://api.anthropic.com/api/oauth/usage con il Bearer preso da
|
|
@@ -139,12 +143,24 @@ fi
|
|
|
139
143
|
# Fonte primaria (come ccstatusline): .context_window del JSON di Claude Code
|
|
140
144
|
# (context_window_size = tetto reale, current_usage = token). Fallback: ultimo
|
|
141
145
|
# usage del transcript + tabella modello. $CONTEXT_LIMIT forza il tetto.
|
|
142
|
-
# Denominatore =
|
|
143
|
-
#
|
|
146
|
+
# Denominatore = soglia di auto-compact di Claude Code:
|
|
147
|
+
# finestra di compattazione = min(tetto modello, primo valido tra
|
|
148
|
+
# $CLAUDE_CODE_AUTO_COMPACT_WINDOW, autoCompactWindow dei settings, tetto);
|
|
149
|
+
# nei settings (utente < progetto < progetto.local, vince il piu' specifico)
|
|
150
|
+
# modelSettings.<model id senza [..]>.autoCompactWindow batte autoCompactWindow;
|
|
151
|
+
# soglia = finestra - 20000 (output riservato, massimo) - 13000 (buffer), min 1;
|
|
152
|
+
# $CLAUDE_AUTOCOMPACT_PCT_OVERRIDE (0 < pct <= 100) puo' solo abbassarla:
|
|
153
|
+
# min(soglia, floor(finestra * pct / 100)).
|
|
154
|
+
# Con DISABLE_AUTO_COMPACT / DISABLE_COMPACT truthy il denominatore e' il tetto pieno.
|
|
155
|
+
# CONTEXT_USABLE_RATIO (0 < r <= 1), se impostato, e' un override manuale:
|
|
156
|
+
# denominatore = tetto * r, senza calcolo di compattazione.
|
|
144
157
|
CTX="$(
|
|
145
158
|
{ [ -n "$TRANSCRIPT" ] && [ -f "$TRANSCRIPT" ] && tail -n 300 "$TRANSCRIPT" 2>/dev/null | tac | grep -m1 'cache_read_input_tokens'; } | \
|
|
146
159
|
CTX_MODEL_ID="$(j '.model.id')" \
|
|
147
160
|
CTX_CW_SIZE="$(j '.context_window.context_window_size')" \
|
|
161
|
+
CTX_PROJECT_DIR="$(j '.workspace.project_dir')" \
|
|
162
|
+
CTX_CURRENT_DIR="$(j '.workspace.current_dir')" \
|
|
163
|
+
CTX_CWD="$(j '.cwd')" \
|
|
148
164
|
CTX_USAGE="$(printf '%s' "$input" | jq -c '.context_window.current_usage // empty' 2>/dev/null)" \
|
|
149
165
|
python3 -c '
|
|
150
166
|
import sys, os, json, re
|
|
@@ -192,16 +208,65 @@ if not limit:
|
|
|
192
208
|
if tok > limit and limit <= 200000: # auto-bump 1M solo nel fallback
|
|
193
209
|
limit = 1000000
|
|
194
210
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
211
|
+
def pos_num(x):
|
|
212
|
+
# numero finito > 0, altrimenti None (bool e stringhe non numeriche scartati)
|
|
213
|
+
if isinstance(x, bool) or x is None: return None
|
|
214
|
+
try: v = float(x)
|
|
215
|
+
except Exception: return None
|
|
216
|
+
return v if v == v and v not in (float("inf"),) and v > 0 else None
|
|
217
|
+
|
|
218
|
+
def truthy(name):
|
|
219
|
+
return (os.environ.get(name) or "").strip().lower() in ("1", "true", "yes", "on")
|
|
220
|
+
|
|
221
|
+
def load_settings():
|
|
222
|
+
# utente < progetto < progetto.local; file illeggibili/invalidi saltati
|
|
223
|
+
cfg = os.environ.get("CLAUDE_CONFIG_DIR") or os.path.join(os.path.expanduser("~"), ".claude")
|
|
224
|
+
proj = os.environ.get("CTX_PROJECT_DIR") or os.environ.get("CTX_CURRENT_DIR") or os.environ.get("CTX_CWD")
|
|
225
|
+
paths = [os.path.join(cfg, "settings.json")]
|
|
226
|
+
if proj:
|
|
227
|
+
paths += [os.path.join(proj, ".claude", "settings.json"),
|
|
228
|
+
os.path.join(proj, ".claude", "settings.local.json")]
|
|
229
|
+
out = []
|
|
230
|
+
for p in paths:
|
|
231
|
+
try:
|
|
232
|
+
d = json.load(open(p))
|
|
233
|
+
if isinstance(d, dict): out.append(d)
|
|
234
|
+
except Exception:
|
|
235
|
+
pass
|
|
236
|
+
return out # dal meno al piu prioritario
|
|
237
|
+
|
|
238
|
+
def setting_window(model):
|
|
239
|
+
files = load_settings()[::-1] # piu prioritario per primo
|
|
240
|
+
for d in files: # per-modello batte top-level: prima cerco il per-modello
|
|
241
|
+
ms = d.get("modelSettings")
|
|
242
|
+
v = pos_num((ms.get(model) or {}).get("autoCompactWindow")) if isinstance(ms, dict) and isinstance(ms.get(model), dict) else None
|
|
243
|
+
if v: return v
|
|
244
|
+
for d in files:
|
|
245
|
+
v = pos_num(d.get("autoCompactWindow"))
|
|
246
|
+
if v: return v
|
|
247
|
+
return None
|
|
248
|
+
|
|
249
|
+
def threshold_for(limit):
|
|
250
|
+
model = re.sub(r"\[[^\]]*\]\s*$", "", os.environ.get("CTX_MODEL_ID") or "")
|
|
251
|
+
w = pos_num(os.environ.get("CLAUDE_CODE_AUTO_COMPACT_WINDOW")) or setting_window(model) or limit
|
|
252
|
+
w = int(min(w, limit))
|
|
253
|
+
thr = max(1, w - 20000 - 13000)
|
|
254
|
+
p = pos_num(os.environ.get("CLAUDE_AUTOCOMPACT_PCT_OVERRIDE"))
|
|
255
|
+
if p is not None and p <= 100:
|
|
256
|
+
thr = max(1, min(thr, int(w * p // 100)))
|
|
257
|
+
return thr
|
|
258
|
+
|
|
259
|
+
ratio = pos_num(os.environ.get("CONTEXT_USABLE_RATIO"))
|
|
260
|
+
if ratio is not None and ratio <= 1:
|
|
261
|
+
denom = round(limit * ratio) # override manuale
|
|
262
|
+
elif truthy("DISABLE_AUTO_COMPACT") or truthy("DISABLE_COMPACT"):
|
|
263
|
+
denom = limit # niente compattazione: tetto pieno
|
|
264
|
+
else:
|
|
265
|
+
denom = threshold_for(limit)
|
|
266
|
+
pct = min(100, round(tok*100/denom)) if denom else 0
|
|
202
267
|
n = 10; filled = round(pct*n/100)
|
|
203
268
|
bar = "█"*filled + "░"*(n-filled)
|
|
204
|
-
# numeri REALI: token effettivi / tetto pieno; la percentuale
|
|
269
|
+
# numeri REALI: token effettivi / tetto pieno; la percentuale e contro la soglia di compattazione
|
|
205
270
|
print(f"[{bar}] {round(tok/1000)}k/{round(limit/1000)}k ({pct}%)")
|
|
206
271
|
' 2>/dev/null
|
|
207
272
|
)"
|