@gobing-ai/spur 0.3.51 → 0.3.52
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/tasks/section-matrix.yaml +7 -2
- package/config/workflows/task-lifecycle.yaml +7 -6
- package/package.json +1 -1
- package/plugins/sp/agents/super-planner.md +3 -2
- package/plugins/sp/agents/super-reviewer.md +5 -0
- package/plugins/sp/commands/dev-review.md +1 -1
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/pr-reviewing.ts +22 -4
- package/plugins/sp/scripts/stage-registry-adapter.ts +40 -25
- package/plugins/sp/skills/code-improvement/SKILL.md +5 -3
- package/plugins/sp/skills/code-verification/SKILL.md +28 -31
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +47 -0
- package/plugins/sp/skills/functional-review/SKILL.md +16 -15
- package/plugins/sp/skills/spec-decomposition/references/decomposition.md +13 -14
- package/plugins/sp/skills/spur-cli/references/tasks/l3-guard-cheatsheet.md +7 -3
- package/plugins/sp/skills/spur-cli/references/tasks/section-editing.md +22 -13
- package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +22 -11
- package/plugins/sp/skills/spur-cli/references/tasks.md +16 -10
- package/plugins/sp/skills/spur-dev/SKILL.md +10 -5
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +5 -5
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +11 -4
- package/plugins/sp/skills/spur-dev/references/gate-checklists.md +5 -3
- package/plugins/sp/skills/spur-dev/references/section-batching.md +29 -13
- package/spur.js +331 -221
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
# Section-Status-Matrix: which sections are required/optional/forbidden per task status.
|
|
2
|
-
# Design §3.2 —
|
|
2
|
+
# Design §3.2 — the SOLE semantic authority for task section layout (F92 R1):
|
|
3
|
+
# * VALIDATION (`spur task check`, incl. target-aware `--as <status>` lifecycle guard)
|
|
4
|
+
# * CREATION (`spur task create` / `spur task batch-create` -> `buildTaskSkeleton`)
|
|
5
|
+
# There is NO hand-maintained creation or matrix fallback — packaged/compiled
|
|
6
|
+
# execution loads data copied/generated from THIS canonical file and fails loudly
|
|
7
|
+
# if no asset is reachable. Template files supply section BODY content + guidance
|
|
8
|
+
# only; they never own the heading list (which comes from this matrix).
|
|
3
9
|
#
|
|
4
10
|
# Each VARIANT (the unified template axis — TASK_VARIANTS) maps status →
|
|
5
11
|
# { required, optional, forbidden } section lists. Sections reference the canonical
|
|
6
12
|
# section vocabulary (§2.1). `gate: true` on a status makes L2 violations hard errors.
|
|
7
13
|
#
|
|
8
14
|
# The variant is selected by a task's `template:` frontmatter (default: `standard`).
|
|
9
|
-
# It drives BOTH validation (`spur task check`) AND creation (`buildTaskSkeleton`).
|
|
10
15
|
$schema: "@gobing-ai/spur/schemas/section-matrix.schema.json"
|
|
11
16
|
|
|
12
17
|
variants:
|
|
@@ -12,15 +12,16 @@ name: task-lifecycle
|
|
|
12
12
|
description: >
|
|
13
13
|
Task lifecycle FSM (design §2.3, §5.1). States are the canonical TaskStatus
|
|
14
14
|
vocabulary (DD-01); transitions encode the §2.3 graph; guards invoke
|
|
15
|
-
`${vars.spurBin} task check
|
|
15
|
+
`${vars.spurBin} task check --as <target>` at the wip→testing and testing→done
|
|
16
|
+
placements (F92 R3): each guard evaluates the task AS the transition target so
|
|
17
|
+
testing→done checks the `done` row instead of the current `testing` row.
|
|
16
18
|
`done` is re-enterable (reopen with a warning); `cancelled` is terminal.
|
|
17
|
-
Guard commands reference the check verb (0051)
|
|
18
|
-
today, behavioral wiring activates as the verb ships. Unconditional transitions
|
|
19
|
+
Guard commands reference the check verb (0051). Unconditional transitions
|
|
19
20
|
use the `always` guard (externally-driven via `requestTransition`, not
|
|
20
21
|
auto-advance).
|
|
21
22
|
|
|
22
23
|
Two-layer done gate (task 0292): this workflow's testing→done guard
|
|
23
|
-
(`spur task check --
|
|
24
|
+
(`spur task check --as done`, F92 R3 — target-aware) is the FIRST gate. The CLI verdict guard
|
|
24
25
|
(`packages/app/src/services/done-transition-guard.ts`) is the SECOND gate,
|
|
25
26
|
invoked by `apps/cli/src/commands/task.ts` for every `spur task update <wbs>
|
|
26
27
|
done` call — it reads `.spur/run/<wbs>-verdict.json` and blocks non-PASS
|
|
@@ -69,14 +70,14 @@ transitions:
|
|
|
69
70
|
guard:
|
|
70
71
|
kind: shell
|
|
71
72
|
options:
|
|
72
|
-
command: '$spurBin task check $wbs'
|
|
73
|
+
command: '$spurBin task check $wbs --as testing'
|
|
73
74
|
- from: testing
|
|
74
75
|
to: done
|
|
75
76
|
description: Testing passed — task complete
|
|
76
77
|
guard:
|
|
77
78
|
kind: shell
|
|
78
79
|
options:
|
|
79
|
-
command: '$spurBin task check $wbs --
|
|
80
|
+
command: '$spurBin task check $wbs --as done'
|
|
80
81
|
|
|
81
82
|
# Reopen: done → wip (warned, mandatory History entry)
|
|
82
83
|
- from: done
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/spur",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.52",
|
|
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",
|
|
@@ -183,8 +183,9 @@ When you fan out or dispatch a subagent, apply the four disciplines the SSOT
|
|
|
183
183
|
- [ ] Never replace yourself as orchestrator when `--agent` is set - it pins the step executor, not you.
|
|
184
184
|
- [ ] Never auto-approve a HITL gate inside a task unless `--auto` was passed (it sets `profile=auto`).
|
|
185
185
|
- [ ] Never silent-pick multi-candidate router stops; surface HITL (batch `--auto` does not break ties).
|
|
186
|
-
- [ ] Never mutate the corpus - the
|
|
187
|
-
|
|
186
|
+
- [ ] Never mutate the corpus - the review coordinator writes `## Review`, the pipeline's `record`
|
|
187
|
+
step writes `## Testing` deterministically (bare-`## Review` fallback only — never an
|
|
188
|
+
overwrite of authored Review, F92 0593 R1); your sole output is the batch report (+ optional recovery dispatch of an existing
|
|
188
189
|
`/sp:dev-*` command).
|
|
189
190
|
- [ ] Never run tasks in parallel unless the operator requested parallel mode and the
|
|
190
191
|
`sp:parallel-execution` checks pass. If checks fail, serialize and report why.
|
|
@@ -38,6 +38,11 @@ You are a **thin delegator**. You do not own the review logic; the three skills
|
|
|
38
38
|
| SECUA quality | `sp:code-verification` (review mode) | Is the code correct/secure/efficient/usable? |
|
|
39
39
|
| Architectural depth | `sp:code-improvement` | Is the architecture deep / testable? |
|
|
40
40
|
|
|
41
|
+
**Section ownership (F92 0593 R1).** Component skills **return review fragments only** — never
|
|
42
|
+
write `## Review`. This coordinator is the single `## Review` writer in coordinated/pipeline mode
|
|
43
|
+
(combined fragment merge). `spur task record`'s bare-Review backfill is a standalone compatibility
|
|
44
|
+
fallback only and never overwrites authored Review.
|
|
45
|
+
|
|
41
46
|
Your job: establish scope, dispatch each requested dimension to its skill, collect findings, merge
|
|
42
47
|
them into a ranked report, and write the report to the task's `## Review` section (pipeline mode) or
|
|
43
48
|
emit it as advisory output (standalone mode).
|
|
@@ -27,6 +27,6 @@ For shared semantics, see the [flag glossary](../skills/spur-dev/references/flag
|
|
|
27
27
|
## Implementation
|
|
28
28
|
|
|
29
29
|
- Apply the [inline-default execution-surface contract](../skills/spur-dev/references/cross-cutting.md#inline-default-execution-surface).
|
|
30
|
-
- WBS mode (`<wbs>`): `Skill(skill="sp:functional-review", args="<wbs> $ARGUMENTS")` + `Skill(skill="sp:code-verification", args="review $ARGUMENTS")` + `Skill(skill="sp:code-improvement", args="<wbs> $ARGUMENTS")` (functional traceability + SECUA framework + architectural depth;
|
|
30
|
+
- WBS mode (`<wbs>`): `Skill(skill="sp:functional-review", args="<wbs> $ARGUMENTS")` + `Skill(skill="sp:code-verification", args="review $ARGUMENTS")` + `Skill(skill="sp:code-improvement", args="<wbs> $ARGUMENTS")` (functional traceability + SECUA framework + architectural depth; the three skills return review fragments and the coordinator (`sp:super-reviewer`) writes the combined `## Review` — F92 0593 R1)
|
|
31
31
|
- Path mode (`<path>`): `Skill(skill="sp:code-verification", args="review $ARGUMENTS")` + `Skill(skill="sp:code-improvement", args="<path> $ARGUMENTS")` (advisory SECUA quality + architectural depth; performs no task mutation)
|
|
32
32
|
- `--fix`: Deprecated (no-op + warning message; route remediation to `/sp:dev-verify --fix`). **`--next` removed** (feature H8, 2026-07-31) — it was a deprecated no-op; route progression to `/sp:dev-next`. **was: `--next` deprecated no-op.**
|
package/plugins/sp/plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.52",
|
|
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"]
|
|
@@ -25,7 +25,7 @@ import { join } from 'node:path';
|
|
|
25
25
|
|
|
26
26
|
// ─── Types ──────────────────────────────────────────────────────────────────
|
|
27
27
|
|
|
28
|
-
interface CmdResult {
|
|
28
|
+
export interface CmdResult {
|
|
29
29
|
code: number;
|
|
30
30
|
stdout: string;
|
|
31
31
|
stderr: string;
|
|
@@ -110,10 +110,14 @@ interface ParsedArgs {
|
|
|
110
110
|
booleans: Set<string>;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
// ─── Process runner (git/gh
|
|
113
|
+
// ─── Process runner (single seam; every git/gh call routes through run) ─────
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
/** How an external command is executed. The only seam between this script and git/gh. */
|
|
116
|
+
export type CommandRunner = (cmd: readonly string[]) => CmdResult;
|
|
117
|
+
|
|
118
|
+
/** Real execution: resolve the binary from PATH and capture its output. The default runner. */
|
|
119
|
+
export const spawnRunner: CommandRunner = (cmd) => {
|
|
120
|
+
// Pass env explicitly so both Node and Bun resolve git/gh against the caller's PATH.
|
|
117
121
|
const proc = spawnSync(cmd[0] ?? '', [...cmd.slice(1)], { encoding: 'utf8', env: process.env });
|
|
118
122
|
return {
|
|
119
123
|
code: proc.status ?? 1,
|
|
@@ -121,6 +125,20 @@ function run(cmd: readonly string[]): CmdResult {
|
|
|
121
125
|
stderr: proc.stderr ?? '',
|
|
122
126
|
error: proc.error?.message,
|
|
123
127
|
};
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
let runner: CommandRunner = spawnRunner;
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Swap the git/gh runner. Tests inject an in-process stub so a suite of CLI cases costs no
|
|
134
|
+
* subprocess spawns; call with no argument to restore real execution.
|
|
135
|
+
*/
|
|
136
|
+
export function setCommandRunner(next?: CommandRunner): void {
|
|
137
|
+
runner = next ?? spawnRunner;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function run(cmd: readonly string[]): CmdResult {
|
|
141
|
+
return runner(cmd);
|
|
124
142
|
}
|
|
125
143
|
|
|
126
144
|
function runOk(cmd: readonly string[], what: string): string {
|
|
@@ -30,7 +30,7 @@ import { join } from 'node:path';
|
|
|
30
30
|
// ─── Inline type definitions (mirrors packages/domain/src/stage-registry/) ─
|
|
31
31
|
|
|
32
32
|
export type SchemaVersion = { major: number; minor: number };
|
|
33
|
-
export const CURRENT_SCHEMA_VERSION: SchemaVersion = { major: 1, minor:
|
|
33
|
+
export const CURRENT_SCHEMA_VERSION: SchemaVersion = { major: 1, minor: 3 };
|
|
34
34
|
export const AUTHORITY_LANES = ['registry', 'workflow', 'skill', 'cli', 'adapter'] as const;
|
|
35
35
|
export type AuthorityLane = (typeof AUTHORITY_LANES)[number];
|
|
36
36
|
// Mirrors packages/domain/src/stage-registry/schema.ts MUTATION_CLASSES. Pinned by
|
|
@@ -65,6 +65,8 @@ export type ContextLayerName = (typeof CONTEXT_LAYER_NAMES)[number];
|
|
|
65
65
|
export interface StageArtifact {
|
|
66
66
|
kind: string;
|
|
67
67
|
direction: ArtifactDirection;
|
|
68
|
+
/** Exact artifact identity (F92 0593 R2): task-section name or artifact file basename. */
|
|
69
|
+
identity?: string;
|
|
68
70
|
description?: string;
|
|
69
71
|
required?: boolean;
|
|
70
72
|
}
|
|
@@ -308,10 +310,7 @@ export const REGISTERED_STAGES: StageRecord[] = [
|
|
|
308
310
|
],
|
|
309
311
|
reasoning_skill: 'sp:spur-dev',
|
|
310
312
|
required_references: ['references/dev-operations.md', 'spur-dev/references/decision-brief.md'],
|
|
311
|
-
gates: [
|
|
312
|
-
{ name: 'refine-skip-gate', timing: 'pre', description: 'Skip sections that already meet L3' },
|
|
313
|
-
{ name: 'l4-advisory', timing: 'post', min_verdict: 'pass', description: 'L4 advisory surface' },
|
|
314
|
-
],
|
|
313
|
+
gates: [],
|
|
315
314
|
mutation_class: 'corpus',
|
|
316
315
|
retry: defaultRetry,
|
|
317
316
|
model_policy: policy('refine', ['capable-2']),
|
|
@@ -346,7 +345,7 @@ export const REGISTERED_STAGES: StageRecord[] = [
|
|
|
346
345
|
description: 'dev-run --mode implement: code edits in worktree',
|
|
347
346
|
artifacts: [
|
|
348
347
|
{ kind: 'worktree-diff', direction: 'output', required: true },
|
|
349
|
-
{ kind: 'task-section', direction: '
|
|
348
|
+
{ kind: 'task-section', direction: 'output', required: true, identity: 'Solution' },
|
|
350
349
|
],
|
|
351
350
|
reasoning_skill: 'sp:code-implementation',
|
|
352
351
|
gates: [],
|
|
@@ -370,7 +369,7 @@ export const REGISTERED_STAGES: StageRecord[] = [
|
|
|
370
369
|
{ kind: 'coverage-report', direction: 'output', required: false },
|
|
371
370
|
],
|
|
372
371
|
reasoning_skill: 'sp:code-testing',
|
|
373
|
-
gates: [
|
|
372
|
+
gates: [],
|
|
374
373
|
mutation_class: 'tests',
|
|
375
374
|
retry: defaultRetry,
|
|
376
375
|
model_policy: policy('test', ['capable-1']),
|
|
@@ -403,13 +402,13 @@ export const REGISTERED_STAGES: StageRecord[] = [
|
|
|
403
402
|
id: 'verify',
|
|
404
403
|
description: 'dev-verify: SECUA review + requirements traceability',
|
|
405
404
|
artifacts: [
|
|
406
|
-
{ kind: '
|
|
407
|
-
{ kind: '
|
|
405
|
+
{ kind: 'worktree-diff', direction: 'input', required: true },
|
|
406
|
+
{ kind: 'verdict-artifact', direction: 'output', required: true, identity: '<wbs>-verdict.json' },
|
|
408
407
|
],
|
|
409
408
|
reasoning_skill: 'sp:code-verification',
|
|
410
409
|
gates: [
|
|
411
410
|
{ name: 'verdict-artifact', timing: 'post', min_verdict: 'pass' },
|
|
412
|
-
{ name: 'strict-core', timing: 'post', description: 'L3 core findings must pass' },
|
|
411
|
+
{ name: 'strict-core', timing: 'post', min_verdict: 'pass', description: 'L3 core findings must pass' },
|
|
413
412
|
],
|
|
414
413
|
mutation_class: 'verdict',
|
|
415
414
|
retry: { max_attempts: 2, terminal_stop: 'escalate', timeout_seconds: 600 },
|
|
@@ -423,10 +422,7 @@ export const REGISTERED_STAGES: StageRecord[] = [
|
|
|
423
422
|
id: 'wrap',
|
|
424
423
|
aliases: ['dev-wrap'],
|
|
425
424
|
description: 'dev-wrap: learnings/doc-sync/feature transition',
|
|
426
|
-
artifacts: [
|
|
427
|
-
{ kind: 'learning-entry', direction: 'output' },
|
|
428
|
-
{ kind: 'task-section', direction: 'output', description: 'Testing/Review updated' },
|
|
429
|
-
],
|
|
425
|
+
artifacts: [{ kind: 'learning-entry', direction: 'output' }],
|
|
430
426
|
reasoning_skill: 'sp:spur-dev',
|
|
431
427
|
gates: [
|
|
432
428
|
{
|
|
@@ -447,9 +443,9 @@ export const REGISTERED_STAGES: StageRecord[] = [
|
|
|
447
443
|
schema_version: CURRENT_SCHEMA_VERSION,
|
|
448
444
|
id: 'review',
|
|
449
445
|
description: 'dev-review: multi-dimensional code review (functional/SECUA/architecture)',
|
|
450
|
-
artifacts: [{ kind: 'review-findings', direction: 'output', required: true }],
|
|
446
|
+
artifacts: [{ kind: 'review-findings', direction: 'output', required: true, identity: 'Review' }],
|
|
451
447
|
reasoning_skill: 'sp:code-verification',
|
|
452
|
-
gates: [
|
|
448
|
+
gates: [],
|
|
453
449
|
mutation_class: 'verdict',
|
|
454
450
|
retry: { max_attempts: 2, terminal_stop: 'block', timeout_seconds: 300 },
|
|
455
451
|
model_policy: policy('review', ['capable-1']),
|
|
@@ -457,6 +453,32 @@ export const REGISTERED_STAGES: StageRecord[] = [
|
|
|
457
453
|
observability: [event('stage-started'), event('findings-produced')],
|
|
458
454
|
execution: inlineInline(),
|
|
459
455
|
},
|
|
456
|
+
{
|
|
457
|
+
schema_version: CURRENT_SCHEMA_VERSION,
|
|
458
|
+
id: 'record',
|
|
459
|
+
aliases: ['dev-record'],
|
|
460
|
+
description: 'record: deterministic Testing write-back from the verdict artifact; bare-Review fallback only',
|
|
461
|
+
artifacts: [
|
|
462
|
+
{ kind: 'verdict-artifact', direction: 'input', required: true, identity: '<wbs>-verdict.json' },
|
|
463
|
+
{ kind: 'task-section', direction: 'output', required: true, identity: 'Testing' },
|
|
464
|
+
{
|
|
465
|
+
kind: 'task-section',
|
|
466
|
+
direction: 'output',
|
|
467
|
+
required: false,
|
|
468
|
+
identity: 'Review',
|
|
469
|
+
description:
|
|
470
|
+
'fallback-only: backfills Review only when the section is bare; never overwrites authored Review',
|
|
471
|
+
},
|
|
472
|
+
],
|
|
473
|
+
reasoning_skill: 'inline',
|
|
474
|
+
gates: [],
|
|
475
|
+
mutation_class: 'corpus',
|
|
476
|
+
retry: { max_attempts: 1, terminal_stop: 'block', timeout_seconds: 120 },
|
|
477
|
+
model_policy: policy('record'),
|
|
478
|
+
context_layers: [layer('task-state')],
|
|
479
|
+
observability: [event('stage-started')],
|
|
480
|
+
execution: inlineDeterministic('cli'),
|
|
481
|
+
},
|
|
460
482
|
{
|
|
461
483
|
schema_version: CURRENT_SCHEMA_VERSION,
|
|
462
484
|
id: 'dogfood',
|
|
@@ -467,15 +489,7 @@ export const REGISTERED_STAGES: StageRecord[] = [
|
|
|
467
489
|
],
|
|
468
490
|
reasoning_skill: 'sp:dogfood-testing',
|
|
469
491
|
required_references: ['references/monitor-ledger.md', 'references/report-template.md'],
|
|
470
|
-
gates: [
|
|
471
|
-
{ name: 'detect-pipeline-driving', timing: 'pre', description: 'Refuse dogfood when driving a pipeline' },
|
|
472
|
-
{
|
|
473
|
-
name: 'report-validate',
|
|
474
|
-
timing: 'post',
|
|
475
|
-
min_verdict: 'pass',
|
|
476
|
-
description: 'Report must pass schema validation',
|
|
477
|
-
},
|
|
478
|
-
],
|
|
492
|
+
gates: [],
|
|
479
493
|
mutation_class: 'driver',
|
|
480
494
|
retry: { max_attempts: 3, terminal_stop: 'block', timeout_seconds: 600 },
|
|
481
495
|
model_policy: policy('dogfood'),
|
|
@@ -568,6 +582,7 @@ export function listStages(): StageLookupEntry[] {
|
|
|
568
582
|
'quality-gate': '/sp:dev-fixall',
|
|
569
583
|
verify: '/sp:dev-verify',
|
|
570
584
|
wrap: '/sp:dev-wrap',
|
|
585
|
+
record: 'spur task record',
|
|
571
586
|
review: '/sp:dev-review',
|
|
572
587
|
dogfood: '/sp:dev-dogfood',
|
|
573
588
|
handover: 'inline (dev-handover)',
|
|
@@ -187,9 +187,11 @@ When invoked as the `--focus architecture` dimension of `/sp:dev-review`:
|
|
|
187
187
|
- The scope is the task's diff (pipeline) or the `path` arg (standalone).
|
|
188
188
|
- `blocker`/`major` candidates block the `approve(HITL)` gate alongside any SECUA blockers from
|
|
189
189
|
`sp:code-verification`.
|
|
190
|
-
- The candidate list is
|
|
191
|
-
`
|
|
192
|
-
|
|
190
|
+
- The candidate list is returned as a **review fragment**; the review coordinator
|
|
191
|
+
(`sp:super-reviewer` under `/sp:dev-review`) merges it into the combined `## Review` section —
|
|
192
|
+
it is never written by `record`, which backfills `## Review` only when the section is bare
|
|
193
|
+
(fallback-only, F92 0593 R1).
|
|
194
|
+
- This skill does **not** write to the task file directly — the coordinator (or the operator) does.
|
|
193
195
|
|
|
194
196
|
Standalone, the skill emits the candidate list as advisory output; the operator acts on it.
|
|
195
197
|
|
|
@@ -27,15 +27,16 @@ metadata:
|
|
|
27
27
|
|
|
28
28
|
The **verifier** in the Spur execution loop. A coding agent reports "done" with overconfidence;
|
|
29
29
|
this skill is the deterministic counterweight that proves — or disproves — the claim against the
|
|
30
|
-
task's own requirements and Acceptance Criteria, then
|
|
31
|
-
through CLI verbs
|
|
30
|
+
task's own requirements and Acceptance Criteria, then emits the canonical verdict artifact
|
|
31
|
+
through CLI verbs (section writes are owned by the review coordinator and the deterministic
|
|
32
|
+
`record` step — F92 0593 R1).
|
|
32
33
|
|
|
33
34
|
It backs two commands:
|
|
34
35
|
|
|
35
36
|
| Command | Mode | Input | Output |
|
|
36
37
|
|---------|------|-------|--------|
|
|
37
|
-
| `/sp:dev-verify <wbs>` | **verify** | a task WBS |
|
|
38
|
-
| `/sp:dev-review <wbs>` | **review** | a task WBS (diff scope) | three-dimensional findings → `## Review`
|
|
38
|
+
| `/sp:dev-verify <wbs>` | **verify** | a task WBS | `.spur/run/<wbs>-verdict.json`; `record` transcribes `## Testing` |
|
|
39
|
+
| `/sp:dev-review <wbs>` | **review** (coordinator) | a task WBS (diff scope) | merged three-dimensional findings → `## Review` |
|
|
39
40
|
|
|
40
41
|
The verify mode is the **completion gate's evidence source**: it emits a machine verdict the
|
|
41
42
|
`task-pipeline.yaml` workflow reads before allowing `record → done`. A `PASS` clears the gate; a
|
|
@@ -243,34 +244,32 @@ blocker finding → FAIL; core PARTIAL or an unresolved major finding (no FAIL)
|
|
|
243
244
|
MET or justified N/A → PASS. Minor/advisory findings do not block. Only `PASS` clears the pipeline
|
|
244
245
|
completion gate (`PARTIAL`/`FAIL` route the pipeline to `failed`).
|
|
245
246
|
|
|
246
|
-
### Step 10 —
|
|
247
|
+
### Step 10 — Emit the verdict artifact (the only verify output)
|
|
247
248
|
|
|
248
|
-
Assemble the evidence and
|
|
249
|
+
Assemble the evidence and **emit the canonical verdict artifact** — verification writes no task
|
|
250
|
+
section (F92 0593 R1). Under the pipeline, the output is captured as
|
|
251
|
+
`.spur/run/<wbs>-verify-answer.txt`; a deterministic shell step derives
|
|
252
|
+
`.spur/run/<wbs>-verdict.json`, and the `record` step transcribes `## Testing` from it.
|
|
253
|
+
|
|
254
|
+
**Standalone** (`/sp:dev-verify` outside the pipeline), write the artifact yourself, then invoke
|
|
255
|
+
the deterministic Testing writer `spur task record` (section authorship never happens here):
|
|
249
256
|
|
|
250
257
|
```bash
|
|
251
|
-
#
|
|
252
|
-
|
|
253
|
-
spur task update <wbs> --section Testing --from-file /tmp/<wbs>-testing.md
|
|
258
|
+
# write .spur/run/<wbs>-verdict.json (shape in references/verdict-schema.md), then:
|
|
259
|
+
spur task record <wbs> --verdict-file .spur/run/<wbs>-verdict.json # renders ## Testing
|
|
254
260
|
```
|
|
255
261
|
|
|
256
262
|
> **Corrections: the answer file is the source of truth.** `spur task record` re-transcribes
|
|
257
|
-
> `## Testing` from the verdict artifact
|
|
258
|
-
>
|
|
259
|
-
> --from-answer <file>` → re-record. `--section` is initial authorship only.
|
|
260
|
-
|
|
261
|
-
> **Do not write `## Review` directly in verify mode.** The `## Review` section is owned by the
|
|
262
|
-
> `review` step (`/sp:dev-review`), which dispatches `functional-review` + `code-verification`
|
|
263
|
-
> review mode + `code-improvement`. The `record` step backfills `## Review` from the verdict
|
|
264
|
-
> artifact only if the section is bare (`sectionIsBare` guard, `task-service.ts:485`). Writing
|
|
265
|
-
> `## Review` here bypasses that guard and destroys the review step's three-dimensional findings.
|
|
266
|
-
|
|
267
|
-
Section bodies passed to `spur task update --section` must be **body-only**. Do not put a same-level
|
|
268
|
-
heading inside any section body; the task writer strips same-level headings to prevent phantom
|
|
269
|
-
sections. Concretely:
|
|
263
|
+
> `## Testing` from the verdict artifact — direct `--section Testing` writes are futile. Fix
|
|
264
|
+
> `.spur/run/<wbs>-verify-answer.txt` → `spur task verdict <wbs> --from-answer <file>` → re-record.
|
|
270
265
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
266
|
+
> **Do not write `## Review` directly, ever.** The `## Review` section is owned by the
|
|
267
|
+
> `review` coordinator (`/sp:dev-review` → `sp:super-reviewer`), which merges
|
|
268
|
+
> `functional-review` + `code-verification` review mode + `code-improvement` fragments. The
|
|
269
|
+
> `record` step backfills `## Review` from the verdict artifact **only** if the section is bare
|
|
270
|
+
> (`sectionIsBare` guard, `task-service.ts`) — a standalone compatibility fallback, never an
|
|
271
|
+
> overwrite of authored Review. Writing `## Review` here destroys the review step's
|
|
272
|
+
> three-dimensional findings.
|
|
274
273
|
|
|
275
274
|
### Step 11 — State the verdict and hand off (the gate contract)
|
|
276
275
|
|
|
@@ -311,10 +310,8 @@ canonical.
|
|
|
311
310
|
`.spur/run/<wbs>-verify-answer.txt`. A deterministic shell step then derives
|
|
312
311
|
`.spur/run/<wbs>-verdict.json` from it plus an independent `spur task check` (R9; the agent
|
|
313
312
|
reporting PASS in prose is necessary but not sufficient — the artifact is never left to the agent's
|
|
314
|
-
discretion).
|
|
315
|
-
|
|
316
|
-
the record step's `sectionIsBare` guard (`task-service.ts:485`) preserves any non-bare Review
|
|
317
|
-
content. Verify mode never writes `## Review`.
|
|
313
|
+
discretion). Section transcription follows the Step 10 contract (record → `## Testing`; bare-only
|
|
314
|
+
Review fallback; verify never writes sections).
|
|
318
315
|
|
|
319
316
|
**Standalone** (`/sp:dev-verify` outside the pipeline — no answer-file capture exists), write the
|
|
320
317
|
artifact yourself; shape and field-by-field contract in
|
|
@@ -455,8 +452,8 @@ re-audit is never misread as a successful `testing -> done` (dev-verify.md `--ne
|
|
|
455
452
|
## Mode: review (`/sp:dev-review`)
|
|
456
453
|
|
|
457
454
|
The source-oriented path: SECUA review of a task's diff without the full traceability verdict. Runs
|
|
458
|
-
Steps 3 + 7
|
|
459
|
-
|
|
455
|
+
Steps 3 + 7 and returns a **review fragment** — no verdict artifact, no section write, no `done`
|
|
456
|
+
gate (F92 0593 R1); the coordinator (`sp:super-reviewer`) merges fragments into `## Review`.
|
|
460
457
|
|
|
461
458
|
Flags: `--agent <inline|auto|name>` (execution surface — inline default, with named escalation triggers taking precedence), `--auto` (no confirmations), `--fix <none|blockers-first|all>` (post-review repair), and `--focus <all|security|efficiency|correctness|usability|architecture>` (SECUA dimensions). Apply the [central contract](../spur-dev/references/cross-cutting.md#inline-default-execution-surface) before starting the review.
|
|
462
459
|
|
|
@@ -139,3 +139,50 @@ completion without an explicit PASS artifact.
|
|
|
139
139
|
- **Read:** by the workflow gate on the `verify → record` transition.
|
|
140
140
|
- **Location:** `.spur/run/` (the run-scratch dir the pipeline's `record` step also uses).
|
|
141
141
|
- **Lifetime:** per execution attempt; overwritten on the next verify of the same WBS.
|
|
142
|
+
|
|
143
|
+
## Optional check severity (task 0592, feature F92)
|
|
144
|
+
|
|
145
|
+
`checks[]` rows may carry an optional `severity` field so the aggregation policy can distinguish a
|
|
146
|
+
blocking review finding from a non-blocking one:
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
checks: Array<{
|
|
150
|
+
name: string; // aliases accepted on read: `check`, `id`
|
|
151
|
+
status: 'pass' | 'fail' | 'warn';
|
|
152
|
+
evidence: string;
|
|
153
|
+
severity?: 'blocker' | 'major' | 'minor' | 'advisory';
|
|
154
|
+
}>;
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Compatibility aliases: check label key
|
|
158
|
+
|
|
159
|
+
Producers SHOULD write the check label as `name`. Consumers ALSO accept `check` and `id` as
|
|
160
|
+
aliases, normalized to `name` in the same single place the `scenario`→`id` coverage alias is
|
|
161
|
+
normalized (`checkSchema` in `verify-verdict.ts`). A row carrying none of the three is
|
|
162
|
+
**structurally invalid** — an unnamed check cannot be matched by the aggregation policy's
|
|
163
|
+
`task-check` detection, so it would silently exempt a failed task-check from the completion rule.
|
|
164
|
+
|
|
165
|
+
The alias resolves for raw rows as well as parsed ones (`checkRowName`), because
|
|
166
|
+
`aggregateVerifyVerdict` and the done guard's task-check lookup both run over unparsed artifacts.
|
|
167
|
+
Precedence is `name` → `check` → `id`, first non-empty string wins.
|
|
168
|
+
|
|
169
|
+
## Canonical runtime contract + one aggregation policy (task 0592)
|
|
170
|
+
|
|
171
|
+
The prose shape above is executed by a single runtime-validated contract:
|
|
172
|
+
`packages/app/src/services/verify-verdict.ts`. It owns:
|
|
173
|
+
|
|
174
|
+
- **`verifyVerdictSchema`** (Zod) — validates the persisted artifact and distinguishes
|
|
175
|
+
**missing** (file absent / empty), **malformed** (bad JSON), **structurally invalid**
|
|
176
|
+
(`invalid` outcome), and **valid** (with case-normalized `verdict`). The `scenario`→`id` coverage
|
|
177
|
+
alias and the `check`/`id`→`name` check-label alias are both normalized here, in exactly one place.
|
|
178
|
+
- **`aggregateVerifyVerdict`** — the ONE aggregation policy every verdict consumer uses (answer
|
|
179
|
+
derivation, persisted-artifact consistency checks, task/feature validation, record rendering,
|
|
180
|
+
and the done-transition gate). Requirements/AC use `MET`/`PARTIAL`/`UNMET`/`N/A`. Checks:
|
|
181
|
+
non-pass **blocker** → FAIL, non-pass **major** → PARTIAL, **minor**/**advisory** do not block;
|
|
182
|
+
legacy rows without a severity map `fail` → FAIL and `warn` → PARTIAL. An independent task-check
|
|
183
|
+
failure can never yield PASS.
|
|
184
|
+
- A row-less artifact aggregates to UNKNOWN — a stored PASS that does not recompute to PASS
|
|
185
|
+
(including a row-less PASS) is treated as non-PASS at the done gate. The done-transition choke
|
|
186
|
+
point (`done-transition-guard.ts` `evaluateDoneTransition`) is the final authority; workflow JSON
|
|
187
|
+
routing may select `verify → record/failed` but cannot weaken the final transition.
|
|
188
|
+
- `--force-done --reason` on `spur task update <wbs> done` remains the sole auditable override.
|
|
@@ -241,16 +241,16 @@ all core requirements MET → PASS
|
|
|
241
241
|
`PARTIAL` blocks the gate exactly like `FAIL` — the distinction only tells the operator *how far
|
|
242
242
|
off* delivery is.
|
|
243
243
|
|
|
244
|
-
### Step 7 —
|
|
244
|
+
### Step 7 — Return the review fragment (no section write)
|
|
245
245
|
|
|
246
|
-
|
|
247
|
-
`| Priority | Dimension | Location | Finding |`
|
|
246
|
+
Assemble the findings into a **review fragment** — the priority-table lead + the per-requirement
|
|
247
|
+
traceability table. The fragment MUST lead with a `| Priority | Dimension | Location | Finding |`
|
|
248
|
+
table (the L3 `hasPopulatedPriorityTable` gate at
|
|
248
249
|
`task-check.ts:96-106` requires at least one `P[1-4]` row with non-placeholder siblings — any other
|
|
249
250
|
shape, e.g. `| Req | Status | Evidence |` alone, is structurally rejected and denies the
|
|
250
251
|
`wip→testing` transition). Use the same canonical shape as `sp:code-verification`:
|
|
251
252
|
|
|
252
|
-
```
|
|
253
|
-
cat > /tmp/<wbs>-functional.md <<'BODY'
|
|
253
|
+
```markdown
|
|
254
254
|
| Priority | Dimension | Location | Finding |
|
|
255
255
|
| --- | --- | --- | --- |
|
|
256
256
|
| P4 | — | — | No P1–P3 findings; functional verdict PASS |
|
|
@@ -260,16 +260,17 @@ cat > /tmp/<wbs>-functional.md <<'BODY'
|
|
|
260
260
|
| R1 | MET | `src/api/users.ts:42` — `createUser()` |
|
|
261
261
|
| R2 | PARTIAL | `src/api/users.ts:42` — basic only; MISSING duplicate-email handling |
|
|
262
262
|
| R3 | UNMET | no implementation found; searched src/api/, src/services/ |
|
|
263
|
-
BODY
|
|
264
|
-
spur task update <wbs> --section Review --from-file /tmp/<wbs>-functional.md
|
|
265
|
-
rm /tmp/<wbs>-functional.md
|
|
266
263
|
```
|
|
267
264
|
|
|
268
265
|
For a PARTIAL/FAIL verdict, replace the P4 row with the actual P1–P3 findings ranked by severity.
|
|
269
266
|
The priority table leads; the traceability table follows for per-requirement detail.
|
|
270
267
|
|
|
271
|
-
|
|
272
|
-
|
|
268
|
+
**Fragment-only discipline (F92 0593 R1).** In coordinated mode (dispatched by `/sp:dev-review` /
|
|
269
|
+
`sp:super-reviewer`), do **not** write `## Review` — return the fragment to the coordinator, which
|
|
270
|
+
merges the functional + SECUA + architecture fragments into the combined `## Review` section. Only
|
|
271
|
+
`sp:super-reviewer` (the review coordinator) writes `## Review`; direct component-skill use is
|
|
272
|
+
advisory output. `spur task record` backfills a **bare** `## Review` from the verdict artifact as a
|
|
273
|
+
standalone compatibility fallback only and never overwrites authored Review (F92 0593 R1).
|
|
273
274
|
|
|
274
275
|
### Step 8 — Report
|
|
275
276
|
|
|
@@ -289,11 +290,11 @@ Include the per-requirement traceability table in the report:
|
|
|
289
290
|
| R3 | UNMET | no implementation found; searched src/api/, src/services/ |
|
|
290
291
|
```
|
|
291
292
|
|
|
292
|
-
**Under the pipeline**, `sp:functional-review` is
|
|
293
|
-
|
|
294
|
-
`record` step transcribes only `## Testing` from the
|
|
295
|
-
`task-
|
|
296
|
-
|
|
293
|
+
**Under the pipeline**, `sp:functional-review` is a component of `/sp:dev-review`: it returns its
|
|
294
|
+
fragment to the coordinator (`sp:super-reviewer`), which writes the combined `## Review`. The
|
|
295
|
+
`record` step transcribes only `## Testing` from the verdict artifact and backfills `## Review`
|
|
296
|
+
only when the section is bare (`sectionIsBare` guard, `task-service.ts`); it never overwrites the
|
|
297
|
+
coordinator's authored Review. Keep the priority-table lead stable in the fragment so the L3 gate
|
|
297
298
|
stays satisfied through `record` → `done`.
|
|
298
299
|
|
|
299
300
|
---
|
|
@@ -147,8 +147,9 @@ blank Design before implement (`/sp:dev-refine` / `dev-refineall`).
|
|
|
147
147
|
The `template` field is the **single variant axis** (TASK_VARIANTS): it selects the section
|
|
148
148
|
layout (the `section-matrix.yaml` variant), the scaffold body file
|
|
149
149
|
(`.spur/templates/task/<variant>.md`), and is written to the task's `template:` frontmatter.
|
|
150
|
-
Which sections actually appear is **
|
|
151
|
-
|
|
150
|
+
Which sections actually appear is **status-driven** by the runtime matrix — query
|
|
151
|
+
`spur task sections <wbs> list --json` rather than assuming a fixed list — pick the variant by
|
|
152
|
+
*purpose*, not by a section checklist.
|
|
152
153
|
|
|
153
154
|
| Variant | When to use |
|
|
154
155
|
|---------|-------------|
|
|
@@ -464,18 +465,16 @@ with research/design/implement/integrate/test as `## Plan` steps.
|
|
|
464
465
|
|
|
465
466
|
A task created with a spec (a `--feature` link, or a batch item carrying `background`/
|
|
466
467
|
`requirements`) lands at **`todo`** — "ready to execute" (§2.3). A bare capture lands at
|
|
467
|
-
**`backlog`** — "still preparing". The
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
| `testing` | verifying | Solution, Testing |
|
|
478
|
-
| `done` | shipped | Solution, Testing, Review (gated) |
|
|
468
|
+
**`backlog`** — "still preparing". The runtime section matrix decides which sections a task
|
|
469
|
+
carries at each status; the producer renders them with invisible HTML guidance comments. You do
|
|
470
|
+
**not** hand-build the section list — `spur task create` / `batch-create` does it from the matrix.
|
|
471
|
+
|
|
472
|
+
**Query the runtime contract — never restate a status-to-section table (F92 0593 R2).**
|
|
473
|
+
`spur task sections <wbs> list --json` returns the matrix required/optional/forbidden sections
|
|
474
|
+
per status; `spur task check <wbs> --json` returns what the gate requires at the current status.
|
|
475
|
+
Both replace any static "sections present at stage X" projection. The section *content* guidance
|
|
476
|
+
below (Design = the decision record, Solution = the change-map) is prose ownership, not a
|
|
477
|
+
section-layout authority.
|
|
479
478
|
|
|
480
479
|
**Design (written at `todo`, for HITL review) = the decision record — WHAT/WHY:**
|
|
481
480
|
the chosen approach + a one-line reason, rejected alternatives, key interface/type **signatures**
|
|
@@ -8,7 +8,10 @@ see_also:
|
|
|
8
8
|
|
|
9
9
|
# Task L3 guard cheat sheet
|
|
10
10
|
|
|
11
|
-
Read this before writing `Solution`, `Testing`, or `Review`.
|
|
11
|
+
Read this before writing `Solution`, `Testing`, or `Review`. Section **writers** are one per
|
|
12
|
+
evidence section (F92 0593 R1): implement owns `Solution`, the review coordinator
|
|
13
|
+
(`/sp:dev-review`) owns `Review`, and `spur task record` owns `Testing` (bare-`## Review`
|
|
14
|
+
backfill is fallback-only). The owning implementations are
|
|
12
15
|
`.spur/workflows/task-lifecycle.yaml`, `hasPopulatedPriorityTable()` in
|
|
13
16
|
`packages/app/src/services/task-check.ts`, the verdict normalizers in
|
|
14
17
|
`packages/app/src/services/task-record.ts`, and `TASK_CANONICAL_SECTIONS` in
|
|
@@ -25,8 +28,9 @@ done → wip
|
|
|
25
28
|
backlog|todo|wip|testing|blocked → cancelled
|
|
26
29
|
```
|
|
27
30
|
|
|
28
|
-
- `wip → testing` runs `spur task check <wbs
|
|
29
|
-
- `testing → done` runs `spur task check <wbs> --
|
|
31
|
+
- `wip → testing` runs `spur task check <wbs> --as testing` (F92 R3 — target-aware).
|
|
32
|
+
- `testing → done` runs `spur task check <wbs> --as done` (evaluates the `done` row incl. `gate:true`),
|
|
33
|
+
followed by the PASS-verdict gate. `--strict-core` remains as a compatibility alias only.
|
|
30
34
|
- Invalid: `todo → testing`, `todo → done`, and `wip → done`.
|
|
31
35
|
- Normal path: `backlog → todo → wip → testing → done`.
|
|
32
36
|
|