pi-gauntlet 4.12.0 → 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 +14 -0
- package/README.md +25 -3
- package/package.json +1 -1
- package/skills/brainstorming/SKILL.md +10 -4
- package/skills/check-delivery/SKILL.md +98 -14
- package/skills/shape-ticket/SKILL.md +54 -17
- package/skills/shape-ticket/reference/split-axes.md +108 -0
- package/skills/writing-plans/SKILL.md +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
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
|
+
|
|
11
|
+
## v4.12.1 - 2026-08-19
|
|
12
|
+
|
|
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.
|
|
14
|
+
- `shape-ticket`: the batched write may include one optional gated Reporter-note comment per approved issue (overflow of demoted Idea material; verbatim at the same single confirmation gate; missing post-comment verb degrades to manual posting, never silently dropped). Roast dispatch failure now retries once with fresh artifacts; a second failure is surfaced inline at the gate (`roast unavailable (dispatch failed twice: <reason>)`) instead of a silent note.
|
|
15
|
+
- README: both skills' contracts synced (slot docs, five-verb custom-tracker contract incl. `post comment`).
|
|
16
|
+
|
|
3
17
|
## v4.12.0 - 2026-08-18
|
|
4
18
|
|
|
5
19
|
- New skill: `check-delivery` - explicit-only post-merge detective control: proves an issue actually shipped (SHA-bound squash-aware default-branch landing, optional deploy-watch + delivery-target check, per-AC evidence with a 6-verdict table and reviewer script) before its tracker status advances; never writes a terminal state; single human-gated batched write with `Delivered: <sha>` marker idempotency; generic across `gh`/`linearis` with a 7-slot `## Delivery` overrides contract so consumer closeout prompts reduce to an overrides block plus a one-line wrapper.
|
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ pi-gauntlet's only hard dependency is pi-cohort - every gate that dispatches a r
|
|
|
35
35
|
|
|
36
36
|
Concretely, one change through the gauntlet:
|
|
37
37
|
|
|
38
|
-
0. *(Optional)* Before there's even a spec, `/skill:shape-ticket` can create or repair a single tracker issue - shaping a raw ask into a Context/Problem/Idea/Acceptance Criteria ticket, gated by an AC integrity check, a cheap council roast, and one human-confirmed write. It's a tool, not a phase: no worktree, no plan/phase tracker, runs from any repo state. It never activates on its own (`disable-model-invocation: true`) - invoke it explicitly.
|
|
38
|
+
0. *(Optional)* Before there's even a spec, `/skill:shape-ticket` can create or repair a single tracker issue - shaping a raw ask into a Context/Problem/Idea/Acceptance Criteria ticket, gated by an AC integrity check, a cheap council roast, and one human-confirmed write that may include one optional gated Reporter-note comment. A failed roast is retried once, then surfaced inline at the gate if it fails again. It's a tool, not a phase: no worktree, no plan/phase tracker, runs from any repo state. It never activates on its own (`disable-model-invocation: true`) - invoke it explicitly.
|
|
39
39
|
1. You describe the change. **`brainstorming`** sets up an isolated worktree, explores the codebase, and turns your description into a written spec. A multi-model critique runs on it automatically. If the spec replaces a known prior spec, brainstorming marks the predecessor with a `> **Superseded by:**` banner under its title (default format, syntax overridable via `.pi/gauntlet-overrides.md`; event-driven only — gauntlet never sweeps historical specs). **You read and approve the spec - human gate 1.** No implementation code exists yet.
|
|
40
40
|
2. **`writing-plans`** decomposes the approved spec into atomic, independently-verifiable tasks, grouped into parallel waves where they don't touch the same files.
|
|
41
41
|
3. **`subagent-driven-development`** executes the plan one task at a time, each in a fresh subagent, behind spec-compliance review then code-quality review. TDD-locked: red, green, refactor.
|
|
@@ -144,7 +144,7 @@ Section headers should match skill names (`## verification-before-completion`) o
|
|
|
144
144
|
|
|
145
145
|
**Discovery ladder:** skills check three locations, in order, and use the first one found - never merged: `.pi/gauntlet-overrides.md`, then `<repo root>/gauntlet-overrides.md`, then `<repo root>/doc/gauntlet-overrides.md` (`<repo root>` = `git rev-parse --show-toplevel`, or the current directory outside a repo). Pick one location per repo.
|
|
146
146
|
|
|
147
|
-
**`## Issue tracker` section:** `shape-ticket` resolves tracker access through a capability ladder, and this is its first rung - it overrides the zero-config `gh` (GitHub) / `linearis` (Linear) defaults for any other tracker. Name the CLI's read, search, create, and
|
|
147
|
+
**`## Issue tracker` section:** `shape-ticket` resolves tracker access through a capability ladder, and this is its first rung - it overrides the zero-config `gh` (GitHub) / `linearis` (Linear) defaults for any other tracker. Name the CLI's read, search, create, update, and post comment commands explicitly. For a Jira CLI, for example:
|
|
148
148
|
|
|
149
149
|
```markdown
|
|
150
150
|
## Issue tracker
|
|
@@ -155,9 +155,18 @@ Use the `jira` CLI (authenticated via `jira login`), not `gh` or `linearis`.
|
|
|
155
155
|
- search (dup/reversal check): `jira issue search --jql "project = ABC AND text ~ '<query>'"`
|
|
156
156
|
- create: `jira issue create --project ABC --type Task --summary "<title>" --description "<body>"`
|
|
157
157
|
- update: `jira issue edit ABC-123 --summary "<title>" --description "<body>"`
|
|
158
|
+
- post comment (Reporter note only): `jira issue comment ABC-123 --body "<text>"`
|
|
158
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
|
+
|
|
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:
|
|
161
170
|
|
|
162
171
|
| Slot | Meaning | Default (unset) |
|
|
163
172
|
|---|---|---|
|
|
@@ -168,6 +177,17 @@ Use the `jira` CLI (authenticated via `jira login`), not `gh` or `linearis`.
|
|
|
168
177
|
| `browser evidence` | When/how to capture UI evidence (requires a browser tool) | never |
|
|
169
178
|
| `ref convention` | How commits/PRs reference tickets (e.g. `(ref ABC-123)`) | tracker-native forms (`#N`, `Fixes #N`, bare `ABC-123`) |
|
|
170
179
|
| `AC location` | Where ACs live if not the ticket body | ticket body |
|
|
180
|
+
| `synthesized AC gaps` | `block` or `soft` - whether an unmet synthesized AC produces a blocking `unexplained gap` or a non-blocking proposal | `soft` |
|
|
181
|
+
| `descope edits` | `strikethrough` - on gate approval, strike ratified `proposed descope` AC lines in the ticket body | none - no body edits ever |
|
|
182
|
+
|
|
183
|
+
Malformed slot values fail safe, with one warning per invocation naming the
|
|
184
|
+
bad value: `synthesized AC gaps` treats anything other than `block`/`soft`
|
|
185
|
+
as `soft`; `descope edits` treats anything other than `strikethrough` as
|
|
186
|
+
unset (no body edits). `descope edits` also requires a resolved edit-body
|
|
187
|
+
write verb: when the slot is active and the approval ratifies a `proposed
|
|
188
|
+
descope` but no edit-body verb resolves, the whole batched write (body
|
|
189
|
+
edit, evidence comment, status advance) degrades to manual - none
|
|
190
|
+
auto-posted.
|
|
171
191
|
|
|
172
192
|
```markdown
|
|
173
193
|
## Delivery
|
|
@@ -176,6 +196,8 @@ Use the `jira` CLI (authenticated via `jira login`), not `gh` or `linearis`.
|
|
|
176
196
|
- delivery target: curl -fsS https://staging.example.com/version | grep <sha>
|
|
177
197
|
- timeout: 15m
|
|
178
198
|
- ref convention: (ref ABC-123)
|
|
199
|
+
- synthesized AC gaps: block
|
|
200
|
+
- descope edits: strikethrough
|
|
179
201
|
```
|
|
180
202
|
|
|
181
203
|
## REVIEW.md convention
|
package/package.json
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
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
|
|
@@ -15,14 +15,16 @@ disable-model-invocation: true
|
|
|
15
15
|
| Comments amend/contradict the body's ACs | pause for operator resolution, then proceed - never silently pick a reading |
|
|
16
16
|
| Zero ACs after extraction and synthesis | STOP - "cannot verify a ticket that asserts nothing" |
|
|
17
17
|
| Genuine open deliverable PR found | STOP - "work still in flight" |
|
|
18
|
-
| Same shipped SHA already recorded (marker + target state reached) | "already recorded" - no write |
|
|
18
|
+
| Same shipped SHA already recorded (marker + target state reached) | "already recorded" - no write (body-only descope repair excepted - see stage 0) |
|
|
19
19
|
| Deliverable set ambiguous, or not on default branch | STOP - abort, no write |
|
|
20
20
|
| Delivery target configured but unreachable / times out / can't bind to the SHA | STOP - abort, no write |
|
|
21
21
|
| Any AC verdict is `unexplained gap` | Failure path - no delivery write; findings comment offered, gated |
|
|
22
22
|
| All ACs clear (incl. proposals, non-observable, unverified-no-target) | One confirmation gate, then one batched write |
|
|
23
23
|
| No delivery target configured | Stage 2 reported skipped - never silently passed |
|
|
24
24
|
| No target state configured | Comment only - never guesses a workflow state |
|
|
25
|
-
| Missing write capability | Full verification +
|
|
25
|
+
| Missing write capability | Full verification + every write half that exists this run emitted for manual execution |
|
|
26
|
+
| Unmet synthesized AC | Non-blocking proposal under `synthesized AC gaps: soft` (default); same verdict matrix as authored ACs under `block` |
|
|
27
|
+
| Ratified `proposed descope` + `descope edits: strikethrough` | Body edit joins the batched write: body -> comment -> status |
|
|
26
28
|
|
|
27
29
|
## Overview
|
|
28
30
|
|
|
@@ -52,6 +54,17 @@ Explicit invocation only (`/skill:check-delivery <ticket-ref>`), read-only
|
|
|
52
54
|
against the repository (no builds, no branch/tag/worktree mutation) plus at
|
|
53
55
|
most one batched tracker write, runs from wherever invoked, no worktree.
|
|
54
56
|
|
|
57
|
+
## Scope boundary
|
|
58
|
+
|
|
59
|
+
Single-ticket by design: the pipeline's depth (repo-identity preflight,
|
|
60
|
+
stage-1 landed-commit resolution, per-AC evidence binding, one human gate
|
|
61
|
+
with pre-write re-fetch) does not loop soundly - N tickets means N gates,
|
|
62
|
+
or a batch gate that dilutes per-ticket consent. Sweep passes (bucketing
|
|
63
|
+
open tickets, orphan scans for merged-but-unreferenced work) are
|
|
64
|
+
portfolio triage - a different control, inherently project-flavored - and
|
|
65
|
+
stay consumer territory. A consumer sweep may invoke this skill per
|
|
66
|
+
ticket as its verification step.
|
|
67
|
+
|
|
55
68
|
## Hard constraint
|
|
56
69
|
|
|
57
70
|
Input is exactly one tracker ref (`ABC-123`, `#N`, `owner/repo#N`, or a
|
|
@@ -77,10 +90,14 @@ Resolved in order:
|
|
|
77
90
|
4. Ask the user.
|
|
78
91
|
|
|
79
92
|
Missing **read** capability = STOP. Missing **write** capability degrades
|
|
80
|
-
gracefully: run the full verification, then emit
|
|
81
|
-
|
|
82
|
-
target state is configured, the exact
|
|
83
|
-
advance as not performed.
|
|
93
|
+
gracefully on ordinary runs: run the full verification, then emit every
|
|
94
|
+
write half that exists this run for manual execution - the
|
|
95
|
+
evidence-comment text, and, when a target state is configured, the exact
|
|
96
|
+
status-advance command - reporting the advance as not performed. When
|
|
97
|
+
`descope edits` is active and the approval ratifies a `proposed descope`
|
|
98
|
+
but no edit-body verb resolves, the **whole** batched write degrades to
|
|
99
|
+
manual instead: body edit, evidence comment, and status advance (when
|
|
100
|
+
configured) all emitted for manual execution, none auto-posted.
|
|
84
101
|
|
|
85
102
|
**Zero-config verb table** (overrides replace it):
|
|
86
103
|
|
|
@@ -89,6 +106,7 @@ advance as not performed.
|
|
|
89
106
|
| read issue + comments | `gh issue view <n> --json title,body,comments` | `linearis issues read <id> --with-comments` |
|
|
90
107
|
| post comment | `gh issue comment <n> --body ...` | `linearis issues discuss <id> --body ...` |
|
|
91
108
|
| update state | override-defined only (never invented labels/columns) | `linearis issues update <id> --status <name>` |
|
|
109
|
+
| edit body (only `descope edits`) | `gh issue edit <n> --body ...` | `linearis issues update <id> --description ...` |
|
|
92
110
|
|
|
93
111
|
## Verification pipeline
|
|
94
112
|
|
|
@@ -107,9 +125,14 @@ current tracker status first: if it is already in a terminal/done state,
|
|
|
107
125
|
report that and stop cleanly - no write; a terminal ticket is never
|
|
108
126
|
downgraded to the configured non-terminal target state. Extract ACs
|
|
109
127
|
from the AC section; if none exists, synthesize candidate ACs from the body,
|
|
110
|
-
label them synthesized
|
|
111
|
-
|
|
112
|
-
|
|
128
|
+
label them synthesized. Their blocking power follows the `synthesized AC
|
|
129
|
+
gaps` slot: under `soft` (the default) a synthesized AC never produces a
|
|
130
|
+
blocking `unexplained gap` - unmet ones surface as non-blocking proposals
|
|
131
|
+
at the gate; under `block` the cap is removed - synthesized ACs run the
|
|
132
|
+
same stage 3 verdict matrix as authored ACs, so an unmet one with no
|
|
133
|
+
sanctioned explanation is a blocking `unexplained gap` (`allowed gap` and
|
|
134
|
+
`proposed descope` remain available outcomes exactly as for authored ACs).
|
|
135
|
+
If a comment amends or contradicts a
|
|
113
136
|
body AC, surface the conflict to the operator and get an explicit
|
|
114
137
|
resolution before proceeding - never silently pick a reading. If
|
|
115
138
|
extraction and synthesis together yield zero candidate ACs, STOP:
|
|
@@ -121,6 +144,13 @@ configured) was already reached -> report "already recorded" and stop
|
|
|
121
144
|
cleanly - success, no write. If the marker exists but the target state
|
|
122
145
|
was not reached, skip the duplicate comment but still offer the status
|
|
123
146
|
write (comment-landed/status-failed repair, per Idempotency below).
|
|
147
|
+
Exception when `descope edits: strikethrough` is configured: if a prior
|
|
148
|
+
run's evidence comment records a `proposed descope` whose matching AC line
|
|
149
|
+
in the body is still unstruck, the run is **not** "already recorded" - it
|
|
150
|
+
short-circuits to a gated body-only repair: the strike is presented at the
|
|
151
|
+
confirmation gate, resolving only the shipped SHA already recorded in the
|
|
152
|
+
marker (no fresh stage 1-3 re-verification); on approval, only the body
|
|
153
|
+
edit executes - no duplicate comment, no status advance.
|
|
124
154
|
|
|
125
155
|
**Stage 1 - Merge landed.** One ordered algorithm, not a toolbox:
|
|
126
156
|
|
|
@@ -212,7 +242,10 @@ explicit yes-gate as the success path. Never posted unprompted.
|
|
|
212
242
|
All-clear path -> **one confirmation gate**: present the shipped SHA,
|
|
213
243
|
deliverable set, stage results (including any skip), the per-AC verdict
|
|
214
244
|
table with evidence, the reviewer script, and the exact write about to
|
|
215
|
-
happen. On approval, one batched write
|
|
245
|
+
happen. On approval, one batched write. The numbered steps below are the
|
|
246
|
+
ordinary-run shape; when a ratified descope strike is in play, the
|
|
247
|
+
body-edit half precedes step 1 - see the `descope edits` paragraph below
|
|
248
|
+
for its mechanics, not repeated here:
|
|
216
249
|
|
|
217
250
|
1. **Evidence comment** - marker line `Delivered: <sha>` as the first line,
|
|
218
251
|
the deliverable set (PR links), stage 2 outcome (or "skipped: no
|
|
@@ -224,13 +257,53 @@ happen. On approval, one batched write:
|
|
|
224
257
|
state, never invents a label.
|
|
225
258
|
|
|
226
259
|
Comment first, status last, so a partial failure leaves evidence without a
|
|
227
|
-
misleading state
|
|
228
|
-
|
|
229
|
-
the gate = no write,
|
|
260
|
+
misleading state - this is the stated order for ordinary runs. Before
|
|
261
|
+
writing, re-fetch the ticket: if ACs or status changed since gather,
|
|
262
|
+
re-present the delta instead of writing. Declining the gate = no write,
|
|
263
|
+
report stays in-session.
|
|
264
|
+
|
|
265
|
+
When `descope edits: strikethrough` is configured and the approval
|
|
266
|
+
ratifies one or more `proposed descope` ACs, the batched write gains a
|
|
267
|
+
body-edit half, ordered **body edit -> evidence comment -> status
|
|
268
|
+
advance** - a partial failure never leaves a status advanced against a
|
|
269
|
+
body still advertising a descoped AC. A failed body-edit half aborts the
|
|
270
|
+
remaining halves outright: the comment and status advance do not run. Any
|
|
271
|
+
partial failure reports exactly which halves landed and which did not.
|
|
272
|
+
The edit strikes exactly the ratified AC lines and nothing else: the AC's
|
|
273
|
+
list item is preserved, its text struck, and a one-line reason appended -
|
|
274
|
+
`- [ ] ~~<AC text>~~ - descoped: <one-line reason>`. The checkbox is
|
|
275
|
+
rendered exactly as found (`- [x]` stays `- [x]`, `- [ ]` stays `- [ ]`) -
|
|
276
|
+
the edit never flips checked state. The reason is one sentence drafted
|
|
277
|
+
from the verdict's evidence, never invented.
|
|
278
|
+
|
|
279
|
+
Strike-target identity: the target is the AC-section list item whose
|
|
280
|
+
normalized text equals the verdict's AC text - normalize by stripping the
|
|
281
|
+
list marker and checkbox, collapsing internal whitespace, and comparing
|
|
282
|
+
the remaining text case-insensitively. When `AC location` is not
|
|
283
|
+
the ticket body, or no unique matching list item exists (synthesized,
|
|
284
|
+
prose, duplicate, or multi-line ACs), the strike is skipped for that AC,
|
|
285
|
+
reported as skipped at the gate, and the evidence comment carries the
|
|
286
|
+
descope record alone. A line already containing `~~` and `- descoped:`
|
|
287
|
+
counts as struck and is skipped.
|
|
288
|
+
|
|
289
|
+
Both zero-config edit verbs replace the whole body, so the strike is
|
|
290
|
+
applied as a surgical patch against the freshly re-fetched body at write
|
|
291
|
+
time - never by replaying the gather-time snapshot. If the target lines
|
|
292
|
+
moved or changed, re-present the delta instead of writing.
|
|
293
|
+
|
|
294
|
+
The exact before/after of the struck lines is presented at the same
|
|
295
|
+
single confirmation gate as the comment and status - one approval covers
|
|
296
|
+
all halves, including every proposed strike; there is no per-strike
|
|
297
|
+
subset selection, so objecting to one strike declines the whole gate this
|
|
298
|
+
round. Descope edits occur only on the all-clear batched write - the
|
|
299
|
+
failure path writes no body edit.
|
|
230
300
|
|
|
231
301
|
## Idempotency and concurrency
|
|
232
302
|
|
|
233
|
-
Append-only, at-least-once
|
|
303
|
+
Append-only, at-least-once for comments - a comment is never edited once
|
|
304
|
+
posted. The ratified-descope body strike is the one sanctioned in-place
|
|
305
|
+
body edit; it does not change this comment guarantee. Same shipped SHA
|
|
306
|
+
already marked -> skip the
|
|
234
307
|
duplicate comment, but still offer the status write if the configured
|
|
235
308
|
target state was not reached on the prior run (comment-landed /
|
|
236
309
|
status-failed repair). A newer shipped SHA -> a fresh comment, never an
|
|
@@ -252,6 +325,8 @@ the newer SHA.
|
|
|
252
325
|
| `browser evidence` | When/how to capture UI evidence (requires a browser tool) | never |
|
|
253
326
|
| `ref convention` | How commits/PRs reference tickets (e.g. `(ref ABC-123)`) | tracker-native forms (`#N`, `Fixes #N`, bare `ABC-123`) |
|
|
254
327
|
| `AC location` | Where ACs live if not the ticket body | ticket body |
|
|
328
|
+
| `synthesized AC gaps` | `block` or `soft` - whether an unmet synthesized AC produces a blocking `unexplained gap` or a non-blocking proposal | `soft` |
|
|
329
|
+
| `descope edits` | `strikethrough` - on gate approval, strike ratified `proposed descope` AC lines in the ticket body | none - no body edits ever |
|
|
255
330
|
|
|
256
331
|
Worked example:
|
|
257
332
|
|
|
@@ -270,6 +345,11 @@ contract: a consumer's closeout prompt reduces to a `## Delivery` block plus
|
|
|
270
345
|
a one-line wrapper invoking this skill. Worktree cleanup is out of scope -
|
|
271
346
|
that belongs to `finishing-a-development-branch`.
|
|
272
347
|
|
|
348
|
+
Malformed slot values fail safe, with one warning per invocation naming the
|
|
349
|
+
bad value: `synthesized AC gaps` treats anything other than `block`/`soft`
|
|
350
|
+
as `soft`; `descope edits` treats anything other than `strikethrough` as
|
|
351
|
+
unset (no body edits).
|
|
352
|
+
|
|
273
353
|
## Rationalization table
|
|
274
354
|
|
|
275
355
|
| Excuse | Reality |
|
|
@@ -280,6 +360,7 @@ that belongs to `finishing-a-development-branch`.
|
|
|
280
360
|
| "No target configured, so delivery passed" | Unconfigured is a reported **skip**, never a silent pass |
|
|
281
361
|
| "The ticket says done in a comment" | Ticket narrative is not evidence; only SHA-pinned code or target observations count |
|
|
282
362
|
| "Just move it to Done, the human can reopen" | Never a terminal status - acceptance is the human's move, not this skill's |
|
|
363
|
+
| "The AC was only synthesized, so the gap can't block" | Under `synthesized AC gaps: block`, a synthesized AC runs the same verdict matrix as an authored one - an unexplained gap blocks |
|
|
283
364
|
|
|
284
365
|
## Red flags - STOP
|
|
285
366
|
|
|
@@ -298,6 +379,9 @@ that belongs to `finishing-a-development-branch`.
|
|
|
298
379
|
- Silently picking a reading when a comment amends or contradicts a body AC
|
|
299
380
|
- Continuing verification with zero candidate ACs
|
|
300
381
|
- Advancing past a genuine open deliverable PR
|
|
382
|
+
- Striking an AC line the gate approval did not ratify as `proposed descope`
|
|
383
|
+
- Applying a body edit by replaying the gather-time snapshot instead of
|
|
384
|
+
patching the re-fetched body
|
|
301
385
|
|
|
302
386
|
## Project overrides
|
|
303
387
|
|
|
@@ -13,7 +13,7 @@ disable-model-invocation: true
|
|
|
13
13
|
| Free text / vague ask, no ticket ref | Create mode: gather -> dup-check -> draft -> gates -> roast -> confirm -> write (9-step pipeline below) |
|
|
14
14
|
| Issue ref (`#N`, `ABC-123`, URL) | Repair mode: fetch full ticket + 1-hop links -> diff old->new -> gates -> roast -> confirm -> re-fetch and re-diff before write (9-step pipeline below) |
|
|
15
15
|
| Ref + extra text | Repair mode, extra text folded into gather |
|
|
16
|
-
| Ticket already conforms + metadata clean | No-op: report and stop, no gate, no write |
|
|
16
|
+
| Ticket already conforms + metadata clean + no Reporter note proposed | No-op: report and stop, no gate, no write |
|
|
17
17
|
| AC is wishful/tautological | Hard stop for the whole run - no write until fixed or split to discovery |
|
|
18
18
|
| AC is unspecified-but-binding or needs external input | Ticket filed, parked in not-ready state, blocker named |
|
|
19
19
|
| Independent shippable slices detected | Split proposed at the gate, one approval, per-issue subset selection |
|
|
@@ -25,7 +25,7 @@ Every path that writes ends at the **same single confirmation gate** - no write
|
|
|
25
25
|
|
|
26
26
|
One process, two entry points: **create** a new tracker issue from a prompt, or **repair** an existing one by re-fetching it and proposing a full replacement. Repair is never a silent patch - it is always shown old->new, links and tracker fields preserved. Running this skill on any ticket trues it up; repeated use is a self-healing backlog pass.
|
|
27
27
|
|
|
28
|
-
**Core principle:** the ticket carries **what and why, never how**. A prescribed solution in the source material is demoted to the **Idea** section as an attributed sketch ("reporter's proposed approach: ...") - never the Problem, never an Acceptance Criterion, never
|
|
28
|
+
**Core principle:** the ticket carries **what and why, never how**. A prescribed solution in the source material is demoted to the **Idea** section as an attributed sketch ("reporter's proposed approach: ...") - never the Problem, never an Acceptance Criterion, never an *ungated* tracker comment - the one gated Reporter-note comment (step 4) is the sole exception.
|
|
29
29
|
|
|
30
30
|
**Violating the letter of the rules is violating the spirit of the rules.** "The gate basically happened" is not the gate happening.
|
|
31
31
|
|
|
@@ -41,7 +41,7 @@ It is a tool, not a phase: no `plan_tracker`, no `phase_tracker`, no worktree re
|
|
|
41
41
|
- **Free text or no argument** -> **create mode**, from the argument plus surrounding conversation.
|
|
42
42
|
- **Ref + extra text** -> repair mode, with the text folded into gather.
|
|
43
43
|
- **Unreadable ref** -> abort repair, offer create mode from any accompanying text. Never guess ticket content from a ref you cannot fetch.
|
|
44
|
-
- **Out of scope**: read-only asks (search, status lookup) and administrative writes (status transitions,
|
|
44
|
+
- **Out of scope**: read-only asks (search, status lookup) and administrative writes (status transitions, standalone comment posts) - use the tracker CLI directly; the gated Reporter-note comment inside a create/repair write is the sole comment exception.
|
|
45
45
|
|
|
46
46
|
## The pipeline
|
|
47
47
|
|
|
@@ -67,13 +67,27 @@ One question at a time, only when intent is unclear or no AC is derivable. Never
|
|
|
67
67
|
|
|
68
68
|
Full replacement body: `Context` / `Problem` / `Idea` / `Acceptance Criteria`, plus optional `Out of scope / Follow-up` and `Post-deployment housekeeping`. Apply the wording rules (below). Preserve links and tracker fields.
|
|
69
69
|
|
|
70
|
+
The draft may propose **at most one comment per approved issue**, e.g.
|
|
71
|
+
`Reporter note: <demoted detail>`. Overflow test: the Idea section keeps
|
|
72
|
+
exactly one attributed sketch line; any demoted material left over after
|
|
73
|
+
that line is overflow, and the overflow is the comment body (long pasted
|
|
74
|
+
code, verbose repro trails, a full proposed diff are the typical shapes).
|
|
75
|
+
No overflow, no comment - unless the user asks in-session, in which case
|
|
76
|
+
the user-named content is the comment body and stands in for overflow.
|
|
77
|
+
Default off: overrides cannot force a comment absent overflow or an
|
|
78
|
+
in-session ask. In repair mode an existing `Reporter note:` comment
|
|
79
|
+
satisfies the overflow (skip), unless the demoted detail materially changed, in which case a new comment
|
|
80
|
+
is proposed - comments are never edited. In a split, the comment attaches
|
|
81
|
+
only to the issue that retained the demoted Idea material; if none does,
|
|
82
|
+
it is omitted.
|
|
83
|
+
|
|
70
84
|
### 5 - Deterministic gates
|
|
71
85
|
|
|
72
86
|
Before any subagent dispatch, inline and cheap: AC integrity gate, evidence gate, metadata audit, split detection (all below).
|
|
73
87
|
|
|
74
88
|
### 6 - No-op check
|
|
75
89
|
|
|
76
|
-
If the body already conforms AND the metadata audit is clean: report "conforms, no changes proposed" and stop. No write, no confirmation prompt, no roast. A conforming ticket never pays for a dispatch.
|
|
90
|
+
If the body already conforms AND the metadata audit is clean AND no Reporter note is proposed: report "conforms, no changes proposed" and stop. No write, no confirmation prompt, no roast. A conforming ticket never pays for a dispatch - a conforming body with pending overflow still reaches the gate.
|
|
77
91
|
|
|
78
92
|
### 7 - Roast
|
|
79
93
|
|
|
@@ -88,13 +102,14 @@ Present, per proposed issue:
|
|
|
88
102
|
- Metadata changeset: `field: current -> proposed -- why` lines.
|
|
89
103
|
- Evidence list.
|
|
90
104
|
- Roast dispositions: applied / surfaced-ambiguous.
|
|
91
|
-
- 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
|
+
- Proposed Reporter-note comment, verbatim, if any - one approval covers title + body + metadata + comment; no affirmative on the exact text = no comment.
|
|
92
107
|
|
|
93
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.**
|
|
94
109
|
|
|
95
110
|
### 9 - Write
|
|
96
111
|
|
|
97
|
-
Immediately before mutation - after approval, not at gate-open. Repair mode: re-fetch the ticket and diff against the exact snapshot the user approved (use tracker version/ETag preconditions where the CLI exposes them); any mismatch (human edited mid-review) loops back to step 8 with the delta. Create mode: no ticket exists yet, so there is nothing to re-fetch or diff - write the approved body as-new. Then one batched write per approved issue; a split is N gated writes under the one approval, honoring subset selection. Mid-batch failure: report exactly what landed; the remaining changeset is preserved for retry.
|
|
112
|
+
Immediately before mutation - after approval, not at gate-open. Repair mode: re-fetch the ticket and diff against the exact snapshot the user approved (use tracker version/ETag preconditions where the CLI exposes them); any mismatch (human edited mid-review) loops back to step 8 with the delta. Create mode: no ticket exists yet, so there is nothing to re-fetch or diff - write the approved body as-new. Then one batched write per approved issue, body first, then the approved Reporter-note comment if any; a split is N gated writes under the one approval, honoring subset selection. Mid-batch failure: report exactly what landed; the remaining changeset is preserved for retry.
|
|
98
113
|
|
|
99
114
|
## AC integrity gate
|
|
100
115
|
|
|
@@ -137,9 +152,19 @@ The gate never bans discovery; it forces discovery to be its own honestly-labele
|
|
|
137
152
|
|
|
138
153
|
### Split rule
|
|
139
154
|
|
|
140
|
-
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
|
+
|
|
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.
|
|
141
166
|
|
|
142
|
-
**
|
|
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.
|
|
143
168
|
|
|
144
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.
|
|
145
170
|
|
|
@@ -148,12 +173,23 @@ Detected during the audit: independent deliverables that form separately shippab
|
|
|
148
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.
|
|
149
174
|
|
|
150
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.
|
|
151
|
-
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.
|
|
152
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.
|
|
153
|
-
4. **Brief covers
|
|
154
|
-
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 a tracker comment, and re-run the deterministic gates (pipeline step 5).
|
|
155
|
-
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
|
|
156
|
-
7. Roast
|
|
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.
|
|
181
|
+
7. **Roast failure and retry.** A roast has failed when the dispatch
|
|
182
|
+
errored, or the artifact the parent reads - the chair synthesis
|
|
183
|
+
(council path) or the worker output (worker path) - is missing, empty,
|
|
184
|
+
or not findings-shaped; partial member loss with a usable chair
|
|
185
|
+
synthesis is success, not failure. On failure, retry once: re-run the
|
|
186
|
+
same full configured dispatch with fresh temp artifacts. If the retry
|
|
187
|
+
also fails, proceed to the confirmation gate with the failure rendered
|
|
188
|
+
inline in the gate message itself: `roast unavailable (dispatch failed
|
|
189
|
+
twice: <reason>)` - the human approves knowing review didn't run. The
|
|
190
|
+
retry is a dispatch retry only - it never grants a second draft-edit
|
|
191
|
+
re-pass (the one-re-pass limit is unchanged). **Roast failure never
|
|
192
|
+
blocks the run.**
|
|
157
193
|
|
|
158
194
|
## Tracker abstraction and capability ladder
|
|
159
195
|
|
|
@@ -174,10 +210,11 @@ Auth failure at detection time makes that rung dead; continue down the ladder (u
|
|
|
174
210
|
| search (dup/reversal) | `gh search issues` / `gh issue list --search` (incl. `state:closed`) | `linearis issues search <query>` |
|
|
175
211
|
| create | `gh issue create --title --body [--label]` | `linearis issues create <title> --description ... --team <team>` |
|
|
176
212
|
| update | `gh issue edit <n> --title --body [--add-label/--remove-label]` | `linearis issues update <id> --title ... --description ...` |
|
|
213
|
+
| post comment (Reporter note only) | `gh issue comment <n> --body ...` | `linearis issues discuss <id> --body ...` |
|
|
177
214
|
|
|
178
215
|
linearis create requires `--team <team>`; it resolves like any other metadata field - named by repo docs/overrides, else asked - never invented.
|
|
179
216
|
|
|
180
|
-
**Tracker-agnostic contract** required of whatever resolves: read the full ticket incl. comments; write title + body + metadata; search (dup/reversal check); tracker-native reference form for links. Field names, states, and taxonomies come from steps 1-2 of the ladder, never hardcoded in this skill.
|
|
217
|
+
**Tracker-agnostic contract** required of whatever resolves: read the full ticket incl. comments; write title + body + metadata; post a comment; search (dup/reversal check); tracker-native reference form for links. Field names, states, and taxonomies come from steps 1-2 of the ladder, never hardcoded in this skill. If an approved Reporter-note comment has no resolvable post-comment verb, the body write still proceeds; the comment text is emitted for manual posting and reported as not performed - never silently dropped.
|
|
181
218
|
|
|
182
219
|
**States:** generalized routing - ready (gates pass), not-ready/triage-equivalent (parked, blocker named). **Zero-config GitHub park fallback:** GitHub Issues have no native not-ready state, and this skill never invents labels - so parking writes no state/label; instead the blocker is recorded in the body (a `Blocked on: <missing value / external input>` line under the ACs), and the run report states the ticket is parked-by-convention. A repo-documented triage/not-ready label or status overrides this. Never auto-assign to an active cycle/sprint unless asked.
|
|
183
220
|
|
|
@@ -237,7 +274,7 @@ Read this when applying the AC integrity gate (drafting, repairing, or adjudicat
|
|
|
237
274
|
| "Skip evidence, the change is trivial" | Evidence is what lets someone other than the author tick the box |
|
|
238
275
|
| "The reporter's fix IS the ticket" | The fix is a sketch in Idea; the ticket is the observable outcome |
|
|
239
276
|
| "Park it quietly so the gate passes" | Parking without naming the missing value hides the defect it exists to surface |
|
|
240
|
-
| "Split by layer to keep tickets small" | Layers are not deliverables -
|
|
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 |
|
|
241
278
|
| "Write it now, the human said it twice" | Repetition is not confirmation - the gate needs an explicit yes on the presented diff |
|
|
242
279
|
|
|
243
280
|
## Edge cases
|
|
@@ -245,11 +282,11 @@ Read this when applying the AC integrity gate (drafting, repairing, or adjudicat
|
|
|
245
282
|
- Unreadable ticket -> abort repair, offer create mode.
|
|
246
283
|
- Ticket changed between gather and write -> re-fetch, diff, re-present, re-ask.
|
|
247
284
|
- Write fails mid-batch -> report exactly what landed; remaining changeset preserved for retry.
|
|
248
|
-
- Conforming ticket -> no-op verdict (requires metadata audit also clean), stop.
|
|
285
|
+
- Conforming ticket -> no-op verdict (requires metadata audit also clean and no Reporter note proposed), stop.
|
|
249
286
|
- Headless run -> stops at the confirmation gate.
|
|
250
287
|
- Ref with no fetch path -> ask; never guess.
|
|
251
288
|
- Split declined -> single ticket with phased AC groups.
|
|
252
|
-
- Roast
|
|
289
|
+
- Roast failure -> retry once; second failure -> gate with `roast unavailable (dispatch failed twice: <reason>)` inline.
|
|
253
290
|
|
|
254
291
|
## Red flags - STOP
|
|
255
292
|
|
|
@@ -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
|
|
72
|
+
Before writing the plan, check the spec one more time:
|
|
73
73
|
|
|
74
|
-
- Does
|
|
75
|
-
-
|
|
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.
|
|
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
|
|