@gobing-ai/spur 0.3.49 → 0.3.51
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/corpus-baseline.json +12198 -1
- package/config/rules/quality/coverage-gate.yaml +3 -2
- package/config/workflows/pr-review.yaml +13 -11
- package/config/workflows/task-pipeline.yaml +37 -16
- package/package.json +9 -9
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/pr-reviewing.ts +35 -1
- package/plugins/sp/skills/code-verification/SKILL.md +14 -0
- package/plugins/sp/skills/pr-reviewing/SKILL.md +6 -3
- package/plugins/sp/skills/spur-cli/references/tasks.md +1 -0
- package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +18 -2
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +14 -6
- package/spur.js +4804 -5601
- package/web/_astro/BoardApp.pdtYu-yW.js +1 -0
- package/web/_astro/{BoardApp.BjQUNhuj.js → BoardApp.plVXYkKo.js} +11 -11
- package/web/_astro/{TaskDetail.CVBuD6dF.js → TaskDetail.5DIyVQ_N.js} +1 -1
- package/web/_astro/{arc.BMMjdODi.js → arc.BnUgL7ho.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.BU5ShzXf.js → architectureDiagram-3BPJPVTR.B9YMJau-.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.Bj1iEqPD.js → blockDiagram-GPEHLZMM.Dw2qgvWJ.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.vX8wepCL.js → c4Diagram-AAUBKEIU.jTbU0-Nk.js} +1 -1
- package/web/_astro/channel.DEqvaz-I.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.BKAipTym.js → chunk-2J33WTMH.Di46EtkU.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.B68XkPG7.js → chunk-4BX2VUAB.DweHBwqQ.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.BmeDLcrc.js → chunk-55IACEB6.rRjTKaa7.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.PDuBA3Kw.js → chunk-727SXJPM.BkEQIkSV.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.C7A044za.js → chunk-AQP2D5EJ.Cfe9IvAd.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.BtzKKFqR.js → chunk-FMBD7UC4.fI6tLbeN.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.BJuDeeOy.js → chunk-ND2GUHAM.BIjgpq0O.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.DSeMDgcQ.js → chunk-QZHKN3VN.ZwLnVxyz.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.D53Q4tCw.js → classDiagram-4FO5ZUOK.BJAGYidE.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.D53Q4tCw.js → classDiagram-v2-Q7XG4LA2.BJAGYidE.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.c712AFRH.js → cose-bilkent-S5V4N54A.B5y3nz26.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.D-idisph.js → dagre-BM42HDAG.DHmw0ZBC.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.DLgnsJCU.js → diagram-2AECGRRQ.10gapJfY.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.BiaxBVqx.js → diagram-5GNKFQAL.Gh33_drM.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.C8HX1vd8.js → diagram-KO2AKTUF.8ROWlfcX.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.CfqDLLes.js → diagram-LMA3HP47.BsP3tKmg.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.15SDiEed.js → diagram-OG6HWLK6.DM2WFDhU.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.DksYtOYM.js → erDiagram-TEJ5UH35.royaC_lH.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.DR_Au-HV.js → flowDiagram-I6XJVG4X.C9GEvIie.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.CHhHrffI.js → ganttDiagram-6RSMTGT7.CXO4T2U0.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.B2Xehvam.js → gitGraphDiagram-PVQCEYII.Q02KzYH7.js} +1 -1
- package/web/_astro/{infoDiagram-5YYISTIA.C9c3CNNN.js → infoDiagram-5YYISTIA.DNfSDKJt.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.BibUHkh8.js → ishikawaDiagram-YF4QCWOH.BFttvtgJ.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.BYoVHiyO.js → journeyDiagram-JHISSGLW.Cfm-2SbG.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.CM1K5wHE.js → kanban-definition-UN3LZRKU.BWGNW6fV.js} +1 -1
- package/web/_astro/{linear.SPpjJUb-.js → linear.BprdUk2Y.js} +1 -1
- package/web/_astro/{mermaid.core.BAgx3nnb.js → mermaid.core.B2zRNxtX.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.D35oPG1R.js → mindmap-definition-RKZ34NQL.g9jij8sw.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.DiWuRwk7.js → pieDiagram-4H26LBE5.CbzR4n5b.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.B9PBzTWn.js → quadrantDiagram-W4KKPZXB.DxV3viWt.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.CYuuamFN.js → requirementDiagram-4Y6WPE33.DvAb90JS.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.W24UhhtD.js → sankeyDiagram-5OEKKPKP.CIFz5t4D.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.BpbNjA51.js → sequenceDiagram-3UESZ5HK.DzlEQynd.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.DqVsHudf.js → stateDiagram-AJRCARHV.Dph2V-ts.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.CzwHYX81.js → stateDiagram-v2-BHNVJYJU.wJhfrLAy.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.Bc3B6djw.js → timeline-definition-PNZ67QCA.BrSiyubX.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.C-D5rh8O.js → vennDiagram-CIIHVFJN.COobPrUX.js} +1 -1
- package/web/_astro/{wardley-L42UT6IY.D7PdYCqn.js → wardley-L42UT6IY.CrjeTx8A.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.CwmJKXF3.js → wardleyDiagram-YWT4CUSO.B8_fTQ8E.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.avDYnLsb.js → xychartDiagram-2RQKCTM6.BPpokgVv.js} +1 -1
- package/web/favicon.svg +1 -0
- package/web/index.html +1 -1
- package/web/_astro/BoardApp.8hiqShQn.js +0 -1
- package/web/_astro/channel.EwdSemIC.js +0 -1
|
@@ -2,8 +2,9 @@ $schema: "@gobing-ai/spur/schemas/rule-file.schema.json"
|
|
|
2
2
|
# Coverage gate — per-file line coverage meets threshold from Bun's lcov output.
|
|
3
3
|
# Absorbed from ts-libs/.spur/rules/quality/coverage-gate.yaml, re-scoped to
|
|
4
4
|
# Spur's monorepo layout:
|
|
5
|
-
# - lcovPath kept at .coverage/lcov.info (Spur's `bun run test`
|
|
6
|
-
#
|
|
5
|
+
# - lcovPath kept at .coverage/lcov.info (Spur's `bun run test:coverage` —
|
|
6
|
+
# what `bun run check` and the `*:full` chains run — writes coverage there
|
|
7
|
+
# via --coverage-dir=.coverage; plain `bun run test` skips coverage)
|
|
7
8
|
# - include expanded to apps/** + packages/** (Spur tests cover both;
|
|
8
9
|
# ts-libs only covers packages/**)
|
|
9
10
|
# - threshold kept at 90 matching bunfig.toml coverageThreshold
|
|
@@ -43,19 +43,21 @@ terminalStates:
|
|
|
43
43
|
failureStates:
|
|
44
44
|
- failed
|
|
45
45
|
vars:
|
|
46
|
-
mode:
|
|
47
|
-
baseBranch:
|
|
48
|
-
focus:
|
|
49
|
-
noWait:
|
|
50
|
-
waitTimeoutSec:
|
|
51
|
-
waitIntervalSec:
|
|
52
|
-
preReviewCmd:
|
|
53
|
-
__runId:
|
|
46
|
+
mode: "full"
|
|
47
|
+
baseBranch: ""
|
|
48
|
+
focus: ""
|
|
49
|
+
noWait: "false"
|
|
50
|
+
waitTimeoutSec: "600"
|
|
51
|
+
waitIntervalSec: "30"
|
|
52
|
+
preReviewCmd: ""
|
|
53
|
+
__runId: ""
|
|
54
54
|
|
|
55
55
|
states:
|
|
56
56
|
- id: preflight
|
|
57
57
|
description: >
|
|
58
|
-
Soft probe: git/gh/repo checks (detached HEAD, dirty tree, gh auth, GitHub remote
|
|
58
|
+
Soft probe: git/gh/repo checks (detached HEAD, dirty tree, gh auth, GitHub remote,
|
|
59
|
+
base-branch refusal — preflight refuses when the current branch IS the resolved base,
|
|
60
|
+
before any push can publish it).
|
|
59
61
|
Writes PASS|FAIL to .spur/run/${vars.__runId}-pr-preflight.status; always exit 0.
|
|
60
62
|
onEnter:
|
|
61
63
|
- kind: shell
|
|
@@ -64,7 +66,7 @@ states:
|
|
|
64
66
|
mkdir -p .spur/run &&
|
|
65
67
|
STATUS_FILE=".spur/run/$__runId-pr-preflight.status" &&
|
|
66
68
|
set +e &&
|
|
67
|
-
bun "$(superskill script path sp pr-reviewing.ts)" preflight --json > ".spur/run/$__runId-pr-context.json";
|
|
69
|
+
bun "$(superskill script path sp pr-reviewing.ts)" preflight --base "$baseBranch" --json > ".spur/run/$__runId-pr-context.json";
|
|
68
70
|
rc=$?; set -e &&
|
|
69
71
|
if [ "$rc" -eq 0 ]; then printf 'PASS\n' > "$STATUS_FILE"; else printf 'FAIL\n' > "$STATUS_FILE"; fi &&
|
|
70
72
|
exit 0
|
|
@@ -212,7 +214,7 @@ transitions:
|
|
|
212
214
|
command: 'test "$(cat .spur/run/$__runId-pr-preflight.status 2>/dev/null)" = PASS'
|
|
213
215
|
- from: preflight
|
|
214
216
|
to: failed
|
|
215
|
-
description: Preflight red (detached HEAD, dirty tree, gh auth, no GitHub remote) — stop before any publishing
|
|
217
|
+
description: Preflight red (detached HEAD, dirty tree, gh auth, no GitHub remote, or the current branch being the base branch) — stop before any publishing
|
|
216
218
|
guard:
|
|
217
219
|
kind: always
|
|
218
220
|
|
|
@@ -85,6 +85,12 @@ vars:
|
|
|
85
85
|
# TRUSTED CONFIG ONLY — this string is executed via `sh -c` (see test/test-recheck). Never
|
|
86
86
|
# interpolate untrusted operator/LLM input into qualityGateCmd (task 0436 SECUA residual).
|
|
87
87
|
qualityGateCmd: "bun run format && bun run spur-check"
|
|
88
|
+
# Cheap red-detector run before the full gate on **recheck only**; empty ⇒ no probe
|
|
89
|
+
# (full gate every recheck — the pre-0587 behavior). A project overriding qualityGateCmd
|
|
90
|
+
# should override this too. TRUSTED CONFIG ONLY — executed via `sh -c` (same surface as
|
|
91
|
+
# qualityGateCmd). Invariant: `review` is only ever entered through a full green
|
|
92
|
+
# qualityGateCmd — only the full gate writes PASS to <wbs>-test-gate.status.
|
|
93
|
+
gateProbeCmd: "bun run lint"
|
|
88
94
|
# Max /sp:dev-fixall attempts after a red quality-gate probe/recheck (bounded; no thrash).
|
|
89
95
|
# Attempt counter: .spur/run/<wbs>-test-fix-attempt. Default 2 = two fixall hops before failed.
|
|
90
96
|
qualityGateMaxFixAttempts: "2"
|
|
@@ -300,7 +306,7 @@ states:
|
|
|
300
306
|
# `${vars.qualityGateCmd}` at `test` is the gate that actually decides.
|
|
301
307
|
- kind: shell
|
|
302
308
|
options:
|
|
303
|
-
command:
|
|
309
|
+
command: "$formatCmd ; exit 0"
|
|
304
310
|
|
|
305
311
|
# ── test hop (quality gate + bounded auto-fix) ─────────────────────────────
|
|
306
312
|
# NOT /sp:dev-unit. That command (sp:code-testing) *extends/generates* tests toward
|
|
@@ -389,28 +395,43 @@ states:
|
|
|
389
395
|
or the pipeline `failed` state (FAIL and attempts exhausted) — never a
|
|
390
396
|
raw lifecycle abort that skips the terminal `failed` state.
|
|
391
397
|
onEnter:
|
|
398
|
+
# 0587 R3: probe-then-full recheck. A red gateProbeCmd records FAIL and skips the full
|
|
399
|
+
# gate (the measured waste is re-running a 110–140s gate to learn the tree is still red);
|
|
400
|
+
# a green probe (or empty gateProbeCmd) falls through to the full-gate loop unchanged.
|
|
401
|
+
# Only the full gate writes PASS, so the `test-recheck → review` guard (reads PASS)
|
|
402
|
+
# still means a full green qualityGateCmd ran — invariant preserved by construction.
|
|
392
403
|
- kind: shell
|
|
393
404
|
options:
|
|
394
405
|
command: >-
|
|
395
406
|
mkdir -p .spur/run &&
|
|
396
407
|
STATUS_FILE=".spur/run/$wbs-test-gate.status" &&
|
|
397
408
|
LOG_FILE=".spur/run/$wbs-test-gate.log" &&
|
|
409
|
+
FINDINGS_FILE=".spur/run/$wbs-test-gate.findings" &&
|
|
398
410
|
: > "$LOG_FILE" &&
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
411
|
+
probe_rc=0;
|
|
412
|
+
if [ -n "$gateProbeCmd" ]; then
|
|
413
|
+
sh -c "$gateProbeCmd" > "$LOG_FILE.probe" 2>&1; probe_rc=$?;
|
|
414
|
+
cat "$LOG_FILE.probe" >> "$LOG_FILE";
|
|
415
|
+
rm -f "$LOG_FILE.probe";
|
|
416
|
+
fi;
|
|
417
|
+
if [ "$probe_rc" -ne 0 ]; then
|
|
418
|
+
gate_rc=$probe_rc;
|
|
419
|
+
else
|
|
420
|
+
gate_attempt=1;
|
|
421
|
+
while [ "$gate_attempt" -le 5 ]; do
|
|
422
|
+
ATTEMPT_LOG="$LOG_FILE.attempt-$gate_attempt";
|
|
423
|
+
sh -c "$qualityGateCmd" > "$ATTEMPT_LOG" 2>&1; gate_rc=$?;
|
|
424
|
+
gate_locked=0;
|
|
425
|
+
grep -q 'SQLiteError: database is locked' "$ATTEMPT_LOG" && gate_locked=1;
|
|
426
|
+
cat "$ATTEMPT_LOG" >> "$LOG_FILE";
|
|
427
|
+
rm -f "$ATTEMPT_LOG";
|
|
428
|
+
if [ "$gate_rc" -eq 0 ] || [ "$gate_locked" -ne 1 ] || [ "$gate_attempt" -ge 5 ]; then break; fi;
|
|
429
|
+
printf 'quality gate: database is locked; retrying (%s/5) in 10s\n' "$gate_attempt" | tee -a "$LOG_FILE";
|
|
430
|
+
sleep 10;
|
|
431
|
+
gate_attempt=$((gate_attempt + 1));
|
|
432
|
+
done;
|
|
433
|
+
fi &&
|
|
412
434
|
cat "$LOG_FILE" &&
|
|
413
|
-
FINDINGS_FILE=".spur/run/$wbs-test-gate.findings" &&
|
|
414
435
|
set +e; grep -oE '[A-Za-z0-9_./-]+\.[A-Za-z]+:[0-9]+' "$LOG_FILE" | sort -u | head -20 | tr '\n' ' ' > "$FINDINGS_FILE"; set -e &&
|
|
415
436
|
if [ "$gate_rc" -eq 0 ]; then
|
|
416
437
|
printf 'PASS\n' > "$STATUS_FILE";
|
|
@@ -709,4 +730,4 @@ transitions:
|
|
|
709
730
|
guard:
|
|
710
731
|
kind: shell
|
|
711
732
|
options:
|
|
712
|
-
command:
|
|
733
|
+
command: "! $spurBin task check $wbs"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/spur",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.51",
|
|
4
4
|
"description": "Spur CLI — local-first harness for mainstream coding agents: constraint checking, workflow orchestration, agent health, and history analytics. Bun-native; exposes the `spur` command.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"spur",
|
|
@@ -53,14 +53,14 @@
|
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@commander-js/extra-typings": "^14.0.0",
|
|
56
|
-
"@gobing-ai/ts-db": "^0.4.
|
|
57
|
-
"@gobing-ai/ts-ai-runner": "^0.4.
|
|
58
|
-
"@gobing-ai/ts-dual-workflow-engine": "^0.4.
|
|
59
|
-
"@gobing-ai/ts-infra": "^0.4.
|
|
60
|
-
"@gobing-ai/ts-llm-jsonl-importer": "^0.4.
|
|
61
|
-
"@gobing-ai/ts-rule-engine": "^0.4.
|
|
62
|
-
"@gobing-ai/ts-runtime": "^0.4.
|
|
63
|
-
"@gobing-ai/ts-utils": "^0.4.
|
|
56
|
+
"@gobing-ai/ts-db": "^0.4.38",
|
|
57
|
+
"@gobing-ai/ts-ai-runner": "^0.4.38",
|
|
58
|
+
"@gobing-ai/ts-dual-workflow-engine": "^0.4.38",
|
|
59
|
+
"@gobing-ai/ts-infra": "^0.4.38",
|
|
60
|
+
"@gobing-ai/ts-llm-jsonl-importer": "^0.4.38",
|
|
61
|
+
"@gobing-ai/ts-rule-engine": "^0.4.38",
|
|
62
|
+
"@gobing-ai/ts-runtime": "^0.4.38",
|
|
63
|
+
"@gobing-ai/ts-utils": "^0.4.38",
|
|
64
64
|
"@types/bun": "1.3.14",
|
|
65
65
|
"@types/figlet": "^1.7.0",
|
|
66
66
|
"@types/node-notifier": "8.0.5",
|
package/plugins/sp/plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.51",
|
|
4
4
|
"description": "Spur — a local-first harness engineering toolkit that wraps mainstream coding agents with constraint checking, workflow orchestration, and history analytics.",
|
|
5
5
|
"extensions": {
|
|
6
6
|
"pi": ["./hooks/pi/guard-extension.ts"]
|
|
@@ -88,6 +88,12 @@ interface PreflightContext {
|
|
|
88
88
|
defaultBranch: string;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
interface Upstream {
|
|
92
|
+
ref: string;
|
|
93
|
+
ahead: number;
|
|
94
|
+
behind: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
91
97
|
interface Finding {
|
|
92
98
|
kind: 'review' | 'inline' | 'comment';
|
|
93
99
|
severity: string;
|
|
@@ -433,6 +439,22 @@ function preflightContext(): PreflightContext {
|
|
|
433
439
|
};
|
|
434
440
|
}
|
|
435
441
|
|
|
442
|
+
/** Upstream divergence, non-fatal: a missing upstream is a normal state, not an error. */
|
|
443
|
+
function resolveUpstream(): Upstream | null {
|
|
444
|
+
const refRes = run(['git', 'rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{u}']);
|
|
445
|
+
if (refRes.code !== 0) return null;
|
|
446
|
+
const ref = refRes.stdout.trim();
|
|
447
|
+
const count = (res: CmdResult): number => {
|
|
448
|
+
const n = Number(res.stdout.trim());
|
|
449
|
+
return res.code === 0 && Number.isFinite(n) ? n : 0;
|
|
450
|
+
};
|
|
451
|
+
return {
|
|
452
|
+
ref,
|
|
453
|
+
ahead: count(run(['git', 'rev-list', '--count', '@{u}..HEAD'])),
|
|
454
|
+
behind: count(run(['git', 'rev-list', '--count', 'HEAD..@{u}'])),
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
436
458
|
function viewPr(): GhPr | null {
|
|
437
459
|
const res = run([
|
|
438
460
|
'gh',
|
|
@@ -507,15 +529,27 @@ function cmdPreflight(args: ParsedArgs): void {
|
|
|
507
529
|
2,
|
|
508
530
|
);
|
|
509
531
|
}
|
|
532
|
+
const base = (args.flags.get('--base') ?? '').trim() || ctx.defaultBranch;
|
|
533
|
+
const upstream = resolveUpstream();
|
|
534
|
+
if (ctx.branch === base) {
|
|
535
|
+
writeStatus(args, 'FAIL');
|
|
536
|
+
fail(
|
|
537
|
+
args,
|
|
538
|
+
`current branch is the base branch (${base}) — a PR reviews a feature branch against it; ` +
|
|
539
|
+
'check out a feature branch (nothing on the base branch is reviewable)',
|
|
540
|
+
2,
|
|
541
|
+
);
|
|
542
|
+
}
|
|
510
543
|
writeStatus(args, 'PASS');
|
|
511
544
|
emit(
|
|
512
545
|
args,
|
|
513
|
-
{ ok: true, ...ctx },
|
|
546
|
+
{ ok: true, ...ctx, upstream },
|
|
514
547
|
[
|
|
515
548
|
`Repository: ${ctx.nameWithOwner}`,
|
|
516
549
|
`Branch: ${ctx.branch}`,
|
|
517
550
|
`HEAD: ${ctx.shortHead}`,
|
|
518
551
|
`Default: ${ctx.defaultBranch}`,
|
|
552
|
+
`Upstream: ${upstream ? `${upstream.ref} (ahead ${upstream.ahead}, behind ${upstream.behind})` : `none (publishing would create origin/${ctx.branch})`}`,
|
|
519
553
|
'Local: clean',
|
|
520
554
|
].join('\n'),
|
|
521
555
|
);
|
|
@@ -127,6 +127,20 @@ heading/comment unrelated to the requirement fails the row to UNMET and surfaces
|
|
|
127
127
|
a finding (severity >= P2). This closes the gap where a verify run certified a task `done` citing
|
|
128
128
|
`evidence:134` that was actually a sibling ticket's telemetry text (0299 R1, from the 0282 re-audit).
|
|
129
129
|
|
|
130
|
+
**External evidence (task 0584 / ADR-062) — legal citation form.** Evidence that lives OUTSIDE this
|
|
131
|
+
repository has a frozen non-anchor form: a named origin plus a backticked path with the line number
|
|
132
|
+
**outside** the backticks.
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
Evidence: @gobing-ai/ts-llm-jsonl-importer `src/mappers.ts` line 481 — omp call_id write
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Use this form ONLY when the evidence genuinely lives outside `spur`'s working tree (a `@gobing-ai/ts-*`
|
|
139
|
+
source under `~/xprojects/ts-libs`, or a gitignored `.spur/run/**` artifact). `checkLineAnchors`
|
|
140
|
+
classifies it as external and never raises `L4.stale-line-anchor` for it (R1). Do NOT use it for a file
|
|
141
|
+
that lives in this repo — in-repo evidence MUST use the repo-relative backtick form
|
|
142
|
+
`` `path:line` `` / `` `path:start-end` ``, and citing it in the external form still reports (R2).
|
|
143
|
+
|
|
130
144
|
### Step 5 — Acceptance Criteria guard
|
|
131
145
|
|
|
132
146
|
If the task has a non-empty Acceptance Criteria section, evaluate every checklist item and every
|
|
@@ -135,9 +135,12 @@ bun "$(superskill script path sp pr-reviewing.ts)" <subcommand> [flags]
|
|
|
135
135
|
Installed targets resolve the staged TypeScript source and execute it with Bun, matching the rest
|
|
136
136
|
of `plugins/sp/scripts`.
|
|
137
137
|
|
|
138
|
-
1. **Preflight** — `<script> preflight --json`. Hard-fails on a
|
|
139
|
-
detached HEAD, missing `gh` auth, no GitHub remote,
|
|
140
|
-
|
|
138
|
+
1. **Preflight** — `<script> preflight --base "$base" --json`. Hard-fails on a
|
|
139
|
+
detached HEAD, missing `gh` auth, no GitHub remote, a dirty tree, or the current branch
|
|
140
|
+
being the base branch (a PR reviews a feature branch against the base; nothing on the
|
|
141
|
+
base branch is reviewable, and the guard runs before any push can publish it). On a
|
|
142
|
+
dirty tree, triage with the user (commit/stash/exclude) before continuing — the
|
|
143
|
+
workflow refuses to guess.
|
|
141
144
|
2. **Hygiene** — `<script> hygiene --base "$base" --json`. `BLOCK` (secrets, `.env`, conflict markers,
|
|
142
145
|
private keys) stops the run — never submit a tainted diff. `WARN` (debug residue) rides along
|
|
143
146
|
into the report. This is a submission sanity check, not a second local review.
|
|
@@ -46,6 +46,7 @@ re-reading or re-tokenizing the task.
|
|
|
46
46
|
| `list` | List tasks, filtered | `--status <s>` `--phase <p>` `--parent <wbs>` `--feature <id>` `--folder` `--json` |
|
|
47
47
|
| `refresh` | Re-scan the corpus and report counts (**`kanban.md` retired** — web Task Kanban is SSOT) | `--folder` `--json` |
|
|
48
48
|
| `migrate` | One-time A17 corpus normalization pass | `--dry-run` `--folder` `--json` |
|
|
49
|
+
| `migrate-anchors` | Qualify in-repo evidence anchors to repo-relative paths (0583 R1–R3) | `--dry-run` `--json` |
|
|
49
50
|
| `refresh-roster <wbs>` | Regenerate a parent task's sub-task roster block in `## Plan` | `--folder` `--json` |
|
|
50
51
|
| `batch-create` | Create many tasks from a validated JSON array | `--file <path>` `--folder` `--json` |
|
|
51
52
|
| `record <wbs>` | Write Testing/Review from a verify verdict; optional Solution + transition | `--verdict-file <path>` `--solution-from-diff` `--transition <status>` `--folder` `--json` |
|
|
@@ -103,7 +103,7 @@ omission is now reported as an `ac-row-dropped` check naming the row and the unr
|
|
|
103
103
|
`rowMatchesScenario` accepts any of these as naming the feature scenario `R3 — Foo`:
|
|
104
104
|
|
|
105
105
|
| Form | Example |
|
|
106
|
-
|
|
106
|
+
| ------ | --------- |
|
|
107
107
|
| Exact title | `R3 — Foo` |
|
|
108
108
|
| Bare title (R-prefix dropped) | `Foo` |
|
|
109
109
|
| `Scenario:` prefix | `Scenario: R3 — Foo` |
|
|
@@ -201,7 +201,7 @@ regenerates the `## Tasks` block.
|
|
|
201
201
|
Each resolved decision from a grilling interview (Phase 1) becomes one or more Gherkin scenarios:
|
|
202
202
|
|
|
203
203
|
| Decision-tree element | Becomes |
|
|
204
|
-
|
|
204
|
+
| ----------------------- | --------- |
|
|
205
205
|
| A **locked decision** (a capability the feature commits to) | A `@core` scenario — the must-ship behavior it enables |
|
|
206
206
|
| The decision's **observable outcome** (why it was chosen) | The scenario's `Then` — assert the observable, not the mechanism |
|
|
207
207
|
| A decision's **preconditions / constraints** | The scenario's `Given` |
|
|
@@ -211,3 +211,19 @@ Each resolved decision from a grilling interview (Phase 1) becomes one or more G
|
|
|
211
211
|
|
|
212
212
|
Number scenarios `R1, R2, …` sequentially, stable forever (the title is the traceability identity
|
|
213
213
|
key). Use the Gherkin template at `.spur/templates/bdd/gherkin.md`.
|
|
214
|
+
|
|
215
|
+
## AC altitude (task 0584 / ADR-062)
|
|
216
|
+
|
|
217
|
+
A task must declare, via the `ac_altitude` frontmatter field, whether its AC scenarios are the
|
|
218
|
+
feature's ship contract or a finer-grained local contract. The field is the **only** input — it is
|
|
219
|
+
never inferred from `template`, `status`, or whether the AC uses Gherkin (R4).
|
|
220
|
+
|
|
221
|
+
| `ac_altitude` | Meaning | DD-09 subset rule |
|
|
222
|
+
| --- | --- | --- |
|
|
223
|
+
| `graduating` (or absent) | Task scenarios are ship-contract criteria that graduate the feature's AC | Enforced — every task scenario must match a linked-feature scenario by normalized title. Drifted titles still report (R5). |
|
|
224
|
+
| `task-local` | Task criteria sit at a finer altitude than the feature's ship contract (fix-task regression criteria, per-defect cases) | Skipped — no uncovered-scenario findings regardless of title drift (R3) |
|
|
225
|
+
|
|
226
|
+
Use `task-local` for a fix/refactor task whose regression criteria are not the feature's ship
|
|
227
|
+
contract. Keep a `graduating` task's scenario titles identical to the feature's so DD-09 stays
|
|
228
|
+
satisfied. Absent-altitude is `graduating` — set `task-local` explicitly only where the subset rule
|
|
229
|
+
truly does not apply (do not silently default new tasks to it).
|
|
@@ -24,7 +24,6 @@ not redefine it.
|
|
|
24
24
|
> does not restate the contract. The value table below is authoritative; parity with it is
|
|
25
25
|
> enforced by `validate-flag-contracts.ts` (C3a/C3b).
|
|
26
26
|
|
|
27
|
-
|
|
28
27
|
### The one rule
|
|
29
28
|
|
|
30
29
|
> **`--agent <value>` names *who* does the model-bearing work. The execution surface is derived from
|
|
@@ -49,7 +48,7 @@ selects the zero-dispatch carve-out below.
|
|
|
49
48
|
> fallback.**
|
|
50
49
|
|
|
51
50
|
| Value | Who does the work | Derived surface |
|
|
52
|
-
|
|
51
|
+
| --- | --- | --- |
|
|
53
52
|
| `(omitted)` | The agent running this session | Host session — host-controlled; eligible model stages may use a native subagent (0508) |
|
|
54
53
|
| `inline` | The agent running this session | Host session — hard guarantee: zero dispatch, never a subprocess, never a workflow hop; headless surfaces reject `inline` (exit 2, stable special error) |
|
|
55
54
|
| `auto` | The role the caller declared — this command's `role:` frontmatter or the workflow step's `role:` (Layer 1, `plugins/sp/references/roles.md`); with nothing declared, `agent.default`'s role (0542) | Subprocess — a tier-resolved executor pins a specific agent/model, which the host session cannot supply |
|
|
@@ -219,7 +218,7 @@ stage is required, select the subprocess path (`--agent auto` or `--agent <name>
|
|
|
219
218
|
Never edit a task or feature file directly. Every mutation goes through:
|
|
220
219
|
|
|
221
220
|
| Intent | CLI verb |
|
|
222
|
-
|
|
221
|
+
| -------- | ---------- |
|
|
223
222
|
| Create a task | `spur task create` |
|
|
224
223
|
| Change status | `spur task update <wbs> <status>` |
|
|
225
224
|
| Edit a section | `spur task update <wbs> --section <name> --from-file <path>` |
|
|
@@ -329,6 +328,15 @@ loop?**
|
|
|
329
328
|
operator, and the *answer* is recorded where the decision belongs — the feature body, an ADR
|
|
330
329
|
(`docs/00_ADR.md`), or the design doc. A task may then be created for the work the answer implies.
|
|
331
330
|
|
|
331
|
+
**Authoring-contract decisions (task 0584 / ADR-062).** Two fields a task author must get right:
|
|
332
|
+
|
|
333
|
+
- **External evidence** — evidence that lives outside `spur`'s working tree uses the frozen non-anchor
|
|
334
|
+
form `origin`path`line N` (line OUTSIDE the backticks), e.g.
|
|
335
|
+
`@gobing-ai/ts-llm-jsonl-importer`src/mappers.ts`line 481`. It is classified external, never
|
|
336
|
+
`L4.stale-line-anchor`. In-repo evidence MUST use the repo-relative backtick form `path:line` —
|
|
337
|
+
citing an in-repo file in the external form still reports (R1/R2). See `ac-style-guide.md` and the
|
|
338
|
+
`code-verification` skill for when each applies.
|
|
339
|
+
|
|
332
340
|
**Why this is a rule and not a preference.** A decision filed as a task sits in `spur task list` and
|
|
333
341
|
in a feature's Tasks table looking like queued work. It gets handed to an implementing agent, which
|
|
334
342
|
either stalls or invents the decision and calls it done. It also inflates task counts, which is how
|
|
@@ -432,7 +440,7 @@ line is a claim to re-verify, not evidence to forward. Re-run and paste.
|
|
|
432
440
|
**Red Flags — an unverified claim is usually hiding behind one of these:**
|
|
433
441
|
|
|
434
442
|
| Red flag | What it usually means |
|
|
435
|
-
|
|
443
|
+
| --- | --- |
|
|
436
444
|
| "This should work" / "this will pass" / "probably fine" | You are predicting, not reporting. Run it and paste the result. |
|
|
437
445
|
| Expressing satisfaction ("great, that's done!") before any check ran | Relief is not evidence — the check has not been run this turn. |
|
|
438
446
|
| Forwarding a subagent's "success" without re-running its gate | You are trusting a claim, not verifying it. Re-run the check yourself. |
|
|
@@ -529,7 +537,7 @@ invariants that keep the pipeline set coherent as new ones are added.
|
|
|
529
537
|
### Pipeline phase table
|
|
530
538
|
|
|
531
539
|
| Pipeline | Lifecycle phase | Entry point | Terminal states |
|
|
532
|
-
|
|
540
|
+
| --- | --- | --- | --- |
|
|
533
541
|
| `idea-pipeline.yaml` | Ideation (vague idea → feature + AC + task batch) | `/sp:dev-idea` | `handoff`, `cancelled` |
|
|
534
542
|
| `planning-pipeline.yaml` | Design (known slug/task → design handoff) | `/sp:dev-plan` | `handoff`, `cancelled` |
|
|
535
543
|
| `task-pipeline.yaml` | Execution (one task → done) | `/sp:dev-run` | `done`, `failed` |
|
|
@@ -691,7 +699,7 @@ The signal is emitted by the `discovery` state's brainstorm dispatch and written
|
|
|
691
699
|
determine routing:
|
|
692
700
|
|
|
693
701
|
| `design` var | `needs_design` signal | Route |
|
|
694
|
-
|
|
702
|
+
| --- | --- | --- |
|
|
695
703
|
| `skip` | (ignored) | `decompose` (skip system-design; brainstorm summary still recorded) |
|
|
696
704
|
| `auto` | `true` | `system-design` -> `design-approval` -> `decompose` |
|
|
697
705
|
| `auto` | `false` | `decompose` (skip system-design) |
|