pi-gauntlet 5.14.0 → 5.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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## v5.16.0 - 2026-09-20
4
+
5
+ - Skills never name a provider or model: `subagent-driven-development` drops its `## Model Selection` tier table for a one-place `## Model` rule (a dispatch's `model:` is omitted, or carries a `gauntlet_setting` value, a user-named model, or the main loop's own string); `doc/configuration.md` gains `### Dispatch model precedence`; `scripts/model-literal-lint.mjs` bans provider/model literals in `skills/`, `agents/`, `extensions/` from `scripts/ci.mjs`. `writing-skills` widens its trigger to personas and prompt templates and adds `## Authoring rules` (imperative voice, low conditionality, minimal diff, oversized-skill extraction); AGENTS core `v6` makes it binding for every skill, persona, and prompt edit. `shape-ticket` and `conformance-check.md` name the main-loop-string provenance explicitly. (#42)
6
+
7
+ ## v5.15.0 - 2026-09-20
8
+
9
+ - New human-only `/skill:gauntlet-handoff`: invokes pi-cohort's `handoff` skill (`--out <path>` or `--key <name>`; default key = the run worktree's branch, `/` flattened to `-`, file `<tmpdir>/pi-handoff/<key>.md`) and appends the gauntlet `## Process state` section in the layout fixed by `skills/gauntlet-resume/reference/brief-contract.md` (`## Producers`); stops instead of writing when the skill is absent or the installed cohort still writes that section itself. `/skill:gauntlet-resume` with no arguments lists the briefs under `<tmpdir>/pi-handoff/` for a human pick, and a token that looks like a path is always a brief file (missing -> stop, never a scan). `scripts/ci.mjs` gains a drift lint (`scripts/brief-contract-lint.mjs`): the three process-state grammar lines may appear only in the contract file, both skills must cite it, and gauntlet-handoff may not carry cohort's core headings. Requires pi-cohort >= the release shipping [pi-cohort #18](https://github.com/jjuraszek/pi-cohort/issues/18) (exact version filled at release). (#40)
10
+
3
11
  ## v5.14.0 - 2026-09-20
4
12
 
5
13
  - Specs carry the ticket's acceptance criteria verbatim in a required `## Acceptance criteria` section, one disposition per row (`in-scope`, `deviates: <why>`, `deferred: <where>`, `venue: <env> - <observation>`; `none - <reason>` when there is no ticket or no ACs). `brainstorming` extracts heading-scoped rows and lints the section's presence, `gatherer` quotes the raw rows, `spec-council-member` flags dropped/reworded rows and invalid deferrals, `conformance-reviewer` and `conformance-check` read the section as origin (`in-scope`/`venue:` rows are requirements, `venue:` observations never block), `writing-plans` tables only `in-scope`/`venue:` rows, and `finishing-a-development-branch` lists `venue:`/`deferred:` rows in the PR body. `scripts/ci.mjs` pins the new tokens. (#41)
package/README.md CHANGED
@@ -69,7 +69,7 @@ Everything between gate 1 and gate 2 - task breakdown, implementation, both revi
69
69
 
70
70
  pi-gauntlet ships three kinds of pieces, layered on top of pi-cohort's dispatch:
71
71
 
72
- - **19 skills** - the workflow logic. Thirteen activate automatically when pi sees the matching kind of task, and each one gates the next: `brainstorming`, `writing-plans`, `roasting-the-spec`, `test-driven-development`, `subagent-driven-development`, `dispatching-parallel-agents`, `verification-before-completion`, `requesting-code-review`, `receiving-code-review`, `using-git-worktrees`, `finishing-a-development-branch`, `writing-skills`, `linear` (reads/searches/comments on/manages Linear tickets via the `linearis` CLI; owns all linearis mechanics and the `## Issue tracker` overrides schema; tracker-facing skills route to it). Six more are explicit-invocation-only (`disable-model-invocation: true`): `shape-ticket` creates or repairs one tracker issue per run against a Context/Problem/Idea/Acceptance-Criteria template, gated by an AC integrity check, a cheap council roast, and a single human-confirmed write - run it with `/skill:shape-ticket`. `gatekeep-pr` is consent-gated pre-merge verification of a PR against its issue - read-only gathering, verification evidence resolved CI-first (green checks on the exact assessed head count as evidence; the project's verification command runs only as fallback), a rubric-based review, then a deterministic authorship-aware menu with stable finding IDs (P#/L#/C#/F#) and numbered pre-composed courses (fixes execute as a single parallel-safe wave: one gate run, one re-review, one push); nothing mutates (fixes, pushes, reviews, merges) until you pick a row - run it with `/skill:gatekeep-pr <pr>`. `check-delivery` is a post-merge detective control: proves an issue actually shipped (default-branch landing, delivery target, per-AC evidence) before its tracker status advances; it never writes a terminal status - run it with `/skill:check-delivery <ref>`. `chase-bug` is human-only bug triage: read-only root-cause discovery to an evidenced verdict menu (real bug -> ticket/brainstorm/hotfix/respond; five negative verdicts), then a gated response to the reporter for addressable origins (GitHub issue / tracker ticket) and a rendered verdict summary otherwise - it never fixes during triage; the hotfix row hands off to `skills/chase-bug/hotfix.md` after the menu - run it with `/skill:chase-bug`. `gauntlet-performance` reads the committed run telemetry (current repo by default, `--dir <path>` adds others) through the parse-only `gauntlet-performance` CLI and answers with one example-led recommendation, 3-5 cornerstone numbers, and a menu of at most three actions (render a report file, open the recommendation as a ticket via `shape-ticket`, drill into one run); it writes nothing unless you pick render - run it with `/skill:gauntlet-performance [--dir <path>]... [--since <version>]`. `gauntlet-resume` is the only way back into an interrupted flow from a fresh session: it takes a pi-cohort `/handoff` brief (file or pasted) or a bare worktree that already holds a spec, restores phase/plan tracker state through the legal arming sequence (`start brainstorm`, `skip` with `resume:` reasons, `plan_check` before implement-or-later), never creates a worktree, and never infers approval from artifacts - run it with `/skill:gauntlet-resume [<brief>] [<worktree>]`.
72
+ - **20 skills** - the workflow logic. Thirteen activate automatically when pi sees the matching kind of task, and each one gates the next: `brainstorming`, `writing-plans`, `roasting-the-spec`, `test-driven-development`, `subagent-driven-development`, `dispatching-parallel-agents`, `verification-before-completion`, `requesting-code-review`, `receiving-code-review`, `using-git-worktrees`, `finishing-a-development-branch`, `writing-skills`, `linear` (reads/searches/comments on/manages Linear tickets via the `linearis` CLI; owns all linearis mechanics and the `## Issue tracker` overrides schema; tracker-facing skills route to it). Seven more are explicit-invocation-only (`disable-model-invocation: true`): `shape-ticket` creates or repairs one tracker issue per run against a Context/Problem/Idea/Acceptance-Criteria template, gated by an AC integrity check, a cheap council roast, and a single human-confirmed write - run it with `/skill:shape-ticket`. `gatekeep-pr` is consent-gated pre-merge verification of a PR against its issue - read-only gathering, verification evidence resolved CI-first (green checks on the exact assessed head count as evidence; the project's verification command runs only as fallback), a rubric-based review, then a deterministic authorship-aware menu with stable finding IDs (P#/L#/C#/F#) and numbered pre-composed courses (fixes execute as a single parallel-safe wave: one gate run, one re-review, one push); nothing mutates (fixes, pushes, reviews, merges) until you pick a row - run it with `/skill:gatekeep-pr <pr>`. `check-delivery` is a post-merge detective control: proves an issue actually shipped (default-branch landing, delivery target, per-AC evidence) before its tracker status advances; it never writes a terminal status - run it with `/skill:check-delivery <ref>`. `chase-bug` is human-only bug triage: read-only root-cause discovery to an evidenced verdict menu (real bug -> ticket/brainstorm/hotfix/respond; five negative verdicts), then a gated response to the reporter for addressable origins (GitHub issue / tracker ticket) and a rendered verdict summary otherwise - it never fixes during triage; the hotfix row hands off to `skills/chase-bug/hotfix.md` after the menu - run it with `/skill:chase-bug`. `gauntlet-performance` reads the committed run telemetry (current repo by default, `--dir <path>` adds others) through the parse-only `gauntlet-performance` CLI and answers with one example-led recommendation, 3-5 cornerstone numbers, and a menu of at most three actions (render a report file, open the recommendation as a ticket via `shape-ticket`, drill into one run); it writes nothing unless you pick render - run it with `/skill:gauntlet-performance [--dir <path>]... [--since <version>]`. `gauntlet-handoff` ends a session whose flow a fresh session will continue: it invokes pi-cohort's `handoff` skill (`--out <path>` or `--key <name>`, default key = the run worktree's branch with `/` flattened to `-`, default file `<tmpdir>/pi-handoff/<key>.md`), then appends the gauntlet process-state section (phase/plan tracker status) per the shared contract `skills/gauntlet-resume/reference/brief-contract.md` - run it with `/skill:gauntlet-handoff [--out <path> | --key <name>]`. `gauntlet-resume` is the only way back into an interrupted flow from a fresh session: it takes a `gauntlet-handoff` brief (a file path, pasted text, or - with no arguments - a pick from the briefs under `<tmpdir>/pi-handoff/`) or a bare worktree that already holds a spec, restores phase/plan tracker state through the legal arming sequence (`start brainstorm`, `skip` with `resume:` reasons, `plan_check` before implement-or-later), never creates a worktree, and never infers approval from artifacts - run it with `/skill:gauntlet-resume [<brief>] [<worktree>]`.
73
73
  - **7 subagent personas** - the specialized child agents the skills dispatch via pi-cohort: `implementer`, `code-reviewer`, `spec-reviewer`, `conformance-reviewer`, `spec-summarizer`, `spec-council-member`, `spec-council-synthesizer`. See [doc/personas.md](./doc/personas.md) for what each one does and why its permissions are scoped the way they are.
74
74
  - **4 runtime extensions** - the enforcement layer. `plan-tracker` and `phase-tracker` are tools skills call to track progress (with a TUI widget); `verify-before-ship` is a hook that warns if you push or open a PR without a passing test run since your last edit; a phase-tracker flow guard reminds on implement-phase commits missing spec/code review. In a brainstorming-entered flow, phase-tracker rejects `implement` or `verify` completion while tracker tasks remain pending or in progress; see [its configuration reference](./doc/configuration.md#phase-tracker). phase-tracker also registers `plan_check`, which verifies a plan against its spec and against the grammar in [skills/writing-plans/reference/plan-contract.md](./skills/writing-plans/reference/plan-contract.md), including that each task's `Tests:` commands are selective and never the full suite; a pass stamps the plan for implementation. `telemetry` records one committed YAML record per gauntlet run (phase timing, models, personas, gate/fix rounds, diff at ship) that ships in the squash beside the spec - finishing and the PR gate restore a stripped record, and stamp a record left `in_progress` with no ship phase as `shipped`, with `gauntlet-telemetry-salvage` - and blocks a brainstorm `write` into an already-shipped spec; see [its configuration reference](./doc/configuration.md#telemetry). See [doc/configuration.md](./doc/configuration.md) for the settings each one reads.
75
75
 
@@ -77,6 +77,20 @@ pi-gauntlet is **opinionated**: every non-trivial change is *meant* to ride this
77
77
 
78
78
  **Worktree contract.** pi runs in the primary checkout; the work happens in `.worktrees/<branch>`. Skills never `cd` there - the worktree path is a value from the `using-git-worktrees` report (or the handoff brief on `gauntlet-resume`), carried as dispatch `cwd: "<path>"`, as `git -C <path>`, or as `(cd "<path>" && <cmd>)` for other cwd-bound commands; the runtime extensions derive the checkout from the artifact path they act on. Requires git >= 2.31; a plain (non-colocated) jj workspace has no `.git`, so the resolver falls back to `jj root` there, reading primary-vs-workspace from `.jj/repo` (a directory in the primary checkout, a pointer file in added workspaces).
79
79
 
80
+ ## Handoff and resume
81
+
82
+ `/skill:gauntlet-handoff` and `/skill:gauntlet-resume` are a pair: cohort's `handoff` skill writes the six flow-agnostic headings, gauntlet-handoff appends `## Process state`, and gauntlet-resume reads the whole brief back. Both gauntlet skills read one grammar file, `skills/gauntlet-resume/reference/brief-contract.md`; the repo validator (`scripts/ci.mjs`) fails if a grammar line appears anywhere else under `skills/`. Requires the pi-cohort release that ships [pi-cohort #18](https://github.com/jjuraszek/pi-cohort/issues/18) (the `handoff` skill); on an older pi-cohort, gauntlet-handoff stops before writing.
83
+
84
+ ### Smoke walkthrough (release-gated)
85
+
86
+ Run by a human against the pi-cohort release that ships #18, before a pi-gauntlet release claims the pair works; record the outcome in the release commit body.
87
+
88
+ 1. Implement phase with tasks `complete`/`in_progress`/`pending` in a `.worktrees/<branch>` flow, session in the primary checkout: `/skill:gauntlet-handoff` writes `<tmpdir>/pi-handoff/<branch>.md` with the six core headings then `## Process state` last; a fresh session running `/skill:gauntlet-resume <path>` restores implement with the three statuses and ends `Gate history not restored; re-validating <task> before any stage advance`.
89
+ 2. Plan phase, `No plan active.`: the brief keeps that line and `Active task: none`; resume restores phase-only with no `plan_tracker init`.
90
+ 3. Hotfix context with non-pending trackers: the brief ends at `## Skills loaded`; resume takes the `chase-bug` route.
91
+ 4. Fresh session, zero arguments: the listing shows the briefs from 1-3 and a pick restores; `/skill:gauntlet-resume /nonexistent.md` stops naming that path without listing anything.
92
+ 5. Branch `hotfix/x`: the default file is `pi-handoff/hotfix-x.md` and appears in the zero-argument listing.
93
+
80
94
  ## Key concepts
81
95
 
82
96
  | Term | Meaning |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-gauntlet",
3
- "version": "5.14.0",
3
+ "version": "5.16.0",
4
4
  "description": "Opinionated, gated workflow skills, subagent personas, and runtime extensions for the pi coding agent.",
5
5
  "author": "Jacek Juraszek",
6
6
  "type": "module",
@@ -0,0 +1,109 @@
1
+ ---
2
+ name: gauntlet-handoff
3
+ description: Human-only producer of a gauntlet handoff brief - invokes pi-cohort's `handoff` skill, then appends the gauntlet process-state section (phase/plan tracker status) to the brief it wrote, per the shared brief contract. Run at the end of a session whose flow a fresh session will continue with /skill:gauntlet-resume.
4
+ disable-model-invocation: true
5
+ argument-hint: "[--out <path> | --key <name>]"
6
+ ---
7
+
8
+ > **Related skills:** Pairs with `/skill:gauntlet-resume`, the sole consumer of the brief this skill finishes. The grammar both read is `../gauntlet-resume/reference/brief-contract.md` - this file cites it and restates none of it.
9
+
10
+ # Gauntlet Handoff
11
+
12
+ ## Overview
13
+
14
+ pi-cohort's `handoff` skill writes the flow-agnostic core of a brief (six headings,
15
+ ending at `## Skills loaded`). Only this package knows what `phase_tracker` and
16
+ `plan_tracker` state mean, so this skill appends that state as one more section, in
17
+ the layout fixed by `reference/brief-contract.md` (`## Producers`). Runs in the main
18
+ loop: tracker state is extension state no subagent can read.
19
+
20
+ **Announce at start:** "I'm using the gauntlet-handoff skill to write a resumable handoff brief."
21
+
22
+ ## Boundaries
23
+
24
+ - Reads: `../gauntlet-resume/reference/brief-contract.md` in full (resolve against this
25
+ skill's directory) before any other action; the transcript; git metadata by path.
26
+ - Writes: only an append to the brief cohort wrote, wherever the resolved path puts it
27
+ (a repository-local `--out` included). Never a second file, never the trackers, never
28
+ any other repository file.
29
+ - Every STOP below prints the offending path or fact and writes nothing further.
30
+
31
+ ## Sequence
32
+
33
+ 1. **Resolve the target.**
34
+ - `--out` and `--key` both given -> STOP: they are exclusive in cohort's grammar.
35
+ - `--out <path>` -> resolve to an absolute path against the session cwd
36
+ (`node -p "require('path').resolve(process.argv[1])" -- "<path>"`); that is the
37
+ expected path and the form passed to cohort.
38
+ - Otherwise the key is `--key <name>` when given, else derived from the **run
39
+ worktree**: the path a flow skill reported as `Worktree ready at <path>` in this
40
+ transcript (the same evidence cohort's snapshot uses):
41
+
42
+ ```bash
43
+ git -C "<run worktree>" branch --show-current
44
+ ```
45
+
46
+ No run worktree in the transcript -> the session cwd branch:
47
+
48
+ ```bash
49
+ BRANCH=$(git branch --show-current)
50
+ DEFAULT=$(git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null | sed 's#^origin/##')
51
+ ```
52
+
53
+ `BRANCH` empty (detached HEAD) or equal to `DEFAULT` -> STOP asking for `--key` or
54
+ `--out` (a default-branch key would collide across flows).
55
+ - A derived key has every `/` replaced by `-` (`hotfix/x` -> `hotfix-x`) so the file
56
+ lands flat in the default directory. A human-given `--key` is passed verbatim.
57
+ - Expected path = `<tmpdir>/pi-handoff/<key>.md`, `<tmpdir>` from
58
+ `node -p "require('os').tmpdir()"`. This is the one place this package restates
59
+ cohort's default-path rule; step 3 verifies it.
60
+ 2. **Invoke cohort.** `/skill:handoff --out <absolute path>` or `/skill:handoff --key <key>`,
61
+ exactly as resolved in step 1. The skill must be present in the session skill list
62
+ under `name: handoff`. Absent -> STOP: "cohort `handoff` skill not in the session
63
+ skill list - install or upgrade pi-cohort to the release that ships pi-cohort #18".
64
+ No version lookup, no fallback to the `/handoff` prompt.
65
+ 3. **Verify the file.** Expected path missing, or line 1 not starting `# Handoff:` ->
66
+ STOP with the expected path.
67
+ 4. **Refuse a double section.** The file already has a line matching
68
+ `^## Process state\s*$` -> STOP: "the installed pi-cohort still writes process state
69
+ itself - upgrade to the release that ships #18".
70
+ 5. **Hotfix exclusion.** `skills/chase-bug/hotfix.md` is in this session's context ->
71
+ append nothing; report a plain hotfix handoff and go to step 7. Checked before the
72
+ trackers: chase-bug never touches tracker state, and a stale armed flow appended here
73
+ would steer resume away from its hotfix route.
74
+ 6. **Append.** `phase_tracker({ action: "status" })`, then `plan_tracker({ action: "status" })`.
75
+ - No phase `in_progress` -> append nothing; say the brief is a plain handoff.
76
+ - A phase `in_progress` (verify or ship included - resume stops at verify on its own)
77
+ -> append the process-state section exactly as `## Producers` in
78
+ `reference/brief-contract.md` lays it out: both status outputs verbatim
79
+ (`No plan active.` verbatim when there is no plan), the active-task line naming
80
+ the first `→` task else `none`, the gate-history line. Append with a single
81
+ `cat >> "<expected path>" <<'EOF' ... EOF` whose body is that block.
82
+ - Re-read the file tail and confirm it ends with the gate-history line from the
83
+ contract and nothing after.
84
+ 7. **Report.** Print the path and `/skill:gauntlet-resume <path>`. When a section was
85
+ appended and the brief's `worktree:` field is `no`, resume's entry check 2 needs an
86
+ override: print `/skill:gauntlet-resume <path> <override>` where `<override>` is the
87
+ run worktree from step 1 when one was found, else the session checkout root,
88
+ `dirname "$(git rev-parse --path-format=absolute --git-common-dir)"`. If that command
89
+ fails (session cwd is not inside a git repository), print the one-argument form and
90
+ the line `No resumable worktree found - pass the worktree path as the second argument.`
91
+
92
+ ## Red flags - STOP
93
+
94
+ - Writing the brief yourself instead of invoking `/skill:handoff` (the core headings are
95
+ cohort's; this skill appends one section).
96
+ - Restating the section layout here instead of reading `reference/brief-contract.md`.
97
+ - Appending when step 4 or step 5 fired.
98
+ - Deriving the key from the primary checkout's branch while a run worktree exists.
99
+
100
+ ## Project overrides
101
+
102
+ If a gauntlet overrides file exists - checked in order:
103
+ `.pi/gauntlet-overrides.md`, `<repo root>/gauntlet-overrides.md`,
104
+ `<repo root>/doc/gauntlet-overrides.md`; first found wins - read it. Any
105
+ sections relevant to this skill - by name match, by topic (routing,
106
+ verification, worktrees, etc.), or by workflow convention - override or
107
+ extend the instructions above. Project-local `AGENTS.md` is already in
108
+ context - check it for project-specific routing tables, service paths, and
109
+ verification commands.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: gauntlet-resume
3
- description: Use when a human wants to continue an interrupted gauntlet flow in a fresh session - from a pi-cohort handoff brief (file or pasted) or from a bare worktree that already holds a spec. Human-only; the sole resume entry point. Restores phase/plan tracker state through the legal arming sequence, never creates a worktree, never infers approval.
3
+ description: Use when a human wants to continue an interrupted gauntlet flow in a fresh session - from a gauntlet-handoff brief (file, pasted, or picked from the default handoff directory) or from a bare worktree that already holds a spec. Human-only; the sole resume entry point. Restores phase/plan tracker state through the legal arming sequence, never creates a worktree, never infers approval.
4
4
  disable-model-invocation: true
5
5
  argument-hint: "[<brief-file>] [<worktree-name-or-path>]"
6
6
  ---
@@ -12,7 +12,7 @@ argument-hint: "[<brief-file>] [<worktree-name-or-path>]"
12
12
  ## Overview
13
13
 
14
14
  Re-enter an interrupted gauntlet flow in a fresh session. Two inputs: a handoff brief
15
- produced by pi-cohort's `/handoff` (grammar in `reference/brief-contract.md`), or a bare
15
+ produced by `/skill:gauntlet-handoff` (grammar in `reference/brief-contract.md`), or a bare
16
16
  worktree whose spec/plan artifacts are reconstructed into tracker state
17
17
  (`reference/reconstruction.md`). Every check runs before any tracker mutation; every
18
18
  stop names the offending path, field, or line.
@@ -35,14 +35,21 @@ optional pasted text after the command line.
35
35
 
36
36
  | Form | Meaning |
37
37
  |---|---|
38
- | `<brief-file>` | readable file whose line 1 starts `# Handoff:` |
38
+ | (no tokens, no pasted text) | discovery: `<tmpdir>` via `node -p "require('os').tmpdir()"`; candidates are the regular files directly under `<tmpdir>/pi-handoff/` ending `.md` (non-recursive) whose line 1 starts `# Handoff:`; sorted by mtime, newest first; displayed one per line as `<n>. <title line> - <worktree: field value> - <mtime ISO-8601>` with `worktree: unavailable` when the field is absent; the human picks `<n>` (a single candidate is still confirmed); none -> STOP "no handoff briefs found under <tmpdir>/pi-handoff" |
39
+ | `<brief-file>` | a single token that is an absolute path, contains a path separator, or ends in `.md`; used as given, never widened to a scan; missing, unreadable, or line 1 not `# Handoff:` -> STOP with that path |
39
40
  | pasted text whose first line starts `# Handoff:` | inline brief (equivalent to a file) |
40
- | `<worktree>` alone | bare name resolved as `<primary>/.worktrees/<name>`, or an absolute path; must already be a git worktree |
41
+ | `<worktree>` alone | any other single token (no path separator, not ending `.md`): bare name resolved as `<primary>/.worktrees/<name>`; must already be a git worktree. An absolute worktree path is given only in the two-token `<brief> <worktree>` form |
41
42
  | `<brief> <worktree>` | brief plus an explicit worktree override (required when the brief's worktree field is `no`/`unavailable`/not a git repo; must equal the brief's worktree after `realpath` otherwise) |
42
43
  | anything else | not a resume input - stop with "this is a new idea - run /skill:brainstorming" |
43
44
 
44
45
  `<primary>` is the checkout owning `.worktrees/`: `dirname "$(git rev-parse --path-format=absolute --git-common-dir)"` run in the session cwd - absolute from any primary subdirectory and from inside a linked worktree (`--show-toplevel` would return the linked worktree there). A bare `<name>` resolves as `<primary>/.worktrees/<name>`. A pasted brief that needs an override uses the file form. This skill never runs `git worktree add`.
45
46
 
47
+ Classification runs before anything else: a token that is an absolute path, contains a
48
+ path separator, or ends in `.md` is a `<brief-file>` - never a worktree name, never
49
+ widened to a scan. Any other single token is tried as `<worktree>`. A picked or given
50
+ brief then runs entry checks 1-5 unchanged, so a stale or foreign-repo brief stops there.
51
+ Free-form text still lands on the "anything else" row.
52
+
46
53
  ## Entry checks
47
54
 
48
55
  In order. All before any tracker mutation; entry check 1 is read-only.
@@ -1,9 +1,12 @@
1
- # Handoff brief contract (gauntlet-resume supplementary)
1
+ # Handoff brief contract (gauntlet-resume + gauntlet-handoff supplementary)
2
2
 
3
- Consumed only by `SKILL.md` in this directory. This file is the single concentration
4
- point for text coupled to pi-cohort's `/handoff` template (`doc/handoff-template.md`
5
- in pi-cohort; baseline inlined from its `prune-prompts` branch, commit `812de45`).
6
- Cohort drift is reconciled here and nowhere else.
3
+ Consumed by `gauntlet-resume/SKILL.md` (consumer) and `gauntlet-handoff/SKILL.md`
4
+ (producer). Grammar lives only here; both skills cite this file and inline none of it
5
+ (`scripts/ci.mjs` drift lint). Coupled to pi-cohort's `handoff` skill (pi-cohort #18)
6
+ for exactly six headings - `# Handoff:`, `## Intent`, `## Repo state`, `## Decisions`,
7
+ `## Open questions`, `## Skills loaded` - and the `## Repo state` fields. `## Process
8
+ state` grammar and its consumer rules are owned here. Cohort drift is reconciled here
9
+ and nowhere else.
7
10
 
8
11
  ## Brief grammar
9
12
 
@@ -24,9 +27,9 @@ Headings, fixed order. A consumer keys on headings, never prose.
24
27
  | `## Repo state` | one field per line: `toplevel`, `worktree: yes <path>` or `worktree: no`, `branch` (`detached` when none), `HEAD`, `base` (`unknown` when no remote resolves), `dirty: <porcelain>` or `dirty: clean`, `diff-stat` (`unavailable` when base unknown), `test cmd`; any field `unavailable` when its command failed; heading `## Repo state: not a git repo` outside git | always |
25
28
  | `## Decisions` | bullets; rejected alternatives marked `rejected:` | always |
26
29
  | `## Skills loaded` | frontmatter `name`s; `## Skills loaded: none` when none | always |
27
- | `## Process state` | `phase_tracker status` then `plan_tracker status` verbatim; `Active task: <name|none>`; the line `Gate history not restored - re-validate before advancing.` | only when both tracker tools exist and a phase is `in_progress` |
30
+ | `## Process state` | `phase_tracker status` then `plan_tracker status` verbatim; `Active task: <name|none>`; the line `Gate history not restored - re-validate before advancing.` | only when a phase is `in_progress` and no hotfix flow is in context |
28
31
 
29
- Consumer rules (cohort): process state absent -> plain handoff (the consumer starts its
32
+ Consumer rules: process state absent -> plain handoff (the consumer starts its
30
33
  own process from `## Intent`); present with `No plan active.` -> phase-only, restore no
31
34
  plan; loading named skills is the consumer's job.
32
35
 
@@ -34,6 +37,38 @@ A brief is any text whose first line starts `# Handoff:` - a file on disk or pas
34
37
  inline. `## Repo state` is matched by prefix: `## Repo state` or
35
38
  `## Repo state: not a git repo`.
36
39
 
40
+ ## Producers
41
+
42
+ `gauntlet-handoff` is the only writer of `## Process state`; it appends to the file
43
+ cohort's `handoff` skill wrote, which ends at `## Skills loaded`. Layout, byte-exact:
44
+ one blank line, the heading on its own line, a blank line, the `phase_tracker status`
45
+ output verbatim, a blank line, the `plan_tracker status` output verbatim, a blank line,
46
+ `Active task: <name|none>`, then the gate-history line - unfenced, nothing after.
47
+ `Active task` is the first `→` task name, else `none` (also for `No plan active.`).
48
+
49
+ Example appended block (the fence is documentation; the brief carries no fence):
50
+
51
+ ```text
52
+
53
+ ## Process state
54
+
55
+ Phases:
56
+ ⊘ brainstorm (resume: pasted brief)
57
+ ⊘ plan (resume: pasted brief)
58
+ → implement(W2)
59
+ ○ verify
60
+ ○ ship
61
+
62
+ Plan: 1/3 done (1 in progress, 1 pending)
63
+
64
+ ✓ [0] W1: contract
65
+ → [1] W2: producer
66
+ ○ [2] W2: consumer
67
+
68
+ Active task: W2: producer
69
+ Gate history not restored - re-validate before advancing.
70
+ ```
71
+
37
72
  ## Tracker output grammar
38
73
 
39
74
  Must match `formatStatus` in the phase-tracker and plan-tracker extensions.
@@ -176,7 +176,7 @@ Inline council dispatch, reusing spec-council config and personas - **not** `/sk
176
176
 
177
177
  1. Resolve `gauntlet_setting({ key: "specCouncil" })` when the tool exists. Verdict `council` -> dispatch `spec-council-member`s in parallel plus a `spec-council-synthesizer` chair. Verdict `worker` (or empty members) -> one fresh `worker` critique, its task text carrying the absolute path to `reference/ticket-wording.md` (resolved against this skill's own directory). Malformed config -> one warning line, then branch on verdict.
178
178
  2. **Dispatch shape**, mirroring `/skill:roasting-the-spec`: write the draft body and the source snapshot (original ticket + comments, or the create-mode inputs) to absolute temp files under `mktemp -d`; delimit untrusted snapshots as data. The raw ask (create mode) or the original ticket body (repair mode) is also passed as the `Human input (verbatim; off-limits for over-spec)` block. When a split is proposed, the draft artifact holds all N proposed bodies plus their three-line justification blocks (see Split rule) in one file, not a single body. Two separate calls - never fuse members and chair into one chain (a fused chain lets one member failure kill the roast before the chair runs). Call 1: one member fanout with `cwd` = repo root, absolute `output` paths per member, run-level `control: { needsAttentionAfterMs: 60000, inFlightSilenceCeilingMs: 240000, inFlightSilenceKillMs: 300000 }` (sits beside `tasks`, not inside each task; effective silence-kill max(300s, 240+60) = 300s - record all three fields verbatim so a pi-cohort default change cannot stretch the kill). Then probe the member output files on disk with item 7's usable test. Call 2: the chair, with the usable member files via `reads`, the same control block (`:low` chair turns are short), and task text that (a) forbids repository access - member disagreement on a fact is reported in the synthesis, never verified against the repo - and (b) states coverage: `Coverage: N of M members reported; <slug>: <reason>` (pi-cohort's kill diagnostic when present, else "no output produced"; omit reasons at full coverage; singular wording when one member reported). Member task text: *the draft at `<path>` is the artifact under review; this ticket brief supersedes your spec-axis template - emit the same findings format against the draft; content-only review: the temp files plus the two referenced reference paths (split-axes, ticket-wording) are the entire permitted input - do not read, search, or scan the repository; do not edit any file.* Include the absolute paths to `reference/split-axes.md` and `reference/ticket-wording.md` (both resolved against this skill's own directory) in each member's task text - members run with `cwd` = the consumer repo, where a package-relative path does not resolve.
179
- 3. **Effort: cheap by default.** Append a `:low` thinking suffix to each member's model string at dispatch (this beats the persona's frontmatter `xhigh` pin). Same for the chair: a configured chair string gets any existing suffix replaced with `:low`; an unconfigured chair is dispatched as the parent's model with `:low` appended. The `worker` fallback carries no thinking pin - it runs at the preset's default. **Full-roast escape:** the user may request a full roast, dispatching all model strings bare/as-configured, restoring the xhigh pins; a full roast reuses the spec-roast control blocks (members `{ needsAttentionAfterMs: 300000, inFlightSilenceCeilingMs: 300000, inFlightSilenceKillMs: 600000 }`, chair `{ needsAttentionAfterMs: 300000, inFlightSilenceCeilingMs: 600000, inFlightSilenceKillMs: 900000 }`) - the 5-minute figures in item 2 are `:low`-only.
179
+ 3. **Effort: cheap by default.** Append a `:low` thinking suffix to each member's model string at dispatch (this beats the persona's frontmatter `xhigh` pin). Same for the chair: a configured chair string gets any existing suffix replaced with `:low`; an unconfigured chair is dispatched with the main loop's own string, read from `$PI_PROVIDER`/`$PI_MODEL` with the bash tool, plus `:low`. The `worker` fallback carries no thinking pin - it runs at the preset's default. **Full-roast escape:** the user may request a full roast, dispatching all model strings bare/as-configured, restoring the xhigh pins; a full roast reuses the spec-roast control blocks (members `{ needsAttentionAfterMs: 300000, inFlightSilenceCeilingMs: 300000, inFlightSilenceKillMs: 600000 }`, chair `{ needsAttentionAfterMs: 300000, inFlightSilenceCeilingMs: 600000, inFlightSilenceKillMs: 900000 }`) - the 5-minute figures in item 2 are `:low`-only.
180
180
  4. **Brief covers three axes**, absorbing the fidelity-review role without a new persona: *fidelity* - compare draft against source intent (original ticket + comments in repair; prompt + answers in create), flag `lost` / `added` / `gap` (unpacking existing claims to satisfy the ticket wording contract is not `added`; contract-conformance findings on Context/Problem/Idea outrank fidelity flags that only object to extra explanation of the same claims); and *quality* - problem framing, AC integrity beyond the deterministic gate, scope, wording, and conformance to the ticket wording contract (reference path provided in every roast brief); and *split soundness* - if the draft proposes a split, test each slice against the split-axes reference (path provided in the task text); an architecture-shaped boundary is reported as a finding line containing the marker `split-axis:` (members keep their existing spec-axis findings template; the marker is a substring flag within it, not a new findings kind), e.g. `- [major] split-axis: <slice> - <why> -> merge`. Members may argue toward one ticket, never propose or endorse a split.
181
181
  5. Disposition: unambiguous concrete fixes applied to the draft (one re-pass max); ambiguous findings surfaced at the confirmation gate. Roast edits affect the body draft pre-write only, never posted as a tracker comment, and re-run the deterministic gates (pipeline step 5). Additionally, the parent scans the **usable member output files (item 7's structural test) directly** for lines containing `split-axis:` (substring match), independent of the chair synthesis; any such finding auto-applies a merge - the split is withdrawn and the draft becomes one ticket with phased AC groups, inside the same one-re-pass budget, and the pre-merge N-body draft is kept alongside: a human re-request of the split at the gate re-presents those N bodies old->new as the approval diff (see the Split rule's sticky override). The chair keeps every other axis; clearing a `split-axis:` finding is not on its path. The same directional rule - toward one ticket, never toward a split - binds the `worker` fallback and the runtime conditional (item 6).
182
182
  6. **Runtime conditional (the one allowed):** on a harness with no `gauntlet_setting`/`subagent()` (e.g. Claude Code), dispatch fresh general-purpose subagents via that harness's native facility at low effort, with the same three-axis brief (including the absolute `reference/ticket-wording.md` path) and temp-file artifacts - on such a harness this conditional IS the roast, so the contract path must ride along.
@@ -21,7 +21,7 @@ Your context window holds the full plan, prior decisions, and conversation histo
21
21
 
22
22
  - **No context pollution.** Task N's noise doesn't leak into Task N+1.
23
23
  - **Tighter focus.** Subagent reads less, makes fewer cross-task assumptions, ships smaller diffs.
24
- - **Cheaper at scale.** Smaller models can handle simple subtasks; you only spend top-tier tokens on orchestration and hard problems.
24
+ - **Cheaper at scale.** Operator pins (`subagents.agentOverrides.<agent>.model`) route simple subtasks to smaller models; you spend top-tier tokens on orchestration and hard problems.
25
25
 
26
26
  You are the **orchestrator**. You read the plan, dispatch, review the review, decide. You do **not** write code yourself.
27
27
 
@@ -111,30 +111,13 @@ Every implementer dispatch returns one of:
111
111
 
112
112
  Implementer prompt templates must instruct subagents to return one of these statuses explicitly.
113
113
 
114
- ## Model Selection
114
+ ## Model
115
115
 
116
- Pi-subagents accepts a per-task `model` override. Use it.
117
-
118
- | Task complexity | Model tier | Use cases |
119
- |---|---|---|
120
- | Trivial mechanical change | Cheap | Rename, formatter run, dependency bump, file-move with no edits |
121
- | Standard implementation | Default | Most plan tasks — feature work with tests, refactor with tests |
122
- | Hard / novel / large surface | Most capable | New subsystem, complex algorithm, cross-service contract change |
123
- | Spec review | Default | Reads diff + spec, mechanical comparison |
124
- | Code-quality review | Most capable | Judgment call on naming, design, complexity |
125
- | Conformance / closure | Most capable | Whole-deliverable-vs-origin intent gate (`conformance-reviewer`; model from `gauntlet_setting({ key: "closureReview" }).model`, injected call-site) |
126
-
127
- ```ts
128
- subagent({
129
- agent: "implementer",
130
- async: false,
131
- cwd: "<abs worktree path>",
132
- task: "...",
133
- model: "anthropic/claude-haiku-4" // cheap tier
134
- })
135
- ```
136
-
137
- When in doubt, default. Don't downgrade reviewers — false negatives are expensive.
116
+ - Omit `model:` so the operator's `subagents.agentOverrides.<agent>.model` pin applies, or else the child inherits the main loop.
117
+ - Pass `model:` only a runtime-resolved `gauntlet_setting` value (`closureReview.model`, `escalationLoop.implModel`, `specCouncil.members[]`, `specCouncil.chair`) or a model the user named for that dispatch.
118
+ - Read the main loop's own string from `$PI_PROVIDER`/`$PI_MODEL` with the bash tool only when a skill must bypass a persona's frontmatter `thinking` pin for cost or must pass an explicit fallback after a configured model is unreachable.
119
+ - Omit `model:` when `closureReview.model` or `specCouncil.chair` is `undefined`; pass an `implModel` string verbatim, and stop with a note when `implModel` is `undefined` (Fix-Loop Rounds).
120
+ - Apply suffix edits such as `:low` only to a runtime-resolved string, never to a name the skill wrote; never write a provider or model name, tier tasks by cost, or swap a pinned model for a cheaper or stronger one.
138
121
 
139
122
  ## Dispatch
140
123
 
@@ -45,7 +45,7 @@ The persona ships model-free. Get the model from `gauntlet_setting({ key: "closu
45
45
  If `gauntlet_setting` is unavailable, stop and report - never fall back to a manual bash/JSON
46
46
  settings merge. Inject the model **call-site** on the dispatch (omit `model:` when it is `undefined` to inherit
47
47
  the parent's model) — the same mechanism the spec-council chair uses. If the configured model
48
- is unreachable, retry once with the inherited model. Point it at the strongest reasoning model
48
+ is unreachable, retry once passing the main loop's own string explicitly (read from `$PI_PROVIDER`/`$PI_MODEL`; the phase-tracker guard blocks an omitted `model:` when `closureReview.model` is set and warns on the difference). Point it at the strongest reasoning model
49
49
  the resolved config can reach — this is the last correctness gate. `thinking` stays
50
50
  frontmatter-pinned at `xhigh` and is not call-site overridable, so the config supplies only
51
51
  `model`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: writing-skills
3
- description: Use when creating new skills, editing existing skills, or verifying skills work before deployment
3
+ description: Use when creating, editing, or refactoring any SKILL.md, its reference/ files, an agent persona (`agents/*.md`, `.pi/agents/*.md`), or a prompt template - including one-line edits - or when such a file exceeds 500 lines, gains if/else branching, or drifts from imperative voice.
4
4
  ---
5
5
 
6
6
  > **Related skills:** Test new skills with `/skill:test-driven-development` discipline. Verify they work with `/skill:verification-before-completion`.
@@ -19,6 +19,15 @@ Write a pressure scenario for a subagent, watch it fail (baseline), write the sk
19
19
 
20
20
  **REQUIRED BACKGROUND:** Read `/skill:test-driven-development` first. This skill applies RED → GREEN → REFACTOR to documentation.
21
21
 
22
+ ## Authoring rules
23
+
24
+ These four rules bind the lines an edit adds or changes in any skill, persona, or prompt-template file. Leave pre-existing text alone (minimal diff). Where `reference/anthropic-best-practices.md` differs (its "Conditional workflow pattern"), this section wins.
25
+
26
+ - **Imperative voice.** Write instructions as commands. Never "should", "consider", "you may want to", "it is recommended".
27
+ - **Low conditionality.** One path per step. Branch only on a runtime fact the agent can observe - a tool result, a file's presence, a settings value - never on the reader's judgment. Two branches are the ceiling; move a third into a table or a `reference/` file.
28
+ - **Minimal diff.** A rule change touches the sentence that owns the rule, not the section. Never restate a rule in a second place; link the owner.
29
+ - **Oversized skill.** A change touching a SKILL.md over 500 lines extracts the concern it touches - or, when that concern is small, the largest self-contained `##` section - into `reference/<topic>.md` or a sibling md, in the same change, before it lands. Keep a one-line "read X now" pointer in the body at the step that needs it.
30
+
22
31
  ## Where Skills Live in Pi
23
32
 
24
33
  Pi discovers skills from multiple roots (see `docs/skills.md` in `@earendil-works/pi-coding-agent` for the full list). The common ones:
@@ -194,7 +203,7 @@ Don't invent capabilities. Don't reference Claude Code's `Task` tool, OpenCode h
194
203
  NO SKILL WITHOUT A FAILING TEST FIRST
195
204
  ```
196
205
 
197
- Applies to new skills AND edits to existing skills.
206
+ Applies to new skills AND edits to existing skills. The RED-GREEN-REFACTOR baseline binds skill bodies - new skills and behavior-changing edits to SKILL.md or reference files; a wording-only edit to a skill, persona, or prompt template is verified by reading the changed lines back against `## Authoring rules`.
198
207
 
199
208
  Wrote a skill before testing it? Delete it. Start over.
200
209
  Edited a skill without testing? Same violation.
@@ -376,7 +385,7 @@ Use `plan_tracker` to create tasks for each item:
376
385
 
377
386
  **Pi-specific**
378
387
  - [ ] If discipline skill *and* the rule is mechanically detectable at a tool boundary: consider an `extensions/*.ts` hook (high bar — runtime hooks are deliberately slim; only add ones that beat false-positive heuristics)
379
- - [ ] If skill has >500 lines: split deep content into `reference/<topic>.md` and instruct the agent to read the specific file inline
388
+ - [ ] If skill has >500 lines, or a skill this change touches does: split deep content into `reference/<topic>.md` and instruct the agent to read the specific file inline
380
389
  - [ ] Skill location: project-scoped lives under `.pi/skills/`; cross-harness skills shared with Claude Code under `.agents/skills/`; reusable workflow skills belong in a package like `pi-gauntlet`
381
390
  - [ ] Update routing: link from `AGENTS.md` if cross-cutting
382
391
 
@@ -409,11 +418,11 @@ Labels should carry semantic meaning.
409
418
  ## Red Flags — STOP
410
419
 
411
420
  - Wrote a skill without running a baseline scenario first
412
- - Edited a skill without re-running the relevant baseline
421
+ - Made a behavior-changing skill edit without re-running the relevant baseline
413
422
  - Description starts with "This skill does…" (summary instead of trigger)
414
423
  - Code-then-test ordering in the skill body
415
424
  - Force-loading other skills with `@`
416
- - SKILL.md over 500 lines with no `reference/` split
425
+ - SKILL.md over 500 lines, or a skill this change touches over 500 lines, with no `reference/` split
417
426
  - Referencing tools that don't exist in pi (Claude Code's `Task`, OpenCode hooks, etc.) for a pi-scope skill
418
427
  - About to ship multiple skills in a batch without testing each
419
428
  - "I'll test it later" — that means never