pi-gauntlet 4.12.1 → 4.12.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,13 @@
1
1
  # Changelog
2
2
 
3
+ ## v4.12.2 - 2026-08-19
4
+
5
+ - New `skills/shape-ticket/reference/split-axes.md`: the shared split test for tickets and specs - identity test (same precipitating failure / mooted outcome = one concern), user-observable outcome test, closed axis list with pass/fail examples, Never-axis list (fix location, service/repo/layer/team, "keep tickets small"), discovery-conversion exemption, fail-closed release-timing precondition gated on a documented `## Deployment` overrides section.
6
+ - `shape-ticket`: split rule rewritten around that test - one ticket is the default; each proposed slice carries a three-line justification block (root cause / outcome / axis) at the gate; roast brief gains a split-soundness axis with a `split-axis:` finding marker the parent scans from member files directly (the chair cannot clear it; merge auto-applied, pre-merge bodies kept for gate resurrection); two-tier sticky human override (deterministic failures need evidence, member-flag withdrawals resurrect on a plain yes). "Vertical/horizontal" vocabulary retired.
7
+ - `brainstorming`: scope check defaults to one spec; a multi-spec offer must render the three-line justification per spec and pass the shared test; failing splits are never offered.
8
+ - `writing-plans`: scope check splits on documented deploy topology (fail closed) or review-risk isolation - never service/contract/schema count.
9
+ - README: new `## Deployment` gauntlet-overrides section documented (deploy topology is a looked-up fact, never inferred; absent or monolithic topology disables the release-timing split axis).
10
+
3
11
  ## v4.12.1 - 2026-08-19
4
12
 
5
13
  - `check-delivery`: two new `## Delivery` overrides slots - `synthesized AC gaps: block|soft` (default `soft`; `block` runs synthesized ACs through the same verdict matrix as authored ones, so an unexplained gap blocks) and `descope edits: strikethrough` (default none; on gate approval, ratified `proposed descope` AC lines are struck in the ticket body with an evidence-drafted reason, write order body -> comment -> status, surgical patch against the re-fetched body, whole-write manual degradation when no edit-body verb resolves). Single-ticket scope boundary recorded in the skill: sweep/reconciliation passes stay consumer territory.
package/README.md CHANGED
@@ -158,6 +158,14 @@ Use the `jira` CLI (authenticated via `jira login`), not `gh` or `linearis`.
158
158
  - post comment (Reporter note only): `jira issue comment ABC-123 --body "<text>"`
159
159
  ```
160
160
 
161
+ **`## Deployment` section:** `shape-ticket` (split rule), `writing-plans` (scope check), and `brainstorming` (scope check) read deploy topology from this section: what ships together, what ships independently, and the mechanism. It is a fact to look up, never to infer - when the section is absent, or when it documents a monolithic topology (like the example below), the "separable release timing" split axis is unavailable and splits fail closed to one artifact.
162
+
163
+ ```markdown
164
+ ## Deployment
165
+
166
+ One deploy workflow ships the whole system at once - nothing ships independently.
167
+ ```
168
+
161
169
  **`## Delivery` section:** `check-delivery` resolves its overrides through the same discovery ladder. Defaults are pessimistic where it matters: an unset `target state` keeps the write comment-only; unset `deploy watch`/`delivery target` skip stage 2 (reported, never silently passed); `browser evidence` defaults to never. `check-delivery` is single-ticket by design - sweep/reconciliation passes over many tickets stay consumer territory, invoking the skill once per ticket. The remaining slots have working defaults shown below:
162
170
 
163
171
  | Slot | Meaning | Default (unset) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-gauntlet",
3
- "version": "4.12.1",
3
+ "version": "4.12.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",
@@ -109,11 +109,17 @@ Require one of: finish prior work, stash, or explicit "continue here". If the to
109
109
 
110
110
  ### 2. Scope check
111
111
 
112
- If the request spans multiple independent subsystems (e.g., "build a new ingestion pipeline and a new admin UI and a new auth flow"), stop and flag it:
112
+ One spec is the default. If the request looks like multiple independent concerns (e.g., "CSV import for operators, plus a partner-facing status API" - different actors, different problem statements), test each candidate slice against `../shape-ticket/reference/split-axes.md` (identity test, outcome test, closed axis list, release-timing precondition). A multi-spec offer renders the three-line justification per proposed spec - root cause / outcome / axis:
113
113
 
114
- > "This looks like 2-3 independent specs to me — A, B, C. Should we brainstorm each separately, or is there a tight coupling I'm missing?"
114
+ root cause: <the precipitating failure or missing capability this slice remedies>
115
+ outcome: <what a user observes once it ships>
116
+ axis: <one item from the closed list>
115
117
 
116
- Don't try to design a multi-subsystem monolith in one spec doc.
118
+ a slice failing any part -> one spec, and the failed split is not offered:
119
+
120
+ > "This looks like 2 independent specs to me - A (axis: <axis>), B (axis: <axis>). Should we brainstorm each separately, or is there a tight coupling I'm missing?"
121
+
122
+ Never split on service, package, repo, layer, or team boundaries - a single feature, bug, or improvement routinely cuts through many layers, and one spec covers it. A genuinely multi-concern request designed as one spec is still wrong; decompose it, but only along a passing axis.
117
123
 
118
124
  ### 3. Understand the idea
119
125
 
@@ -379,7 +385,7 @@ phase_tracker({ action: "complete", phase: "brainstorm" })
379
385
  - About to proceed to `/skill:writing-plans` before the user has approved the spec (proceeding *after* approval is correct; skipping the gate is the violation)
380
386
  - About to finish spec-writing for a replacement design without marking the known predecessor (see [Marking superseded specs](#marking-superseded-specs))
381
387
  - Spec contains `TODO`, `TBD`, or unnamed components
382
- - Spec spans multiple independent subsystems with no decomposition flag
388
+ - About to offer a multi-spec split that fails the split test in `../shape-ticket/reference/split-axes.md`, or without its three-line justification per spec
383
389
  - User said "this is just a small change" and you accepted it without applying the [Anti-Pattern](#anti-pattern-too-simple-to-need-a-design) check
384
390
 
385
391
  ## Project overrides
@@ -102,7 +102,7 @@ Present, per proposed issue:
102
102
  - Metadata changeset: `field: current -> proposed -- why` lines.
103
103
  - Evidence list.
104
104
  - Roast dispositions: applied / surfaced-ambiguous.
105
- - Split proposal, if any.
105
+ - Split proposal, if any: per slice, the three-line justification block (see Split rule). A withdrawn split is reported with the `split-axis:` finding that killed it, so the user can re-request it (sticky; see Split rule).
106
106
  - Proposed Reporter-note comment, verbatim, if any - one approval covers title + body + metadata + comment; no affirmative on the exact text = no comment.
107
107
 
108
108
  Number the options. A split offers per-issue subset selection, e.g. "approve 1,3; decline 2". **No affirmative answer on the exact presented diff = no write.**
@@ -152,9 +152,19 @@ The gate never bans discovery; it forces discovery to be its own honestly-labele
152
152
 
153
153
  ### Split rule
154
154
 
155
- Detected during the audit: independent deliverables that form separately shippable, separately verifiable AC clusters. Proposed at the confirmation gate (N bodies: one scoped-down original + N-1 new, each old->new or as-new) - one approval covers the batch, with subset selection. Decline -> single issue with the decomposition made explicit as phased AC groups; never a silently accepted monolith.
155
+ Detected during the audit: independent deliverables that form separately shippable, separately verifiable AC clusters. **A split is the exception; one ticket is the default.** Proposed at the confirmation gate (N bodies: one scoped-down original + N-1 new, each old->new or as-new) - one approval covers the batch, with subset selection. Decline -> single issue with the decomposition made explicit as phased AC groups; never a silently accepted monolith.
156
156
 
157
- **Hard rule: split boundaries are vertical** - feature/capability slices, each independently shippable and verifiable end-to-end. Never horizontal architecture layers: "backend part" / "frontend part" / "DB migration" tickets are a named anti-pattern - one ticket routinely cuts through many layers.
157
+ **Split test.** Apply `reference/split-axes.md` (resolve the path against this skill's own directory): every slice needs its own concern (identity test), a user-observable outcome (outcome test), and one axis from the closed list; the Never-axis list and the release-timing precondition apply as written there. Discovery/delivery pairs from the AC integrity gate's hard-stop conversion are exempt (see the reference).
158
+
159
+ **Split justification.** Each proposed slice carries three lines at the gate:
160
+
161
+ root cause: <the precipitating failure or missing capability this slice remedies>
162
+ outcome: <what a user observes once it ships>
163
+ axis: <one item from the closed list>
164
+
165
+ No block, no split. A block failing the identity or outcome test, or naming a non-axis, fails -> one ticket with phased AC groups. Locational wording anywhere in the block or the metadata rationale ("fix is X-side") is the same failure.
166
+
167
+ **Human override, sticky.** A human re-request of a withdrawn split is sticky for the rest of the run: the merge is not re-applied and the pair is not re-roasted. After a deterministic failure (locational wording, shared precipitating failure, release-timing claimed without documented topology) the re-request needs evidence the classification was factually wrong - approval alone never waives it, and `other` may not proxy a Never axis. After a member-flag withdrawal (`split-axis:` finding) a plain yes suffices - the flag is a judgment call and the human is the jury.
158
168
 
159
169
  **Over-split guard:** a single undecided parameter that the ticket's own work settles is not a split reason - restate the AC around the observable outcome instead.
160
170
 
@@ -163,11 +173,11 @@ Detected during the audit: independent deliverables that form separately shippab
163
173
  Inline council dispatch, reusing spec-council config and personas - **not** `/skill:roasting-the-spec` (that skill's contract is spec-file apply mechanics; a tracker draft is not a spec file). No new agents.
164
174
 
165
175
  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. Malformed config -> one warning line, then branch on verdict.
166
- 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. Dispatch members with `cwd` = repo root, absolute `output` paths per member, run-level `control: { needsAttentionAfterMs: 600000 }` (sits beside `tasks`, not inside each task). Give the chair the member files via `reads`. 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; do not edit any file.*
176
+ 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. 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. Dispatch members with `cwd` = repo root, absolute `output` paths per member, run-level `control: { needsAttentionAfterMs: 600000 }` (sits beside `tasks`, not inside each task). Give the chair the member files via `reads`. 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; do not edit any file.* Include the absolute path to `reference/split-axes.md` (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.
167
177
  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.
168
- 4. **Brief covers two 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`; and *quality* - problem framing, AC integrity beyond the deterministic gate, scope, wording.
169
- 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).
170
- 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 two-axis brief and temp-file artifacts.
178
+ 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`; and *quality* - problem framing, AC integrity beyond the deterministic gate, scope, wording; 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.
179
+ 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 **member output files 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).
180
+ 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 and temp-file artifacts.
171
181
  7. **Roast failure and retry.** A roast has failed when the dispatch
172
182
  errored, or the artifact the parent reads - the chair synthesis
173
183
  (council path) or the worker output (worker path) - is missing, empty,
@@ -264,7 +274,7 @@ Read this when applying the AC integrity gate (drafting, repairing, or adjudicat
264
274
  | "Skip evidence, the change is trivial" | Evidence is what lets someone other than the author tick the box |
265
275
  | "The reporter's fix IS the ticket" | The fix is a sketch in Idea; the ticket is the observable outcome |
266
276
  | "Park it quietly so the gate passes" | Parking without naming the missing value hides the defect it exists to surface |
267
- | "Split by layer to keep tickets small" | Layers are not deliverables - slice vertically or do not split |
277
+ | "Split by layer to keep tickets small" | Layers are not deliverables - apply the split test in `reference/split-axes.md` or keep one ticket |
268
278
  | "Write it now, the human said it twice" | Repetition is not confirmation - the gate needs an explicit yes on the presented diff |
269
279
 
270
280
  ## Edge cases
@@ -0,0 +1,108 @@
1
+ # Split axes: when one request becomes N artifacts
2
+
3
+ Consumed by `shape-ticket` (one ticket vs N), `brainstorming` (one spec vs N), and
4
+ `writing-plans` (scope check). One artifact is the default; a split must pass every
5
+ test below.
6
+
7
+ ## Scope
8
+
9
+ This test governs **tickets and specs**. PR and plan decomposition is owned by
10
+ `writing-plans`; one ticket normally ships as one PR. A ticket carrying too many
11
+ concerns is split by this test - never by splitting its PRs, and never by layer.
12
+
13
+ ## Identity test (a) - one concern or two
14
+
15
+ Two slices are **one** concern when either holds:
16
+
17
+ - they remediate the same precipitating failure (one trigger, one incident, one
18
+ missing capability), however many components must change; or
19
+ - landing one makes the other's stated outcome true, moot, or unobservable.
20
+
21
+ A slice is **distinct** only if it remains independently user-valuable when every
22
+ sibling is never done, and answers a different top-level need from the source
23
+ request. Complementary cause statements are one cause: "A emits something B rejects"
24
+ and "B mishandles what A emits" describe one trigger from two vantage points.
25
+ Rewording a component's internal step as a "cause" does not create a second concern.
26
+
27
+ For feature and improvement work there is no root cause, so (a) reduces to the
28
+ counterfactual: would each slice still be worth doing alone, and would the requester
29
+ call them two different things? This is a judgment call and is labeled as one - it
30
+ is weaker than the bug-side test, not a mechanical check in disguise.
31
+
32
+ ## Outcome test (b)
33
+
34
+ Each slice names an outcome a **user** observes. An outcome scoped to a component's
35
+ output ("the exporter emits a recognized identifier") is an internal step, not a
36
+ slice outcome - if a slice's headline outcome or AC is phrased at a component
37
+ boundary, it is an internal step renamed, and the slices merge.
38
+
39
+ ## Closed axis list (c)
40
+
41
+ | Axis | Passes | Fails |
42
+ |---|---|---|
43
+ | Different actor or user journey | Operator bulk-import UX vs. installer first-run onboarding - different people, neither waits on the other | "API for the operator, UI for the operator" - one journey, two layers |
44
+ | Different data domain or lifecycle | Ingest correctness vs. retention/expiry policy - different data questions, separately valuable | "Write path vs. read path of the same record" - one lifecycle, two halves |
45
+ | Separable release timing | Only with documented topology + a real A-before-B schedule (see precondition below) | "Service A ships from a different repo than service B" - boundary without schedule or topology |
46
+ | Genuinely different problem statements sharing code | Two unrelated reported defects that happen to live in one file | One defect whose fix spans two files |
47
+ | `other - <justification>` | Never auto-qualifies; explicit human approval, and it may not restate or proxy a Never axis | "other - the fix is split across tiers" |
48
+
49
+ ## Never an axis
50
+
51
+ Where the fix lives; which service, package, repo, or deployable the diff touches;
52
+ which layer (API, UI, DB, worker, ingest, job); which team owns it; "keep tickets
53
+ small". A single feature, bug, concern, or improvement routinely cuts through many
54
+ layers - normal, not a decomposition signal. Two slices sharing one concern are one
55
+ artifact however cleanly the code divides. A Never-axis failure is not waivable by
56
+ rewording; it is waivable only by evidence that the classification was factually
57
+ wrong.
58
+
59
+ ## Discovery-conversion exemption
60
+
61
+ A discovery/delivery pair produced by the AC integrity gate's hard-stop conversion
62
+ is **not** an audit-detected split and is not subject to this test: one concern
63
+ deliberately sequenced by missing knowledge, with the delivery ticket linked and
64
+ parked not-ready. The exemption covers exactly that forced pair - it is not a
65
+ template for voluntary splits.
66
+
67
+ ## Release-timing precondition (fail closed)
68
+
69
+ `axis: separable release timing` is available **only** when the resolved gauntlet
70
+ overrides file documents a `## Deployment` section stating independent ship
71
+ cadences, **and** the slices have a real schedule separation (A lands and is
72
+ verified before B starts), **and** each slice is independently verifiable
73
+ end-to-end. No inference from CI workflow files, no ask-and-assume: an undocumented
74
+ or monolithic topology makes the axis **unavailable** and the split fails closed to
75
+ one artifact. The skill may offer to document the topology in overrides; it may not
76
+ treat an in-session answer as the documented fact.
77
+
78
+ ## Fixtures
79
+
80
+ ```
81
+ Fixture 1 - relabeled layer split (must fail)
82
+ slice 1: root cause: producer emits an unrecognized identifier
83
+ outcome: the producer emits a recognized identifier
84
+ axis: different data domain or lifecycle
85
+ slice 2: root cause: consumer discards the whole run on one bad record
86
+ outcome: the import run completes
87
+ axis: different actor or user journey
88
+ rejected by: identity test (a) - one precipitating failure described from two
89
+ vantage points; slice 1's outcome is a component-boundary step under (b).
90
+ correct result: one ticket, phased AC groups (identifier fix, then resilience).
91
+
92
+ Fixture 2 - deployable boundary (must fail)
93
+ two slices differing only in which service/repo the diff touches, axis:
94
+ separable release timing.
95
+ rejected by: Never-axis list plus the release-timing precondition (no documented
96
+ `## Deployment`, no A-before-B schedule).
97
+
98
+ Fixture 3 - legitimate split (must pass)
99
+ slice 1: root cause: no retry surface exists for failed imports
100
+ outcome: an operator can retry a failed import from the run list
101
+ axis: different actor or user journey
102
+ slice 2: root cause: expired records are never purged
103
+ outcome: a compliance reviewer sees records disappear after the
104
+ retention window
105
+ axis: different data domain or lifecycle
106
+ passes: distinct triggers, each independently valuable if the other is never
107
+ done, user-observable outcomes, non-locational axes.
108
+ ```
@@ -69,16 +69,16 @@ Then continue with the normal flow below (Scope Check onward, including Recon).
69
69
 
70
70
  ## Scope Check
71
71
 
72
- Before writing the plan, check the spec one more time for hidden subsystem boundaries:
72
+ Before writing the plan, check the spec one more time:
73
73
 
74
- - Does this touch 2+ independent services / contracts / schemas?
75
- - Are there phases where intermediate state needs to be deployable?
74
+ - Does an intermediate state need to be **independently deployable**, under a deploy topology documented in the gauntlet overrides file's `## Deployment` section? Fail closed: undocumented or monolithic topology -> no deployment-driven split.
75
+ - Is there a **review-risk isolation** reason to land part separately (e.g. a large mechanical rename apart from the behavior change that motivated it)?
76
76
 
77
77
  If yes, decompose into separate plans and call it out:
78
78
 
79
79
  > "The spec covers A and B. I'd split into two plans, executed in order. OK?"
80
80
 
81
- A single plan should land in one PR worth of work. Multi-PR sequences get separate plans.
81
+ Otherwise one plan. Service, contract, or schema count is not a split signal - one concern routinely spans several. The concern test itself lives in `../shape-ticket/reference/split-axes.md` (resolve the path against this skill's own directory) and was applied upstream at spec time; plans do not re-litigate it. A single plan should land in one PR worth of work.
82
82
 
83
83
  ## Recon (mandatory)
84
84