pi-gauntlet 5.3.7 → 5.5.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 +10 -0
- package/README.md +1 -1
- package/agents/spec-reviewer.md +7 -6
- package/extensions/lib/gauntlet-settings.test.ts +37 -0
- package/extensions/lib/gauntlet-settings.ts +24 -0
- package/extensions/lib/plan-check.test.ts +281 -8
- package/extensions/lib/plan-check.ts +187 -2
- package/extensions/phase-tracker.test.ts +24 -1
- package/extensions/phase-tracker.ts +11 -3
- package/package.json +1 -1
- package/skills/subagent-driven-development/SKILL.md +21 -16
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +1 -1
- package/skills/subagent-driven-development/implementer-prompt.md +9 -1
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +3 -1
- package/skills/subagent-driven-development/stop-note.md +48 -0
- package/skills/test-driven-development/SKILL.md +3 -3
- package/skills/verification-before-completion/reference/settings-precedence.md +1 -1
- package/skills/writing-plans/SKILL.md +14 -26
- package/skills/writing-plans/reference/plan-contract.md +70 -0
|
@@ -126,14 +126,13 @@ If you can't list the files, the spec isn't ready: amend or redraw per brainstor
|
|
|
126
126
|
|
|
127
127
|
Group tasks into **waves** so the executor can parallelize independent work (see `subagent-driven-development` Parallel-Wave Mode). A wave is a maximal set of tasks that (a) have no ordering dependency on each other, (b) own **pairwise-disjoint files**, and (c) contend on **no shared mutable runtime resource** (same DB/schema, port, fixture file, external service, shared temp path).
|
|
128
128
|
|
|
129
|
-
- Tasks nest under `## Wave N — <label>` headers; `### Task N` headers sit inside a wave.
|
|
130
129
|
- Group independent tasks into the same wave by default. A wave with one task is legal **only with a named-blocker justification**: a body line directly under the `## Wave N — <label>` header, `Solo: <reason>`, where the reason names the blocking task/wave, the contended runtime resource, or `lone remaining task` (reserved for the genuinely final unmatched task; doc-only trailing waves qualify). Category-only justifications ("dependency" with no named task) do not satisfy the rule.
|
|
131
130
|
- A pure dependency chain yields one task per wave — no parallelism, which is correct; each such wave carries its `Solo:` line naming the prior-wave dependency.
|
|
132
131
|
- Each wave after the first states its dependency on prior waves.
|
|
133
132
|
|
|
134
|
-
**File-ownership contract.**
|
|
133
|
+
**File-ownership contract.** See [reference/plan-contract.md § Files](reference/plan-contract.md).
|
|
135
134
|
|
|
136
|
-
**Test
|
|
135
|
+
**Test contract.** Every task that creates or modifies code declares a `Test:` path and an executable `**Tests:**` command anchored to it (grammar: [reference/plan-contract.md § Tests](reference/plan-contract.md)); `- none: <category>` only when no tests apply. Anchoring is presence, not coverage. When the anchored spec names the thing under test, the task carries `- via:` naming it; a fixture path the spec names goes under `Create:`.
|
|
137
136
|
|
|
138
137
|
**Runtime-resource disjointness.** File-disjoint is necessary but not sufficient: two tasks with disjoint files that both mutate the same DB, bind the same port, or share a fixture are **not** parallel-safe and must land in different waves. The executor auto-selects parallel for *every* multi-task wave, so this grouping is the sole parallel-safety guarantee — there is no selection-time judgment downstream. No new mandatory per-task syntax; when a shared runtime resource is the reason two file-disjoint tasks sit in different waves, record it in an inline note on the later wave.
|
|
139
138
|
|
|
@@ -192,7 +191,7 @@ Each step is **one action, 2-5 minutes**:
|
|
|
192
191
|
---
|
|
193
192
|
```
|
|
194
193
|
|
|
195
|
-
The
|
|
194
|
+
The full verification entrypoint appears only on the `**Verification:**` line — see [reference/plan-contract.md § Header-only entrypoint](reference/plan-contract.md). The verify phase reads it from the plan; execution runs `Tests:` commands only.
|
|
196
195
|
|
|
197
196
|
## Task Structure
|
|
198
197
|
|
|
@@ -210,6 +209,10 @@ Each task uses `- [ ]` checkbox steps so execution tools (and humans) can track
|
|
|
210
209
|
- Modify: `exact/path/to/existing.py:123-145`
|
|
211
210
|
- Test: `tests/exact/path/to/test.py`
|
|
212
211
|
|
|
212
|
+
**Tests:**
|
|
213
|
+
- `uv run pytest tests/exact/path/to/test.py`
|
|
214
|
+
- via: `function()`
|
|
215
|
+
|
|
213
216
|
- [ ] **Step 1: Write the failing test**
|
|
214
217
|
|
|
215
218
|
```python
|
|
@@ -250,28 +253,14 @@ Each task uses `- [ ]` checkbox steps so execution tools (and humans) can track
|
|
|
250
253
|
|
|
251
254
|
Every code task carries this step (red -> green -> fmt/lint -> commit). Doc-only tasks omit it unless the project formats Markdown.
|
|
252
255
|
|
|
253
|
-
**Anchor rules.**
|
|
256
|
+
**Anchor rules.** See [reference/plan-contract.md § Spec anchors](reference/plan-contract.md). A task with no anchorable requirement omits the `**Spec:**` line and carries a mechanical-task row in `## Spec coverage` — silence is never valid.
|
|
254
257
|
|
|
255
258
|
## Spec Coverage Table
|
|
256
259
|
|
|
257
|
-
Every plan ends with a `## Spec coverage` section
|
|
258
|
-
|
|
259
|
-
```markdown
|
|
260
|
-
## Spec coverage
|
|
261
|
-
|
|
262
|
-
| anchor | requirement (short) | owner |
|
|
263
|
-
|---|---|---|
|
|
264
|
-
| § "Design" L34-L37 | anchor line in task template | Task 2 |
|
|
265
|
-
| § "Edge cases" L120 | stale anchor = blocking SR finding | Task 4, Task 5 |
|
|
266
|
-
| § "Testing" L84 | checker fixtures: `node --test extensions/lib/plan-check.test.ts` | Task 3 |
|
|
267
|
-
| § "Acceptance" L88 | full suite passes: `npm test` | Verification |
|
|
268
|
-
| § "Out of scope" L131 | fix-round anchoring | waived: out of scope per spec |
|
|
269
|
-
| - | mechanical: release commit | Task 7 |
|
|
270
|
-
```
|
|
260
|
+
Every plan ends with a `## Spec coverage` section (grammar and example: [reference/plan-contract.md § Spec coverage table](reference/plan-contract.md)). Build it extraction-first: walk the spec top to bottom and write one row per normative requirement **before** assigning owners — every Design imperative (Add/Remove/Keep/Replace-style directives, not any fixed lexical form), every Edge-cases rule, every Acceptance criterion, every Out-of-scope entry, and every non-none Documentation-impact entry. Then assign owners, then re-walk the spec once: every normative clause has a row. Two row kinds:
|
|
271
261
|
|
|
272
|
-
- **Requirement rows:**
|
|
273
|
-
- **`Verification` owner:**
|
|
274
|
-
- **Mechanical-task rows:** anchor `-`, requirement `mechanical: <short>`, owner = the task ID. One such row per anchor-less task.
|
|
262
|
+
- **Requirement rows:** a cross-cutting requirement (decided in more than one task) lists **every** deciding task as owner, not the first. `waived: <reason>` is only for requirements the spec marks out of scope **and** that exclude work from the change. A requirement whose text carries an inline code span (`` `literal` ``) names concrete behaviour and is never waivable - it maps to a task or `Verification`. A waiver on an in-scope normative requirement is a Self-Review failure — there is no human plan-review gate to catch it downstream.
|
|
263
|
+
- **`Verification` owner:** only for a requirement the header command proves; grammar in the reference.
|
|
275
264
|
- The table is plan-authoring-time only — never passed to implementer or reviewer dispatches.
|
|
276
265
|
|
|
277
266
|
## No Placeholders
|
|
@@ -282,11 +271,10 @@ Every plan failure mode:
|
|
|
282
271
|
- ❌ `# Implement the rest of the function` — incomplete code is invalid code.
|
|
283
272
|
- ❌ "Add tests for edge cases" — name the edge cases.
|
|
284
273
|
- ❌ "Wire it up to the existing system" — give file paths and call sites.
|
|
285
|
-
- ❌ "timeout/gtimeout ladder" when the spec fixes the literal `timeout 30` — never paraphrase an exact-string requirement (setting keys, error messages, banner/format strings, command names and invocations, API shapes); transcribe it as a backtick-quoted spec literal: `timeout 30`. Spec-side backtick spans containing `<placeholder>` segments are templates the plan instantiates, not exact-string requirements — exempt from quote integrity.
|
|
286
274
|
- ❌ "Similar to Task N" — repeat the code. Implementers (and subagents with fresh context) may read tasks out of order; pointing at a sibling task is not a substitute for showing the code.
|
|
287
275
|
- ❌ References to types, functions, methods, or fields not defined in any task in this plan. If it shows up in Task 5, it must be introduced by Task 1–4 or already exist in the codebase (with a file:line citation).
|
|
288
|
-
- ❌ `[fill in]`, `<example>`, `xxx` markers anywhere in the doc.
|
|
289
276
|
- ❌ "Probably also need to update the docs" — either yes (which doc) or no. Docs are named plan tasks, sourced from the spec's Documentation impact section (materiality bar in `brainstorming/reference/documentation-impact.md`).
|
|
277
|
+
- Quote integrity and the banned-token list: [reference/plan-contract.md § Placeholders and quote integrity](reference/plan-contract.md).
|
|
290
278
|
|
|
291
279
|
If a decision is genuinely open, put it in an explicit **Open Questions** section at the top and resolve before execution starts.
|
|
292
280
|
|
|
@@ -294,10 +282,10 @@ If a decision is genuinely open, put it in an explicit **Open Questions** sectio
|
|
|
294
282
|
|
|
295
283
|
After drafting the plan and before announcing it complete, run the deterministic checker, then the judgment checks yourself — not a subagent dispatch.
|
|
296
284
|
|
|
297
|
-
- **Deterministic checker.** Run `plan_check({ planPath })` on the saved plan. Assess and fix every finding yourself (no human involvement), then re-run until it passes — a pass writes the execution stamp that implement-start verifies mechanically. If the same finding survives 3 fix rounds, convert it to an explicit Open Question and stop (the pre-existing Open-Questions halt, resolved by the human in-session — not a new gate).
|
|
285
|
+
- **Deterministic checker.** Run `plan_check({ planPath })` on the saved plan. Assess and fix every finding yourself (no human involvement), then re-run until it passes — a pass writes the execution stamp that implement-start verifies mechanically. If the same finding survives 3 fix rounds, convert it to an explicit Open Question and stop (the pre-existing Open-Questions halt, resolved by the human in-session — not a new gate). Findings are defined in [reference/plan-contract.md](reference/plan-contract.md).
|
|
298
286
|
- **Code-vs-anchor sanity.** For each task-owned requirement row, re-read the anchored spec lines and confirm the owner tasks' bodies do what they say - mechanism present, not just the quoted literal. For each `Verification` row, confirm the header command exercises the anchored requirement. Fix the task, don't annotate.
|
|
299
287
|
- **Type / API consistency.** Function signatures and field names that appear in multiple tasks must match exactly. The plan is its own contract — internal contradictions surface as bugs during execution.
|
|
300
|
-
- **
|
|
288
|
+
- **Test contract.** Every code task's `Tests:` commands are anchored to its `Test:` path(s); `none:` only where no tests apply; a spec-named seam appears as `via:`, a spec-named fixture path as `Create:`.
|
|
301
289
|
- **Runtime-resource disjointness.** For every multi-task wave, confirm no two tasks contend on a shared mutable runtime resource (DB/schema, port, fixture, external service, shared temp path) — `Files:` overlap is checked mechanically, resource contention is not. Contention = mis-grouped wave; split or re-order before handoff.
|
|
302
290
|
- **Solo-reason validity.** Every single-task wave's `Solo:` line (presence is checked mechanically) must name its specific blocker — the blocking task/wave, the contended resource, or `lone remaining task`. Category-only justifications are under-justified; merge or justify before handoff.
|
|
303
291
|
- **Waiver authorization.** `waived: <reason>` is only for requirements the spec marks out of scope **and** that exclude work from the change. A requirement whose text carries an inline code span (`` `literal` ``) names concrete behaviour and is never waivable - it maps to a task or `Verification`. A waiver on an in-scope normative requirement is a Self-Review failure — there is no human plan-review gate to catch it downstream.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Plan contract
|
|
2
|
+
|
|
3
|
+
The grammar `plan_check` enforces. Each section names its check(s). Findings resolve in `writing-plans` Self-Review: fix, re-run.
|
|
4
|
+
|
|
5
|
+
## Waves and tasks
|
|
6
|
+
|
|
7
|
+
A wave is a maximal set of tasks that have no ordering dependency on each other, own pairwise-disjoint files, and contend on no shared mutable runtime resource (same DB/schema, port, fixture file, external service, shared temp path).
|
|
8
|
+
|
|
9
|
+
- Tasks nest under `## Wave N — <label>` headers; `### Task N` headers sit inside a wave.
|
|
10
|
+
|
|
11
|
+
## Files (`wave-file-disjointness`, `paths-exist`)
|
|
12
|
+
|
|
13
|
+
**File-ownership contract.** The per-task `**Files:**` block *is* the ownership declaration — no new syntax. Rule: **within a wave, the union of every task's declared paths must be pairwise disjoint.** Globs are allowed for `Modify` when exact paths are unknown, but must not overlap another same-wave task's paths. A task that must touch another's file belongs in a later wave.
|
|
14
|
+
|
|
15
|
+
`Test:` entries are run anchors, not ownership: `Test`/`Test` on the same path across same-wave tasks is allowed; `Test` vs another task's `Create`/`Modify` is a conflict; writer/writer stays a conflict. `Modify:` paths must exist.
|
|
16
|
+
|
|
17
|
+
## Spec anchors (`anchor-resolution`)
|
|
18
|
+
|
|
19
|
+
**Anchor rules.** The task's `**Spec:**` line cites the plan header's spec path; multiple anchors sit comma-separated on one line (`§ "A" L10-L18, § "C" L40-L44`). Checks key on the `§` marker, so the header's path-only `**Spec:**` line is never matched. Anchors are captured against the gated spec at plan-writing time; a change to the approved spec follows brainstorming's [Amending an approved spec](../../brainstorming/SKILL.md#amending-an-approved-spec), executed in place. A task with no anchorable requirement (pure-mechanics chore) omits the `**Spec:**` line entirely (never `**Spec:** none`) and carries a mechanical-task row in `## Spec coverage` — silence is never valid.
|
|
20
|
+
|
|
21
|
+
## Tests (`tests-block`)
|
|
22
|
+
|
|
23
|
+
Every `### Task N` carries a `**Tests:**` block: the bare line `**Tests:**` directly after the last `Files:` entry (blank lines allowed). Bullets, in either form:
|
|
24
|
+
|
|
25
|
+
- `- ` + backtick + command + backtick - one scoped test command; `- via: <entry point>` - the seam the tests call directly (function, route, CLI, module - e.g. `each_finding`), zero or more
|
|
26
|
+
- `- none: <category>` - the task runs no tests; `<category>` is free text derived from the project (docs, config, fixtures, generated assets, ...); exactly one, and no `- Test:` path in `Files:`
|
|
27
|
+
|
|
28
|
+
Absence is never valid. A `- [ ]` step or any non-bullet line ends the block. `via:` and `none:` are unchecked beyond form.
|
|
29
|
+
|
|
30
|
+
Commands run from the repo root. Each command is split into segments on `&&`, `||`, `;`, `|`; every segment must contain, as a whitespace-delimited token, a `Test:` path of the same task (the path alone, or followed by `::`, `#`, or `:` and a filter). A `Test:` value containing `*`, `?`, `[` or ending in `/` never anchors; any other argument token with those shapes is a broadening selector and fails. `cd `, `sh -c`, `bash -c`, `eval `, `$(` are unsupported. Each `Test:` path must exist or be a `Create:` path of some task. A segment equal to a header `**Verification:**` segment is a full-suite command and fails. Runners with no file-addressable form are out of scope (`go test ./pkg -run X`, `mvn -Dtest=`).
|
|
31
|
+
|
|
32
|
+
## Solo line (`solo-line`)
|
|
33
|
+
|
|
34
|
+
A wave with one task carries, directly under its `## Wave N — <label>` header, the line `Solo: <reason>`. The reason names the blocking task/wave, the contended runtime resource, or `lone remaining task`. Presence is checked here; validity is `writing-plans` Self-Review.
|
|
35
|
+
|
|
36
|
+
## Header-only entrypoint (`header-entrypoint`)
|
|
37
|
+
|
|
38
|
+
The `**Verification:**` line is the **only** place the full verification entrypoint may appear — never in any task or wave step. The verify phase reads it from the plan instead of re-deriving it; execution runs scoped commands only.
|
|
39
|
+
|
|
40
|
+
The header value's backtick spans (else the raw value) are split on `&&`, `||`, `;`, `,` into segments. No `Run:` step payload segment and no `Tests:` bullet segment may equal a header segment; prose inside waves may not contain the whole header value.
|
|
41
|
+
|
|
42
|
+
## Spec coverage table (`table-closure`, `waiver-literal`)
|
|
43
|
+
|
|
44
|
+
Every plan ends with a `## Spec coverage` section — authored last, placed after all Task sections (owner IDs do not exist earlier). Closure both ways: every `### Task N` appears as an owner in some row; every row's owner task exists.
|
|
45
|
+
|
|
46
|
+
```markdown
|
|
47
|
+
## Spec coverage
|
|
48
|
+
|
|
49
|
+
| anchor | requirement (short) | owner |
|
|
50
|
+
|---|---|---|
|
|
51
|
+
| § "Design" L34-L37 | anchor line in task template | Task 2 |
|
|
52
|
+
| § "Edge cases" L120 | stale anchor = blocking SR finding | Task 4, Task 5 |
|
|
53
|
+
| § "Testing" L84 | checker fixtures: `node --test extensions/lib/plan-check.test.ts` | Task 3 |
|
|
54
|
+
| § "Acceptance" L88 | full suite passes: `npm test` | Verification |
|
|
55
|
+
| § "Out of scope" L131 | fix-round anchoring | waived: out of scope per spec |
|
|
56
|
+
| - | mechanical: release commit | Task 7 |
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Requirement rows:** anchor + short requirement + owner = task-ID list, or `Verification`, or `waived: <reason>`. A `waived:` row whose requirement cell contains an inline code span fails `waiver-literal`.
|
|
60
|
+
|
|
61
|
+
**`Verification` owner:** use for a requirement the header `**Verification:**` command proves. Write the exact string `Verification`, alone. Quote only literals contained in that header. Anchor the single requirement line. Keep scoped commands task-owned.
|
|
62
|
+
|
|
63
|
+
**Mechanical-task rows:** anchor `-`, requirement `mechanical: <short>`, owner = the task ID. One such row per anchor-less task.
|
|
64
|
+
|
|
65
|
+
## Placeholders and quote integrity (`placeholder-scan`, `quote-integrity`)
|
|
66
|
+
|
|
67
|
+
- ❌ "timeout/gtimeout ladder" when the spec fixes the literal `timeout 30` — never paraphrase an exact-string requirement (setting keys, error messages, banner/format strings, command names and invocations, API shapes); transcribe it as a backtick-quoted spec literal: `timeout 30`. Spec-side backtick spans containing `<placeholder>` segments are templates the plan instantiates, not exact-string requirements — exempt from quote integrity.
|
|
68
|
+
- ❌ `[fill in]`, `<example>`, `xxx` markers anywhere in the doc.
|
|
69
|
+
|
|
70
|
+
The banned token list is `BANNED_TOKENS` in `extensions/lib/plan-check.ts`; a token inside a literal the anchored spec requires is exempt.
|