@gobing-ai/spur 0.3.97 → 0.3.99
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/.claude-plugin/marketplace.json +1 -1
- package/config/plugin-scripts.json +5 -0
- package/config/templates/task/brainstorm.md +1 -1
- package/config/templates/task/feature-impl.md +1 -1
- package/config/templates/task/issue.md +1 -1
- package/config/templates/task/meta.md +1 -1
- package/config/templates/task/review.md +1 -1
- package/config/templates/task/standard.md +1 -1
- package/package.json +9 -9
- package/plugins/sp/README.md +9 -9
- package/plugins/sp/lib/idea-handoff.generated.mjs +37 -4
- package/plugins/sp/lib/inline-run.generated.mjs +54 -14
- package/plugins/sp/lib/residual-scan.generated.d.mts +5 -0
- package/plugins/sp/lib/residual-scan.generated.mjs +45 -5
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/references/roles.md +3 -3
- package/plugins/sp/scripts/inline-run-setup.mjs +4 -2
- package/plugins/sp/scripts/inline-run-setup.ts +12 -12
- package/plugins/sp/scripts/persist-out-check.mjs +164 -0
- package/plugins/sp/scripts/persist-out-check.ts +185 -0
- package/plugins/sp/scripts/residual-scan.mjs +50 -8
- package/plugins/sp/scripts/residual-scan.ts +4 -3
- package/plugins/sp/scripts/task-diffstat.mjs +17 -5
- package/plugins/sp/scripts/task-diffstat.ts +25 -4
- package/plugins/sp/skills/code-implementation/SKILL.md +1 -1
- package/plugins/sp/skills/code-verification/SKILL.md +3 -0
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +17 -1
- package/plugins/sp/skills/conflict-finding/SKILL.md +1 -1
- package/plugins/sp/skills/conflict-finding/references/authority-resolution.md +5 -3
- package/plugins/sp/skills/conflict-finding/references/remediation-routing.md +9 -8
- package/plugins/sp/skills/doc-evolve/SKILL.md +4 -3
- package/plugins/sp/skills/spur-cli/references/tasks/section-editing.md +7 -4
- package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +7 -4
- package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +40 -7
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +16 -13
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +217 -38
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +3 -1
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +7 -8
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +110 -3
- package/spur.js +2142 -637
- package/web/_astro/{BoardApp.vveLOdDq.js → BoardApp.BVp-LYi6.js} +101 -101
- package/web/_astro/{BoardApp.Bmv5WkJ9.js → BoardApp.i4eh_fHA.js} +1 -1
- package/web/_astro/{TaskDetail.IKc3uDYV.js → TaskDetail.C5Cu8LdB.js} +1 -1
- package/web/_astro/{arc.C63Ufg35.js → arc.Bu5XpBEz.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.CP8Hyldk.js → architectureDiagram-3BPJPVTR.9vHQTvhF.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.Bo_48MRA.js → blockDiagram-GPEHLZMM.Cn2Xl7D6.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.Ch61sw-O.js → c4Diagram-AAUBKEIU.7o0P_QDo.js} +1 -1
- package/web/_astro/channel.6IFIKBhu.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.CHpzS7xf.js → chunk-2J33WTMH.C3zZFsNt.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.DzMNNWIw.js → chunk-4BX2VUAB.CNy_M7fO.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.BqPrDw_Q.js → chunk-55IACEB6.2TiPnLBL.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.ZzBHhNw-.js → chunk-727SXJPM.5CGSyKAp.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.1_O_LLkx.js → chunk-AQP2D5EJ.w-ZIgfsK.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.GS4UHg7_.js → chunk-FMBD7UC4.BkcEyhKa.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.BnqotN4g.js → chunk-ND2GUHAM.mxKKbBG4.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.Ghuh8zoJ.js → chunk-QZHKN3VN.cyA0z9aT.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.BW9vC3zg.js → classDiagram-4FO5ZUOK.CqtUzwhX.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.BW9vC3zg.js → classDiagram-v2-Q7XG4LA2.CqtUzwhX.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.D6jNC0ti.js → cose-bilkent-S5V4N54A.BExPTopI.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.BOGoCs4L.js → cynefin-OW5HDTMX.Bq2i10Cn.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.8iU5VcWi.js → dagre-BM42HDAG.By_HsOuv.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.CVt88HwH.js → diagram-2AECGRRQ.BtU0b7hz.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.C9HxUUMl.js → diagram-5GNKFQAL.D00ztMdT.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.4ywkQN3t.js → diagram-KO2AKTUF.DMVJWaUN.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.C48A6gpz.js → diagram-LMA3HP47.Cxqrjg5-.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.DxFieQW4.js → diagram-OG6HWLK6.2lz_1B5h.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.CtjtQqYs.js → erDiagram-TEJ5UH35.BVQEuJ3T.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.C5zAiahU.js → flowDiagram-I6XJVG4X.Qlyo90ky.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.D_L6zsny.js → ganttDiagram-6RSMTGT7.DRwb8HHZ.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.Ignnrk3V.js → gitGraphDiagram-PVQCEYII.YfoPthfS.js} +1 -1
- package/web/_astro/{infoDiagram-5YYISTIA.DLv8-P5u.js → infoDiagram-5YYISTIA.DvFREck1.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.p0OTpwFO.js → ishikawaDiagram-YF4QCWOH.BzCj9Q0-.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.CIaBt-DD.js → journeyDiagram-JHISSGLW.BDMT_AFb.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.DGySIntq.js → kanban-definition-UN3LZRKU.D6Fh3gj9.js} +1 -1
- package/web/_astro/{linear.BATV4RXu.js → linear.Dxw00fGQ.js} +1 -1
- package/web/_astro/{mermaid.core.DzkwM3VX.js → mermaid.core.CsoxUWa0.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.3piBRRzA.js → mindmap-definition-RKZ34NQL.CN8oeHLu.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.DUNueLub.js → pieDiagram-4H26LBE5.C7ADqQ_7.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.9bihVbrt.js → quadrantDiagram-W4KKPZXB.Cw9lKyHP.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.CkjsC-YT.js → requirementDiagram-4Y6WPE33.B1ni-z4q.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.Dbq4vaoQ.js → sankeyDiagram-5OEKKPKP.BhexdjVr.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.BivrH99P.js → sequenceDiagram-3UESZ5HK.C3Rm7dpQ.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.CqLQ1Vzz.js → stateDiagram-AJRCARHV.CkuMt27u.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.DvvoLD3b.js → stateDiagram-v2-BHNVJYJU.Dc_aazUA.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.B-KR78gv.js → timeline-definition-PNZ67QCA.ja7sUFhH.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.Ck0Eieb5.js → vennDiagram-CIIHVFJN.BxIshX_K.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.CRfdd_do.js → wardleyDiagram-YWT4CUSO.DfHlC0sh.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.Ck_Jxk9C.js → xychartDiagram-2RQKCTM6.DSnhHwCh.js} +1 -1
- package/web/index.html +1 -1
- package/web/_astro/channel.BKCmJpWM.js +0 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
// plugins/sp/scripts/task-diffstat.ts
|
|
5
5
|
import { spawnSync } from "child_process";
|
|
6
|
-
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
6
|
+
import { fstatSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "fs";
|
|
7
7
|
import { join } from "path";
|
|
8
8
|
|
|
9
9
|
// plugins/sp/lib/env.ts
|
|
@@ -85,10 +85,14 @@ function runDiffstat(env, options = {}, git = defaultGitRunner(options.cwd)) {
|
|
|
85
85
|
process.stderr.write(`task-diffstat: ${reason} \u2014 failing safe (sensitive)
|
|
86
86
|
`);
|
|
87
87
|
const row2 = { files: 0, insertions: 0, deletions: 0, paths: [], sensitive: true };
|
|
88
|
-
|
|
89
|
-
`);
|
|
88
|
+
writeArtifact(abs(relResult), row2);
|
|
90
89
|
return { ...row2, resultFile: relResult, exitCode: 0 };
|
|
91
90
|
};
|
|
91
|
+
let stdoutIsArtifact = false;
|
|
92
|
+
try {
|
|
93
|
+
const [out, art] = [fstatSync(1), statSync(abs(relResult))];
|
|
94
|
+
stdoutIsArtifact = out.dev === art.dev && out.ino === art.ino;
|
|
95
|
+
} catch {}
|
|
92
96
|
let base = "";
|
|
93
97
|
try {
|
|
94
98
|
base = readFileSync(abs(join(relRun, `${wbs}-base.sha`)), "utf8").trim();
|
|
@@ -125,9 +129,17 @@ function runDiffstat(env, options = {}, git = defaultGitRunner(options.cwd)) {
|
|
|
125
129
|
const all = [...paths].filter((p) => p.length > 0).sort();
|
|
126
130
|
const sensitive = all.some((p) => sensitiveReasonForPath(p) !== null);
|
|
127
131
|
const row = { files: all.length, insertions, deletions, paths: all, sensitive };
|
|
128
|
-
|
|
132
|
+
writeArtifact(abs(relResult), row);
|
|
133
|
+
if (stdoutIsArtifact)
|
|
134
|
+
process.stderr.write(`task-diffstat: stdout redirected onto the artifact \u2014 kept; redirect stdout elsewhere
|
|
135
|
+
`);
|
|
136
|
+
return { ...row, resultFile: relResult, exitCode: stdoutIsArtifact ? 1 : 0 };
|
|
137
|
+
}
|
|
138
|
+
function writeArtifact(absPath, row) {
|
|
139
|
+
const tmp = `${absPath}.tmp`;
|
|
140
|
+
writeFileSync(tmp, `${JSON.stringify(row)}
|
|
129
141
|
`);
|
|
130
|
-
|
|
142
|
+
renameSync(tmp, absPath);
|
|
131
143
|
}
|
|
132
144
|
var TASK_DIFFSTAT_USAGE = "usage: task-diffstat (env: wbs)";
|
|
133
145
|
function main(argv, env = getEnvVars(), options = {}) {
|
|
@@ -19,13 +19,17 @@
|
|
|
19
19
|
* other probe problem writes `sensitive: true` (and exits 0) so the lane degrades to
|
|
20
20
|
* safety, never to fast.
|
|
21
21
|
*
|
|
22
|
+
* The artifact is written atomically (tmp + rename), so redirecting stdout onto the artifact path
|
|
23
|
+
* cannot corrupt it — the rename swaps in the real JSON after the shell's redirect truncates the
|
|
24
|
+
* old inode (1067 R3); that redirect is still refused with a nonzero exit. Normal output is silent.
|
|
25
|
+
*
|
|
22
26
|
* Node-builtin imports only; the git lookup is a spawnable `GitRunner` so tests can fake
|
|
23
27
|
* it; `main(argv, env, options)` is the same injection point the workflow wrapper and its
|
|
24
28
|
* node twin exercise. Only an empty `wbs` (mis-invocation) exits nonzero.
|
|
25
29
|
*/
|
|
26
30
|
|
|
27
31
|
import { spawnSync } from 'node:child_process';
|
|
28
|
-
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
32
|
+
import { fstatSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from 'node:fs';
|
|
29
33
|
import { join } from 'node:path';
|
|
30
34
|
import { getEnvVars } from '../lib/env';
|
|
31
35
|
|
|
@@ -166,10 +170,18 @@ export function runDiffstat(
|
|
|
166
170
|
const failSafe = (reason: string): DiffstatResult => {
|
|
167
171
|
process.stderr.write(`task-diffstat: ${reason} — failing safe (sensitive)\n`);
|
|
168
172
|
const row: Diffstat = { files: 0, insertions: 0, deletions: 0, paths: [], sensitive: true };
|
|
169
|
-
|
|
173
|
+
writeArtifact(abs(relResult), row);
|
|
170
174
|
return { ...row, resultFile: relResult, exitCode: 0 };
|
|
171
175
|
};
|
|
172
176
|
|
|
177
|
+
let stdoutIsArtifact = false; // 1067 R3: stdout redirected onto the artifact itself clobbered it once
|
|
178
|
+
try {
|
|
179
|
+
const [out, art] = [fstatSync(1), statSync(abs(relResult))];
|
|
180
|
+
stdoutIsArtifact = out.dev === art.dev && out.ino === art.ino;
|
|
181
|
+
} catch {
|
|
182
|
+
/* artifact absent or stdout not a regular file — nothing to compare */
|
|
183
|
+
}
|
|
184
|
+
|
|
173
185
|
let base = '';
|
|
174
186
|
try {
|
|
175
187
|
base = readFileSync(abs(join(relRun, `${wbs}-base.sha`)), 'utf8').trim();
|
|
@@ -203,8 +215,17 @@ export function runDiffstat(
|
|
|
203
215
|
const all = [...paths].filter((p) => p.length > 0).sort();
|
|
204
216
|
const sensitive = all.some((p) => sensitiveReasonForPath(p) !== null);
|
|
205
217
|
const row: Diffstat = { files: all.length, insertions, deletions, paths: all, sensitive };
|
|
206
|
-
|
|
207
|
-
|
|
218
|
+
writeArtifact(abs(relResult), row);
|
|
219
|
+
if (stdoutIsArtifact)
|
|
220
|
+
process.stderr.write('task-diffstat: stdout redirected onto the artifact — kept; redirect stdout elsewhere\n');
|
|
221
|
+
return { ...row, resultFile: relResult, exitCode: stdoutIsArtifact ? 1 : 0 };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Atomic artifact write (tmp + rename): a stdout redirect onto the path cannot interleave (1067 R3). */
|
|
225
|
+
function writeArtifact(absPath: string, row: Diffstat): void {
|
|
226
|
+
const tmp = `${absPath}.tmp`;
|
|
227
|
+
writeFileSync(tmp, `${JSON.stringify(row)}\n`);
|
|
228
|
+
renameSync(tmp, absPath);
|
|
208
229
|
}
|
|
209
230
|
|
|
210
231
|
export const TASK_DIFFSTAT_USAGE = 'usage: task-diffstat (env: wbs)';
|
|
@@ -184,7 +184,7 @@ reproduce → isolate → minimal fix → regression guard.
|
|
|
184
184
|
|
|
185
185
|
- Writing code without having read the immediate callers of what you're changing.
|
|
186
186
|
- A `## Solution` section that lists files but not what changed or why.
|
|
187
|
-
- A `## Solution` section with file references that are not in backtick `` `path:line` `` form (L3 requirement: `` `packages/app/src/foo.ts:123` `` or `` `packages/app/src/bar.ts:10-20` ``; paths from repo root).
|
|
187
|
+
- A `## Solution` section with file references that are not in backtick `` `path:line` `` form (L3 requirement: `` `packages/app/src/foo.ts:123` `` or `` `packages/app/src/bar.ts:10-20` ``; paths from repo root). Non-repo paths (external packages, `@scope/...`) can never resolve repo-relative — cite them in the frozen external-evidence form instead: `Evidence: @gobing-ai/ts-x \`src/persistence.ts\` line 108` (origin and line number OUTSIDE the backticks). Wrong: `` `@gobing-ai/ts-x/src/persistence.ts:108` `` — the L4 gate fails it as an unresolvable anchor (task 1055).
|
|
188
188
|
- A new abstraction with exactly one caller and no second use in sight.
|
|
189
189
|
- The diff touches files unrelated to the task's scope.
|
|
190
190
|
- The diff touches another WBS's surfaces, or the pass reads sibling `todo` / `wip` task files.
|
|
@@ -336,6 +336,7 @@ Verdict: PASS (or PARTIAL / FAIL)
|
|
|
336
336
|
|
|
337
337
|
```markdown
|
|
338
338
|
Verdict: PASS
|
|
339
|
+
Confidence: HIGH
|
|
339
340
|
|
|
340
341
|
### Per-Requirement Traceability
|
|
341
342
|
| Req | Status | Evidence |
|
|
@@ -354,6 +355,8 @@ Verdict: PASS
|
|
|
354
355
|
```
|
|
355
356
|
|
|
356
357
|
The per-requirement traceability table MUST use `| Req | Status | Evidence |` (exactly this header, no `R#`/`R`/`Requirement` variant — `Status` in column 2 — and no extra columns between Req and Status). The Acceptance Criteria table MUST use `| AC | Status | Evidence Type | Evidence |`.
|
|
358
|
+
The answer MUST carry exactly one `Confidence: HIGH|MEDIUM|LOW` line (task 1068): the verifier's stated confidence in the verdict — HIGH = fresh executable evidence + re-read anchors this run; MEDIUM = partial reliance on prior evidence, external/delegated outcomes, or an unstable tree; LOW = key evidence not re-executed. A missing or out-of-vocabulary line fails lint (`confidence-missing` / `confidence-value`) and blocks derivation; the value lands in the verdict artifact's `confidence` field and the recorded `## Testing` section.
|
|
359
|
+
The answer MUST cite a source for external API/library claims in Requirement/AC evidence cells: a cell asserting a scoped-package (`@scope/name`), `node_modules/`, semver-version (`x.y.z`), or backticked function-call (`` `name(args)` ``) claim is rejected with `evidence-citation` unless the SAME cell carries a citation — a repo `path:line(-end)` (backticked or bare), the external named-origin form (@origin `path` line N), or a URL. Cross-cell references ("receipt above") do not count; marker-free receipts ("bun test 398 pass / 0 fail") need no citation.
|
|
357
360
|
**MUST NOT:** place a `Severity` column between `Req` and `Status` in the authoring contract.
|
|
358
361
|
The parser is tolerant of these variants (defense-in-depth), but the authoring contract is
|
|
359
362
|
canonical.
|
|
@@ -115,7 +115,9 @@ AC ids that do not resolve to one accepted identity — a task AC checklist labe
|
|
|
115
115
|
(exact/bare title, `Scenario:` prefix, bracket tags, `AC-N`); paraphrases and ambiguous aliases
|
|
116
116
|
fail — invalid status (`MET | PARTIAL | UNMET` for requirements;
|
|
117
117
|
`N/A` additionally allowed for AC), invalid evidence type (`test | command | static-ref |
|
|
118
|
-
manual-review | llm-judge | n/a`, or a `+` compound), and empty evidence.
|
|
118
|
+
manual-review | llm-judge | n/a`, or a `+` compound), and empty evidence. Evidence cells that assert an
|
|
119
|
+
external API/library claim without a same-cell citation are rejected with `evidence-citation` (see
|
|
120
|
+
"Source citations for external claims" below). Interrupted runs keep the
|
|
119
121
|
rows that pass the lint and complete only the missing IDs on retry.
|
|
120
122
|
|
|
121
123
|
**Feature-credited AC ids are narrower than the lint's accepted set (task 0966 finding).** A row keyed
|
|
@@ -136,6 +138,20 @@ the run actually verified. Since 0804 R9 the checker ignores complete parsed cit
|
|
|
136
138
|
scanning for subjects, so a citation's filename (including snake_case paths) can never become a
|
|
137
139
|
false subject — a real absent symbol, nonexistent file, or invalid range still reports.
|
|
138
140
|
|
|
141
|
+
## Source citations for external claims (task 1070)
|
|
142
|
+
|
|
143
|
+
A Requirement/AC evidence cell that asserts an external API/library claim must carry a verifiable
|
|
144
|
+
citation in the SAME cell, or lint rejects the row with `evidence-citation` (blocking derivation).
|
|
145
|
+
|
|
146
|
+
- Claim markers (closed set): a scoped-package mention (`@scope/name`), a `node_modules/` reference,
|
|
147
|
+
a semver version claim (`x.y.z`), or a backticked function-call-shaped token (`` `name(args)` ``).
|
|
148
|
+
- Accepted citation forms (any one clears the cell): a repo `path:line(-end)` (backticked or bare),
|
|
149
|
+
the external named-origin form (@origin `path` line N — line number outside the backticks), or an
|
|
150
|
+
http(s) URL.
|
|
151
|
+
- The rule is cell-local: a citation in another row or a "receipt above" cross-reference does not
|
|
152
|
+
satisfy it. Marker-free receipts (test counts, reviewed-log notes) need no citation. The check is
|
|
153
|
+
presence-only — which citation proves which claim is not deterministically decidable.
|
|
154
|
+
|
|
139
155
|
## Checks evidence
|
|
140
156
|
|
|
141
157
|
Wave C verification can emit the following additive `checks[]` rows:
|
|
@@ -249,7 +249,7 @@ Thin wrapper: `Skill(skill="sp:conflict-finding", args="$ARGUMENTS")`.
|
|
|
249
249
|
- Silently omitting a selected pillar or a tool failure (no coverage accounting).
|
|
250
250
|
- Claiming "comprehensive" when `coverage.complete` is false.
|
|
251
251
|
- Any mutation in audit mode, or automatic mutation merely because `--resolve` is present.
|
|
252
|
-
- Editing a derived projection before its
|
|
252
|
+
- Editing a derived projection before correcting a defective authority, or changing a correct authority merely to repair its stale projection.
|
|
253
253
|
- Adding a production analyzer, index/cache/database, dependency, or dedicated subagent in v1.
|
|
254
254
|
|
|
255
255
|
## Reference files
|
|
@@ -206,13 +206,15 @@ authority it governs:
|
|
|
206
206
|
| Owner | Repair route | Governs (claim types) |
|
|
207
207
|
|-------|--------------|----------------------|
|
|
208
208
|
| Task/feature corpus | `sp:spur-cli` / `spur task` / `spur feature`; never direct file writes | Task obligation/status, feature goal and AC |
|
|
209
|
-
| Numbered docs and AGENTS projections | `sp:doc-evolve`; authority first,
|
|
209
|
+
| Numbered docs and AGENTS projections | `sp:doc-evolve`; inspect authority first, correct it only if defective, then repair affected projections | Process rules, structural decision, product scope, architecture, command surface |
|
|
210
210
|
| Source/tests | create/use a Spur task and route through `sp:spur-dev` / build competencies | Implementation behavior, architecture mechanism |
|
|
211
211
|
| Command/skill capability source | Superskill command/skill lifecycle in the owning plugin source | Command/API/schema surface |
|
|
212
212
|
| Ambiguous authority | **stop** for an operator decision; do not mutate either side | (all, when unresolved) |
|
|
213
213
|
|
|
214
|
-
|
|
215
|
-
|
|
214
|
+
Inspect the governing authority before repairing a derived projection. Correct the authority first
|
|
215
|
+
only when it is defective and the correction is authorized; otherwise leave it unchanged and repair
|
|
216
|
+
the projection. The authority column of §2 remains the owner surface's source of truth; a projection
|
|
217
|
+
is brought into line with it, not the reverse.
|
|
216
218
|
|
|
217
219
|
## 7. Worked examples
|
|
218
220
|
|
|
@@ -113,13 +113,13 @@ between "the audit said X" and "the file now says Y."
|
|
|
113
113
|
Each approved repair is dispatched through the artifact's **owner surface** — the harness path that
|
|
114
114
|
owns writes to that artifact — not a direct file write. Authority for a repair is the same
|
|
115
115
|
authority model as the audit ([./authority-resolution.md](./authority-resolution.md)): for a given
|
|
116
|
-
subject + claim type, one artifact is normative
|
|
117
|
-
authority.
|
|
116
|
+
subject + claim type, one artifact is normative. Inspect it first; correct it only if defective and
|
|
117
|
+
authorized, then update affected projections. A correct authority needs no edit for a stale projection.
|
|
118
118
|
|
|
119
119
|
| Owner | Repair route | Never |
|
|
120
120
|
|-------|--------------|-------|
|
|
121
121
|
| Task/feature corpus | `sp:spur-cli` — `spur task` / `spur feature` (with `--section`, `--from-file`, `--json` as needed) | Direct file writes to task/feature files. |
|
|
122
|
-
| Numbered docs and AGENTS projections | `sp:doc-evolve` —
|
|
122
|
+
| Numbered docs and AGENTS projections | `sp:doc-evolve` — inspect authority first; correct it only if defective, then repair affected projections (see below) | Changing correct authority to repair a stale projection, or projecting a defective authority before its authorized correction. |
|
|
123
123
|
| Source / tests | Create or use a **Spur task** and route through `sp:spur-dev` / build competencies (`sp:code-implementation`, `sp:code-testing`), **unless** the active session already has explicit implementation authority for the change. | Bare in-place edits to source/tests without a task or explicit authority. |
|
|
124
124
|
| Command/skill capability source | Superskill command/skill **lifecycle** (`superskill command …` / `superskill skill …` scaffold/validate/evaluate/refine) in the owning plugin source. | Hand-editing capability source outside the Superskill lifecycle. |
|
|
125
125
|
| Ambiguous authority | **Stop for an operator decision.** Do not mutate either side. | Choosing a winner, editing both sides, or forcing a global ranking. |
|
|
@@ -148,12 +148,13 @@ item; never route around it with a direct write.
|
|
|
148
148
|
|
|
149
149
|
### 3.2 Numbered docs and AGENTS projections
|
|
150
150
|
|
|
151
|
-
Documentation repairs
|
|
151
|
+
Documentation repairs **inspect authority first**. If a derived
|
|
152
152
|
projection (`docs/04_DESIGN.md`, `docs/05_FEATURES.md`, `AGENTS.md`, templates) has drifted from its
|
|
153
153
|
authority (`docs/00_ADR.md`, `docs/01_PRD.md`, `docs/03_ARCHITECTURE.md`,
|
|
154
|
-
`docs/99_PROJECT_CONSTITUTION.md`),
|
|
155
|
-
projection via `sp:doc-evolve`.
|
|
156
|
-
|
|
154
|
+
`docs/99_PROJECT_CONSTITUTION.md`), leave a correct authority unchanged and repair the stale
|
|
155
|
+
projection via `sp:doc-evolve`. If the authority itself is defective, correct it first within the
|
|
156
|
+
approved scope, then update affected projections. Incomparable authority still requires an operator
|
|
157
|
+
decision; projection drift alone never authorizes changing the governing contract (see §6).
|
|
157
158
|
|
|
158
159
|
`sp:doc-evolve` owns the numbered-doc/projection lifecycle: sync checks, contract verification,
|
|
159
160
|
frontmatter checks, and derived-doc refresh. A doc repair is dispatched to it; it is not hand-applied.
|
|
@@ -244,7 +245,7 @@ These are the explicit boundaries for remediation. Each is a hard violation, not
|
|
|
244
245
|
| Anti-pattern | Why it is prohibited |
|
|
245
246
|
|--------------|----------------------|
|
|
246
247
|
| **Automatic mutation merely because `--resolve` is present** | `--resolve` only opens the workflow; it never authorizes automatic repair. Confirmation + freshness + owner surface are all required. |
|
|
247
|
-
| **
|
|
248
|
+
| **Changing correct authority for projection drift** | A stale projection is repaired against its governing contract. Correct authority only when defective and authorized; then update affected projections. |
|
|
248
249
|
| **Broad cleanup outside confirmed findings** | Only the artifacts named by confirmed, freshness-checked findings are touched. No opportunistic refactors or doc "improvements" while repairing. |
|
|
249
250
|
| **Ambiguity silently resolved** | Ambiguous/missing authority stops for an operator decision; it is never forced through a global ranking or a guessed owner. |
|
|
250
251
|
| **Partial failure silently continued** | A failed repair is reported, not papered over; siblings are not marked successful and the run does not roll forward silently. |
|
|
@@ -100,9 +100,10 @@ rg --files apps packages # real modules
|
|
|
100
100
|
git log -1 --format='%ci' -- docs/04_DESIGN.md # last touch vs. recent surface changes
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-
**Repair protocol (§7):**
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
**Repair protocol (§7):** inspect the governing statement first. Correct it only if defective and
|
|
104
|
+
authorized, then update affected detail/index/entry files; otherwise leave the authority unchanged
|
|
105
|
+
and repair its stale projections. Preserve ADR numbers, original titles/dates and decision history;
|
|
106
|
+
editorial condensation follows §6.1, while actual reversals need a superseding decision. Record findings in a task/report.
|
|
106
107
|
Routine lessons go to existing learning/context storage (§8); no automatic constitution edits.
|
|
107
108
|
|
|
108
109
|
Output a **drift report**: per finding, `{ doc, what code says, what the doc says, authority, repair
|
|
@@ -42,13 +42,16 @@ for `Plan`, `Acceptance Criteria`, hand-authored `Solution`, and any narrative s
|
|
|
42
42
|
spur task update 0040 --section Review --from-file /tmp/review.md
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
3. The whole `### Review` body is now that file's contents. To amend rather than overwrite,
|
|
46
|
-
the current body (`spur task show 0040`), edit the temp file to the full desired state, and
|
|
47
|
-
replace again —
|
|
45
|
+
3. The whole `### Review` body is now that file's contents. To amend rather than overwrite, either
|
|
46
|
+
read the current body (`spur task show 0040`), edit the temp file to the full desired state, and
|
|
47
|
+
replace again — or pass `--append` (task 1057) to write the file's body after the existing
|
|
48
|
+
section content (existing content + one blank line + new body; surrounding whitespace trimmed; an empty/missing section is
|
|
49
|
+
created). `--append` requires `--section` and never replaces on its own.
|
|
48
50
|
|
|
49
51
|
**`Q&A` is the exception.** `--section "Q&A"` APPENDS a timestamped `#### Q&A entry — <ISO>`
|
|
50
52
|
block rather than replacing the section. Start the body with `<!-- qa:replace -->` to replace it
|
|
51
|
-
wholesale.
|
|
53
|
+
wholesale. Explicit `--append` on `Q&A` uses the generic append (no timestamped wrapper); the
|
|
54
|
+
`<!-- qa:replace -->` marker still wins and forces wholesale replace.
|
|
52
55
|
|
|
53
56
|
`--section` **requires** `--from-file` (exit `2` otherwise). Section names match the DD-08 headings
|
|
54
57
|
exactly: `Background`, `Requirements`, `Acceptance Criteria`, `Q&A`, `Design`, `Plan`, `Solution`, `Root Cause`, `Testing`, `Review`, `References`, `History`, `Notes` (universal sections are `History`, `References`, `Notes`; `Root Cause` is carried by the `issue` template variant).
|
|
@@ -70,9 +70,12 @@ frontmatter scalar.
|
|
|
70
70
|
`testing→done` `check` gates above still run; the CLI evaluates them inline when the FSM guard
|
|
71
71
|
does not. `--force-done` waives the verify **verdict** only, never the section matrix.
|
|
72
72
|
- **Section** (`--section` **requires** `--from-file`): writes the named section body from the file.
|
|
73
|
-
Most sections replace wholesale.
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
Most sections replace wholesale. `--append` extends instead: existing body + one blank line + the
|
|
74
|
+
file's body (task 1057; empty/missing section is created; still requires `--section`). Exception:
|
|
75
|
+
`--section "Q&A"` APPENDS a timestamped `#### Q&A entry — <ISO>` block; start the body with
|
|
76
|
+
`<!-- qa:replace -->` to replace the section wholesale (explicit `--append` on `Q&A` uses the
|
|
77
|
+
generic append, marker still wins). No inline-body flag. Section names (case-sensitive):
|
|
78
|
+
`Background`, `Requirements`, `Acceptance Criteria`, `Q&A`, `Design`, `Plan`, `Solution`, `Testing`, `Review`, `References`, `History`, `Notes`.
|
|
76
79
|
- **Frontmatter** (`--feature <id>`, `--priority <p>`): sets the scalar frontmatter field on an
|
|
77
80
|
existing task — the only post-create path, allow-listed to `feature_id` / `parent_wbs` / `priority`.
|
|
78
81
|
- **Estimate** (`--estimate-hours <n>`): sets the `estimate_hours` frontmatter field (positive number) —
|
|
@@ -113,7 +116,7 @@ Flags: `--folder <path>`, `--json`. Exit codes: `0` success, `1` error, `2` usag
|
|
|
113
116
|
|
|
114
117
|
## `sections <wbs> <op> [name]`
|
|
115
118
|
|
|
116
|
-
CLI-safe, matrix-enforced task section mutation. Section names are validated against canonical sections (`Background`, `Requirements`, `Acceptance Criteria`, `Q&A`, `Design`, `Plan`, `Solution`, `Root Cause`, `Testing`, `Review`, `References`, `History`, `Notes`). Universal sections (`History`, `References`, `Notes`) are always allowed; `Root Cause` is carried by the `issue` template variant. `Q&A` on `update --section` appends rather than replacing (see the Section bullet above); `<!-- qa:replace -->` forces a wholesale replace.
|
|
119
|
+
CLI-safe, matrix-enforced task section mutation. Section names are validated against canonical sections (`Background`, `Requirements`, `Acceptance Criteria`, `Q&A`, `Design`, `Plan`, `Solution`, `Root Cause`, `Testing`, `Review`, `References`, `History`, `Notes`). Universal sections (`History`, `References`, `Notes`) are always allowed; `Root Cause` is carried by the `issue` template variant. `Q&A` on `update --section` appends rather than replacing (see the Section bullet above); `<!-- qa:replace -->` forces a wholesale replace. `--append` is the generic extend-after-existing path (task 1057).
|
|
117
120
|
|
|
118
121
|
| Op | Usage | Description |
|
|
119
122
|
| --- | --- | --- |
|
|
@@ -30,10 +30,28 @@ Rules:
|
|
|
30
30
|
## Task-side numbering (`AC<n>`)
|
|
31
31
|
|
|
32
32
|
`R<n>` is the **feature** scenario key and the **task Requirements** key. Task `### Acceptance
|
|
33
|
-
Criteria` items therefore use their own namespace:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
Criteria` items therefore use their own namespace: `AC1, AC2, …`. Two frontmatter fields split the
|
|
34
|
+
gate work, and neither implies the other:
|
|
35
|
+
|
|
36
|
+
- **`ac_altitude`** owns the **feature subset** (DD-09). `graduating` (or absent) keeps
|
|
37
|
+
normalized-title matching against the linked feature (`normalizeTitle` strips both `AC<n>` and
|
|
38
|
+
`R<n>` prefixes, so copied titles match); `task-local` skips that check — and nothing else.
|
|
39
|
+
- **`ac_numbering: task-local`** owns **task requirement bindings**. It opts the AC section into the
|
|
40
|
+
`L3.ac-requirement-coverage` loop, which reads **`Scenario:` titles only**: bind requirements with
|
|
41
|
+
`(req: R1; R2)` in the title, or ship a legacy `Scenario: R1 — …` title, which still binds by its
|
|
42
|
+
own prefix.
|
|
43
|
+
|
|
44
|
+
**Preferred task-local form** (regression/fix tasks): `Scenario: AC1 — <concrete outcome> (req: R1)`
|
|
45
|
+
with Given/When/Then, declaring **both** `ac_altitude: task-local` and `ac_numbering: task-local`.
|
|
46
|
+
|
|
47
|
+
Parsed checkbox rows (`- [ ] AC1 — <title>`) are a supported form: they join checklist parsing, the
|
|
48
|
+
feature-subset match, and the terminal-status unchecked-box count — but they do **not** enter the
|
|
49
|
+
Scenario-only requirement-binding loop, so a checkbox row never substitutes for a `(req: …)` binding.
|
|
50
|
+
|
|
51
|
+
Raw freeform bullets (`- AC1 — …`: no checkbox, no `Scenario:`) parse as nothing — no subset match,
|
|
52
|
+
no requirement binding, no box counting. They survive in the corpus as **legacy unparsed records**:
|
|
53
|
+
read them, don't author them, and don't reach for them to dodge traceability. Completed tasks keep
|
|
54
|
+
their records; new work uses one of the two parsed forms above.
|
|
37
55
|
|
|
38
56
|
## Two AC tiers (authoring convention)
|
|
39
57
|
|
|
@@ -145,9 +163,11 @@ The `spur task verdict` answer lint declares the bold span of a single-line crit
|
|
|
145
163
|
(`- **AC2 — The roster runtime is gone (R3).** Given …, when …, then …`) and its head before the
|
|
146
164
|
first ` — ` or `:` — so answer rows may key `AC2` or `AC2 — The roster runtime is gone (R3).`. Keep
|
|
147
165
|
at least one answer row keyed to the verbatim feature scenario title the task graduates, and keep
|
|
148
|
-
requirement ids in the corpus form
|
|
149
|
-
|
|
150
|
-
|
|
166
|
+
requirement ids in the template-taught corpus form, `- [ ] R1. <text>`: that (or an unbolded `R1.`
|
|
167
|
+
variant) is what the task-local binding loop parses — bold wrapping (`**R1**`) passes the
|
|
168
|
+
`L3.requirements-format` ratio but binds nothing — and a colon after the bold span still silently
|
|
169
|
+
drops the whole section to a warning. Bold AC heads are likewise a verdict-table id form, not the AC
|
|
170
|
+
authoring form: author new AC rows in the plain `- [ ] AC1 —` / `Scenario: AC1 —` shapes.
|
|
151
171
|
|
|
152
172
|
### The id is exactly the scenario title — no Gherkin body appended
|
|
153
173
|
|
|
@@ -276,3 +296,16 @@ Use `task-local` for a fix/refactor task whose regression criteria are not the f
|
|
|
276
296
|
contract. Keep a `graduating` task's scenario titles identical to the feature's so DD-09 stays
|
|
277
297
|
satisfied. Absent-altitude is `graduating` — set `task-local` explicitly only where the subset rule
|
|
278
298
|
truly does not apply (do not silently default new tasks to it).
|
|
299
|
+
|
|
300
|
+
### Choosing an AC form (decision table)
|
|
301
|
+
|
|
302
|
+
| Task situation | Author AC as | Frontmatter | Gate effect |
|
|
303
|
+
| --- | --- | --- | --- |
|
|
304
|
+
| Criteria that graduate the feature's ship contract | `Scenario: AC1 — <feature scenario title>` | `ac_altitude: graduating` (or absent) | DD-09 subset enforced — drifted titles report `L4.uncovered-task-scenario`; `(req: …)` binding only when `ac_numbering: task-local` is also declared |
|
|
305
|
+
| Task-local regression/fix criteria | `Scenario: AC1 — <concrete outcome> (req: R1)` + Given/When/Then | `ac_altitude: task-local` + `ac_numbering: task-local` | subset skipped; every `- [ ] R<n>.` requirement must be bound by some scenario's `(req: …)` |
|
|
306
|
+
| Simple verifiable checklist rows | `- [ ] AC1 — <title>` | either altitude, per the rows above | joins checklist parsing and the subset match; **no** requirement binding (`Scenario:`-only) |
|
|
307
|
+
| Legacy raw bullets | `- AC1 …` (no checkbox, no `Scenario:`) | n/a | unparsed: invisible to subset, binding, and box checks — preserve in place, never author new |
|
|
308
|
+
|
|
309
|
+
`task-local` altitude skips only the feature-subset check. It does not waive the unchecked-box check,
|
|
310
|
+
required sections, the proof-bound verdict, line anchors, or any other done check — AC syntax alone,
|
|
311
|
+
at any altitude, never makes a task done.
|
|
@@ -534,7 +534,8 @@ is the procedure. The backing is a combination of git CLI, `spur` CLI, and agent
|
|
|
534
534
|
- **Coverage** — bullet count equals the commit count in `<since>..<until>`, and every short hash appears exactly once.
|
|
535
535
|
- **Mapping** — per-category bullet counts match the conventional-type counts (`feat`→`Added`, `fix`→`Fixed`, other recognized types→`Changed`, unrecognized→`Other`).
|
|
536
536
|
- **Header** — `[<version>] - <date>` matches the resolved `--version` (or detected tag) and the header date.
|
|
537
|
-
- **
|
|
537
|
+
- **Citations** — a bullet that asserts API/library behavior or a version/dependency claim beyond its commit summary carries a source citation: an official docs URL, or a local `path:line` verified against the installed manifest/lockfile (`package.json`, lockfile, or the cited file). A commit hash is provenance for what shipped, not a citation for what the API/library does; an uncited claim is flagged inline on the bullet (`(uncited: <claim>)`) rather than silently dropped.
|
|
538
|
+
- **HIGH** — all checks pass with every API/library claim cited. **MEDIUM** — coverage intact but mapping or header deviates, or at least one API/library claim lacks a source citation. **LOW** — coverage broken (missing/duplicate hashes), the range cannot be reconciled against git, or a citation fails verification (dead URL, or a `path:line` that does not support the claim).
|
|
538
539
|
- **Invariants:** Never mutates `CHANGELOG.md` directly — the command surface is stdout-only; writing it to a file (e.g. appending to `CHANGELOG.md`) is the operator's redirect choice, never the command's.
|
|
539
540
|
|
|
540
541
|
### 9. gitmsg
|
|
@@ -624,25 +625,27 @@ is the procedure. The backing is a combination of git CLI, `spur` CLI, and agent
|
|
|
624
625
|
- **Inputs:** `--scope <path>` (default: entire working tree) — limits fixes to a file or directory. `--gate-log <path>` (R3, task 0482) — before fixing, read this captured validation-run log and start at the `file:line` anchors its findings name, rather than re-deriving the failure from a fresh gate run. `--findings <anchors>` (R3, task 0482) — space-separated `file:line` anchors already extracted from that log by the `test` hop; when present these ARE the failing set, so fix them in order and do not run the gate to discover what broke.
|
|
625
626
|
- **Backing:** `inline` — lint + test fix loop.
|
|
626
627
|
- **Behavior:** 0. **Start at the anchors, never at a discovery run.** If `--findings <anchors>` is given, that space-separated `file:line` list IS the failing set — open those locations first, in order, and fix them; do not run the gate to find out what broke. If only `--gate-log <path>` is given, read that log first and identify the failing findings (lint/test errors with their `file:line` anchors) before running the loop — the captured log is the authoritative source. Either way the first action of this hop is a read, not a gate run (R3, task 0482).
|
|
627
|
-
1.
|
|
628
|
-
2. Run `bun run lint`
|
|
629
|
-
3. **Surface-change sweep (before any fix).** If the failing set is a **surface/parity** change — CLI noun/verb/flag surface, reference-file structure, or a renamed/moved symbol — do one upfront `rg` sweep across ALL test dirs + docs for stale references to the changed symbols (e.g. `rg -l "<old-noun>|<old-file>.md|Tier C" apps/*/tests plugins/sp/tests plugins/sp/skills docs`), and fix every pinned test/docs file in that sweep. A surface change is pinned in **multiple independent test files** (each with its own hardcoded noun→reference map); discovering them one-by-one via full-suite probes is the dominant waste — each wave costs a suite run and a fresh edit loop (task 0436 R2 lesson). One sweep produces the complete edit set upfront.
|
|
628
|
+
1. Inspect the project's formatter command, then settle formatter-only diffs. Without `--scope`, use the project formatter (here `bun run format`). With `--scope`, use a formatter invocation whose only path operands are inside the selected scope — for Biome, `bunx biome check --write "$SCOPE"`. Never append a path to a script already containing `.`: `biome check . --write <path>` still writes across the repository. Preserve quoted paths and apply the same scope to every later formatting pass. If no scoped formatter is available, report that limitation and leave out-of-scope files untouched. Here `bun run lint` checks formatting without rewriting it and also runs typecheck; formatting errors fail that check.
|
|
629
|
+
2. Run the project's read-only lint/type check (here `bun run lint`). Collect all errors. A repository-wide check may report outside-scope errors; report them without widening the authorized fix scope.
|
|
630
|
+
3. **Surface-change sweep (before any fix).** If the failing set is a **surface/parity** change — CLI noun/verb/flag surface, reference-file structure, or a renamed/moved symbol — do one upfront `rg` sweep across ALL test dirs + docs for stale references to the changed symbols (e.g. `rg -l "<old-noun>|<old-file>.md|Tier C" apps/*/tests plugins/sp/tests plugins/sp/skills docs`), and fix every in-scope pinned test/docs file in that sweep; report outside-scope matches without editing them. A surface change is pinned in **multiple independent test files** (each with its own hardcoded noun→reference map); discovering them one-by-one via full-suite probes is the dominant waste — each wave costs a suite run and a fresh edit loop (task 0436 R2 lesson). One sweep produces the complete edit set upfront.
|
|
630
631
|
4. If lint is clean, skip to step 6.
|
|
631
|
-
5. **Lint fix loop:** for each error, diagnose the root cause and apply the smallest fix. **Use targeted probes to verify each fix** (`bunx tsc --noEmit` on the affected package, `
|
|
632
|
-
6.
|
|
633
|
-
7. If tests are green,
|
|
634
|
-
8. **Test fix loop:** for each failure, diagnose (test bug vs implementation bug), apply the fix, re-run the **failing test only** (`bun test <file> --test-name-pattern "<test>"`). Do NOT re-run the full suite per fix — it is the dominant loop cost (task 0436 R2).
|
|
635
|
-
9. **Confirming run — standalone invocation only (at most once).** After all fixes,
|
|
632
|
+
5. **Lint fix loop:** for each in-scope error, diagnose the root cause and apply the smallest fix. **Use targeted probes to verify each fix** (`bunx tsc --noEmit` on the affected package, `bunx biome check <file>`) — do NOT re-run the full gate after every batch. Loop until lint is green, or report outside-scope blockers without editing them.
|
|
633
|
+
6. For standalone execution, run `bun run test` and collect all failures. With `--gate-log`, use its failing set and targeted tests instead of running the full suite here.
|
|
634
|
+
7. If tests are green, proceed to the applicable standalone confirmation or pipeline handoff below.
|
|
635
|
+
8. **Test fix loop:** for each in-scope failure, diagnose (test bug vs implementation bug), apply the fix, re-run the **failing test only** (`bun test <file> --test-name-pattern "<test>"`). Report outside-scope blockers without editing them. Do NOT re-run the full suite per fix — it is the dominant loop cost (task 0436 R2).
|
|
636
|
+
9. **Confirming run — standalone invocation only (at most once).** After all fixes, repeat the scope-safe formatting invocation from step 1, then run the selected validation command **at most once** to confirm (here the default project gate is `bun run spur-check`). If it passes, the hop is done. **Skip this step entirely when `--gate-log` is set** (see step 10) — under the pipeline nothing consumes its verdict.
|
|
636
637
|
10. **Pipeline-awareness (R4, task 0483; confirming run dropped 2026-08-31).** `--gate-log` is set only by the pipeline's `test-fix` hop, so treat it as the pipeline signal. There, `test-recheck` runs the full `${vars.qualityGateCmd}` gate immediately after this hop returns — that is the **deciding** run, the only one that writes PASS to `.spur/run/<wbs>-test-gate.status`. A confirming run inside this hop decides nothing and costs a second full gate (~105 s here) to answer a question `test-recheck` is about to answer anyway. So under `--gate-log`: **run no full gate at all.** Your self-check is what step 8 already gives you — the failing tests you re-ran individually — plus **one `bun run lint`** (~7 s: Biome + typecheck) to catch type or lint breakage your fixes introduced. Then return and let `test-recheck` judge. `test-recheck` opens with the same cheap `${vars.gateProbeCmd}` probe, so a still-red tree is caught in seconds rather than by a full suite. Historical anti-pattern this replaces: 0482 ran the gate 3× plus a standalone `bun run test`, and `test-recheck` then ran it a 5th time.
|
|
637
638
|
11. Report: list what was fixed (file + one-line summary per fix). If any error could not be resolved, report it explicitly — do not suppress.
|
|
638
|
-
- **Invariants:** Never bypass with `--no-verify`, `--force`, or new `biome-ignore`/`eslint-disable` suppressions. Never skip or `.skip` a test to make the suite green. Fix the root cause, not the symptom.
|
|
639
|
-
- **MANDATORY Exit Condition.**
|
|
639
|
+
- **Invariants:** Never bypass with `--no-verify`, `--force`, or new `biome-ignore`/`eslint-disable` suppressions. Never skip or `.skip` a test to make the suite green. Fix the root cause, not the symptom. Here lint checks formatting and types; it does not prove that the full test gate passed. Keep every write inside `--scope`. **Never re-run the full gate more than once per confirming pass** (R4) — use targeted probes during the fix loops and let the pipeline's `test-recheck` state be the deciding run.
|
|
640
|
+
- **MANDATORY Exit Condition — standalone only.** At step 9, execute the following once; do not add another confirming run:
|
|
640
641
|
1. Run validation command: `eval "$VALIDATION_CMD"`
|
|
641
642
|
2. Capture exit code: `EXIT_CODE=$?`
|
|
642
643
|
3. Output: `echo "EXIT_CODE=$EXIT_CODE"`
|
|
643
644
|
4. **EXIT_CODE must equal 0**
|
|
644
645
|
|
|
645
|
-
If EXIT_CODE != 0: NOT completed.
|
|
646
|
+
If EXIT_CODE != 0: NOT completed. Continue the bounded fix loop for in-scope failures; report outside-scope blockers.
|
|
647
|
+
|
|
648
|
+
**Pipeline exit condition (`--gate-log` set):** return the fixes and the targeted-test/lint evidence from step 10 to `test-recheck`. Do not run the standalone validation command or claim a full-gate PASS; `test-recheck` owns that verdict.
|
|
646
649
|
|
|
647
650
|
**Hallucination Red Flags — STOP if you think:**
|
|
648
651
|
- "The errors look fixed" — check exit code, not appearance
|
|
@@ -671,7 +674,7 @@ is the procedure. The backing is a combination of git CLI, `spur` CLI, and agent
|
|
|
671
674
|
└─────────────────────────────────────────────────┘
|
|
672
675
|
```
|
|
673
676
|
|
|
674
|
-
The 7 phases
|
|
677
|
+
The 7 phases describe standalone execution: Phases 1–2 capture the gate, Phase 3 settles auto-fixable formatting within scope, Phases 4–6 are the per-group root-cause fix loops, Phase 7 is the final verification run. With `--gate-log`, use the supplied evidence instead of capturing the gate again, and hand off to `test-recheck` instead of running Phase 7's full validation.
|
|
675
678
|
|
|
676
679
|
- **Fix Priority:**
|
|
677
680
|
|