pi-gauntlet 5.5.0 → 5.5.2

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,17 @@
1
1
  # Changelog
2
2
 
3
+ ## v5.5.2 - 2026-09-13
4
+
5
+ - `release.sh <level>` promotes the CHANGELOG `## Unreleased` section to `## vX.Y.Z - <date>` and commits it with `package.json` in the single `Release X.Y.Z` commit, so `patch`/`minor`/`major` now work here (the `current`-only path is gone). New CONFIG field `CHANGELOG_HEADING`.
6
+ - Release skill: a user instruction naming the level is the approval - no proposal step or re-confirmation; bundled follow-ups run after `verify`.
7
+ - AGENTS.md rewritten to always-on essentials plus routing; shared core bumped to v3. Persona frontmatter knobs table and pin rationale moved to `doc/personas.md`. Gold rule scoped to agent-initiated writes; a user instruction naming the write is its confirmation.
8
+ - Added `.pi/gauntlet-overrides.md` (`tracker: github`, release path, write-gate carve-out for user-named writes).
9
+ - `verification-before-completion/reference/conformance-check.md`: the conformance fix round is one parallel `implementer` `tasks` wave (greedy `Gn` selection, `conflicts` partners held) -> `git apply` -> scoped tests -> delta re-audit. Per-gap `spec-reviewer`, per-round `code-reviewer`, and the per-round full test set are gone. New **Convergence** step after every `CONFORMS`: full `Verification` set once, one direct `code-reviewer` over `git diff <r0-head>..HEAD`; repairs re-enter the round as a `conformance fix CR` wave that counts against `maxFixRounds`. Finish grammar, personas, and settings unchanged.
10
+
11
+ ## v5.5.1 - 2026-09-10
12
+
13
+ - `linear`: copyable, version-scoped recovery for attachment-download 401s resolves the decrypted credential through linearis instead of reading encrypted token storage. Restricts credential delivery to HTTPS Linear uploads, rejects redirects, and checks downloaded bytes; an offline regression executes the documented example.
14
+
3
15
  ## v5.5.0 - 2026-09-10
4
16
 
5
17
  - `writing-plans`: every task carries a `**Tests:**` block - scoped commands anchored to the task's `Test:` paths, optional `via:` entry point, or `none: <category>`; the plan grammar `plan_check` enforces moves to `skills/writing-plans/reference/plan-contract.md`. (#28)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-gauntlet",
3
- "version": "5.5.0",
3
+ "version": "5.5.2",
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",
@@ -24,7 +24,9 @@ this skill alters re-gates.
24
24
  Preferred: `linearis` on PATH and authenticated (`linearis auth status`). Token
25
25
  resolution order: `--api-token`, `LINEAR_API_TOKEN`, `~/.linearis/token`. This is a
26
26
  preference, not a precondition - a missing or unauthenticated CLI degrades Linear
27
- functionality and is reported, never blocks the run.
27
+ functionality and is reported, never blocks the run. The token file is encrypted
28
+ storage: never use its contents as an HTTP credential or infer the credential type
29
+ from its `v1:` storage-format prefix. Resolve it through linearis instead.
28
30
 
29
31
  > **No `linearis` installed?** If `command -v linearis` fails, fall back to a
30
32
  > **Linear MCP server** when the harness has one configured - its tools cover the
@@ -192,7 +194,7 @@ Safety rules, in addition to the write gate above:
192
194
  | Symptom | Cause | Fix |
193
195
  |---|---|---|
194
196
  | 401 | Not authenticated / expired token | `linearis auth status`; re-auth - unless the download row below applies. |
195
- | 401 on `files download` while `issues read` works | linearis 2026.7.0 and 2026.8.0 prepend `Bearer ` to personal API keys on file downloads ([linearis-oss/linearis#300](https://github.com/linearis-oss/linearis/issues/300)) | Not an auth problem - do not re-auth. Fetch the URL with the bare key, or use a version without the bug once one ships. |
197
+ | 401 on `files download` while `issues read` works | linearis 2026.7.0 and 2026.8.0 prepend `Bearer ` to personal API keys on file downloads ([linearis-oss/linearis#300](https://github.com/linearis-oss/linearis/issues/300)) | Not an auth problem - do not re-auth. Use the recovery below, or use a version without the bug once one ships. |
196
198
  | Issue not found | Wrong workspace, or issue archived | Confirm workspace; check archived state. |
197
199
  | Status not found | Status name doesn't match the team's workflow states | List the team's states before setting one. |
198
200
  | Missing `--team` error on create | `--team` is required | Supply `--team <default team>`. |
@@ -203,6 +205,45 @@ Safety rules, in addition to the write gate above:
203
205
  | Read is slow | Big ticket with many comments/attachments | Drop `--with-*` flags not needed. |
204
206
  | Parser-shape failure on a documented invocation: unknown command/option, unexpected argument | Section 3's snapshot may have drifted from the installed CLI | Re-read that subcommand's `--help`; report the row stale **only if** help actually contradicts it, then follow help |
205
207
 
208
+ For that download-only 401, this 2026.7.0/2026.8.0 workaround calls linearis's
209
+ version-specific internal `getApiToken` API. Supply the fresh `uploads.linear.app` URL
210
+ from `issues read --with-attachments` and an output path. It rejects other hosts and
211
+ redirects, sends the resolved key without `Bearer`, writes only a non-empty response,
212
+ and never prints or stores the key separately:
213
+
214
+ <!-- linear-download-recovery:start -->
215
+ ```bash
216
+ download_linear_asset() {
217
+ LINEARIS_BIN="${LINEARIS_BIN:-$(command -v linearis)}" node --input-type=module - "$1" "$2" <<'NODE'
218
+ import { realpathSync, writeFileSync } from "node:fs";
219
+ import { dirname, join } from "node:path";
220
+ import { pathToFileURL } from "node:url";
221
+
222
+ const [urlText, output] = process.argv.slice(2);
223
+ const url = new URL(urlText);
224
+ if (url.protocol !== "https:" || url.hostname !== "uploads.linear.app")
225
+ throw new Error("refusing to send a credential outside https://uploads.linear.app");
226
+ const packageRoot = dirname(dirname(realpathSync(process.env.LINEARIS_BIN)));
227
+ const { getApiToken } = await import(pathToFileURL(join(packageRoot, "dist/common/auth.js")));
228
+ const response = await fetch(url, {
229
+ headers: { Authorization: getApiToken({}) },
230
+ redirect: "error",
231
+ });
232
+ if (!response.ok) throw new Error(`download failed: HTTP ${response.status}`);
233
+ const bytes = new Uint8Array(await response.arrayBuffer());
234
+ if (bytes.byteLength === 0) throw new Error("download failed: empty response");
235
+ writeFileSync(output, bytes);
236
+ console.log(`downloaded ${bytes.byteLength} bytes to ${output}`);
237
+ NODE
238
+ }
239
+ download_linear_asset 'https://uploads.linear.app/...' '/tmp/attachment'
240
+ ```
241
+ <!-- linear-download-recovery:end -->
242
+
243
+ Do not declare the attachment inaccessible until this recovery used the resolved
244
+ credential; reading `~/.linearis/token` directly does not count. Afterward, confirm the
245
+ reported byte count and inspect the file type before consuming or extracting it.
246
+
206
247
  The last row's trigger is deliberately narrow. Data, auth, status-name, and root-thread
207
248
  validation errors have their own rows above and are **not** drift - routing them to "the
208
249
  skill is stale" would misdiagnose ordinary failures. This row is the reactive path for
@@ -142,29 +142,24 @@ prerequisites hold.
142
142
  Per round:
143
143
 
144
144
  1. **Synchronize gap tasks** — append only a genuinely new gap that is entering remediation, named `Gn: <gap origin clause verbatim, truncated>`; never `init`. Find existing gaps by their exact `Gn:` prefix and reuse that index even if origin wording changes. Carried-OPEN inventory-only gaps add nothing. Before dispatch, mark every remediated gap's existing index `in_progress`; a re-audit needing more work reopens that same `Gn` index. The lifecycle traces `[T1,T2]`, then `[T1,T2,G1]`, then `[T1,T2,G1,G2]`; no test-retry or review-round wrapper task.
145
- 2. **Fix dispatch** — per `dispatching-parallel-agents` "Fix fan-out": a `disjoint`
146
- group of ≥ 2 gaps (per the report's `Parallel-safe:` line) fixes in one parallel
147
- foreground dispatch — one `implementer` per gap (fresh context, `async: false`,
148
- `worktree: true`, `cwd` = the conformance worktree, task = the gap block verbatim
149
- with `touched-files` as the ownership boundary). For an `UNAUTHORIZED` `fix` gap
145
+ 2. **Fix wave** — select gaps greedily in `Gn` order: take each `fix` gap unless a gap it `conflicts` with (per the report's `Parallel-safe:` line) is already taken; the certificate's `disjoint` grouping is ignored, and a certificate still malformed after the one re-ask means every gap `conflicts` with every other. Held gaps carry to the next round; the wave is never empty while an eligible `fix` gap exists. Dispatch **one** call - `subagent({ context: "fresh", async: false, tasks: [...] })` - with one `implementer` task per selected gap (`worktree: true`, `cwd` = the conformance worktree, task = the gap block verbatim with `touched-files` as the ownership boundary). A single gap is a one-task `tasks` call; a lone `agent: "implementer"` call never appears in this loop. For an `UNAUTHORIZED` `fix` gap
150
146
  whose `evidence` opens with the over-spec provenance (`spec "<section>" - "<clause>" (over-spec)`), the orchestrator adds the spec path to that gap's `touched-files` before dispatch,
151
147
  so the implementer deletes the surface **and** the clause/AC line in the same
152
148
  fix commit; the re-audit then has no `Rn` for it and no `MISSING` echo. The dispatch adds `SCOPED_TEST_COMMANDS`
153
- to the gap block: the gap-relevant plan-declared commands, or `none` (the round's
154
- test gate owns execution). `conflicts` pairs serialize. Gaps outside any ≥ 2-ID
155
- `disjoint` group run sequentially as before. Then dispatch foreground `spec-reviewer`
156
- per gap on the gap-block reference contract below.
149
+ to the gap block: the gap-relevant plan-declared commands, or `none`; on an ad-hoc no-plan path, the project's canonical test command.
157
150
  3. **Integrate** serially via `git apply` onto the worktree HEAD, one gap's
158
- patch at a time. Failure handling is inherited verbatim from
151
+ patch at a time. Commit each per-gap fix with the message **`conformance fix Gn`** (durable,
152
+ `git log`-readable pre-squash) so the finish gate and any revert can identify
153
+ auto-applied fixes; a Convergence repair wave commits as one **`conformance fix CR`**.
154
+ Failure handling is inherited verbatim from
159
155
  `dispatching-parallel-agents` "Review and Integrate": textual conflict →
160
156
  re-run one agent sequentially with the other's integrated changes as
161
157
  context; semantic conflict (applies clean, suite fails) → re-run the
162
158
  offending task sequentially on integrated HEAD; a failed agent → integrate
163
159
  the successes, then retry the failure with fresh context including the
164
160
  integrated changes. A `BLOCKED`/`NEEDS_CONTEXT` return surfaces to the user.
165
- 4. **Test gate** on the integrated tree. In a plan flow, run the full plan-header `Verification` set once here; on an ad-hoc no-plan path, use the project's canonical test command. A failure re-enters the failure-handling rules above.
166
- 5. **Round CR and completion** — run `code-reviewer` once on the round's cumulative fix delta (not per gap), foreground with `async: false` and `SCOPED_TEST_COMMANDS` = the round's gap-relevant commands, or `none` (the round's test gate owns execution). After integration, tests, and this CR accept the work, explicitly mark every remediated gap's same `Gn` index `complete`, before re-audit.
167
- 6. **Re-audit**: foreground re-dispatch `conformance-reviewer` with `async: false` over the fixes **plus** the
161
+ 4. **Scoped tests** on the integrated tree: the round's `SCOPED_TEST_COMMANDS` union. A failure re-enters the failure-handling rules above.
162
+ 5. **Re-audit**: foreground re-dispatch `conformance-reviewer` with `async: false` over the fixes **plus** the
168
163
  regression guard (any prior-`DELIVERED` requirement whose `evidence` file
169
164
  the fix diff touched). Pass the full prior conformance report (every row,
170
165
  including DELIVERED rows and their `evidence` `file:line`) and the round's
@@ -173,18 +168,23 @@ Per round:
173
168
  `model:` when it is `undefined` to inherit the parent's model. Inside a
174
169
  brainstorming-entered flow, the phase-tracker closure guard blocks a dispatch
175
170
  that omits `model:` when `closureReview.model` is set, and warns (non-blocking)
176
- on one whose model differs.
177
- 7. **Converge or continue**: verdict `CONFORMS` → record it, done. Open gaps
171
+ on one whose model differs. Mark every `Gn` the re-audit reports `DELIVERED`
172
+ `complete`; open ones stay `in_progress`.
173
+ 6. **Converge or continue**: verdict `CONFORMS` → Convergence below. Open gaps
178
174
  within the cap → re-partition (per the rule above) and start the next
179
175
  round. Cap (`gauntlet_setting({ key: "closureReview" }).maxFixRounds`,
180
176
  default `2`, floors negatives at `0`, coerces non-integers to `2`) reached
181
- with an open `fix` gap → **escalate to the human** with the per-gap
177
+ with an open `fix` gap or repair item → **escalate to the human** with the per-gap
182
178
  round-by-round verdict trail. Escalation is the sole non-completing
183
179
  terminal state — no silent re-loop, no auto-ship.
184
180
 
185
- Commit each per-gap fix with the message **`conformance fix Gn`** (durable,
186
- `git log`-readable pre-squash) so the finish gate and any revert can identify
187
- auto-applied fixes.
181
+ **Convergence** — runs after R0 `CONFORMS` and after every `CONFORMS` re-audit. `r0-head` is HEAD when the loop was entered (the R0 dispatch, or the finish-time `fix-now` entry) - the parent of the oldest `conformance fix` commit; `audited-base` stays the last audit's HEAD SHA.
182
+
183
+ a. Run the full plan-header `Verification` set once (ad-hoc: the project's canonical test command). After R0 `CONFORMS` with no round run, the pre-R0 full run counts.
184
+ b. Dispatch `code-reviewer` directly (foreground, `async: false`, `SCOPED_TEST_COMMANDS: none`) over `git diff <r0-head>..HEAD`; never via `/skill:requesting-code-review`. An empty diff is nothing to review - no dispatch.
185
+ c. Repair items = every failing command from a + every Critical/Moderate finding from b (`Behaviour-change: yes` included; the re-audit is its origin check). None → write the closure block; done. Any at the cap → escalate per step 6 with the test/CR trail. Any under the cap → re-enter step 2 as a one-task `tasks` wave: one `implementer` whose task is every repair item verbatim (ownership boundary = the files in `git diff <r0-head>..HEAD`, the CR findings' `touched-files`, and the files each failing command's output names, `SCOPED_TEST_COMMANDS: none`, no `Gn` tracker task, no gap selection), integrate as one `conformance fix CR`, re-audit, then Convergence again. That wave counts against `maxFixRounds`. Later Convergence CRs keep the same `<r0-head>..HEAD` range.
186
+
187
+ `conformance fix CR` is not a gap fix: it is absent from the `auto-applied fix commits` index and has no `revert conformance fix Gn` action at the finish gate.
188
188
 
189
189
  **`maxFixRounds: 0`**: skip this loop entirely; every `recommended: fix` gap is
190
190
  carried OPEN to the finish gate per the precondition-unavailable
@@ -194,22 +194,6 @@ auto-fix, so treat `fix` gaps like any other deferred gap — unlike a cap > 0
194
194
  that is *exhausted*, which escalates mid-verify because the loop tried and
195
195
  could not converge.
196
196
 
197
- ### `spec-reviewer` gap-block reference contract
198
-
199
- Per-gap `spec-reviewer` in step 2 above is a **pre-integration mechanical
200
- check**, distinct from the round-level re-audit in step 6 (which still
201
- references the *origin* — spec + original prompt — unchanged). Frame the
202
- per-gap dispatch against the **gap block**, not a plan task:
203
-
204
- - **Requirement** = the gap's `origin` + `remediation` (what must be true
205
- after the fix).
206
- - **Closure proof** = the patch satisfies that requirement within the gap's
207
- `touched-files` — nothing missing, nothing extra.
208
- - **Output** = `spec-reviewer`'s normal MATCH/DRIFT verdict, referenced to the
209
- gap block instead of a plan task.
210
-
211
- This is a task-framing contract in the dispatch, not a new persona.
212
-
213
197
  ## Concern decomposition
214
198
 
215
199
  The main verification orchestrator — **not** `conformance-reviewer` — decomposes
@@ -459,15 +443,15 @@ concerns into one gap-scoped fix contract:
459
443
  boundary).
460
444
 
461
445
  Rescoped, accepted, and followed-up sibling concerns are **excluded** from the
462
- projection. The `implementer` and the pre-integration `spec-reviewer` receive
463
- this projected contract in place of the original whole-gap block.
464
-
465
- The projected task runs the existing full loop above: it retains the gap-level
466
- `conformance fix Gn` commit name, reruns the project's tests, runs
467
- `code-reviewer`, re-audits against the amended spec, and reenters the gate only
468
- if concerns remain. Gap-level revert stays available through the flat commit
469
- index. The gate records the final result by concern ID and title before showing
470
- branch integration options.
446
+ projection. The `implementer` receives this projected contract in place of the
447
+ original whole-gap block.
448
+
449
+ The projected task runs the round and Convergence above with `r0-head` = HEAD at
450
+ this entry: it retains the gap-level `conformance fix Gn` commit name, re-audits
451
+ against the amended spec, and reenters the gate only if concerns remain.
452
+ Gap-level revert stays available through the flat commit index. The gate records
453
+ the final result by concern ID and title before showing branch integration
454
+ options.
471
455
 
472
456
  ## Checklist
473
457