@gobing-ai/spur 0.2.5 → 0.2.8
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/package.json +8 -11
- package/schemas/section-matrix.schema.json +68 -0
- package/schemas/spur-config.schema.json +71 -1
- package/schemas/state-machine-workflow.schema.json +4 -0
- package/schemas/task-batch.schema.json +53 -0
- package/spur-cli/config/config.example.yaml +25 -1
- package/spur-cli/config/rules/boundary/config-loading-ownership.yaml +54 -0
- package/spur-cli/config/rules/boundary/planning-folder-hardcode.yaml +46 -0
- package/spur-cli/config/rules/recommended-pre-check.yaml +3 -2
- package/spur-cli/config/rules/structure/test-location.yaml +9 -0
- package/spur-cli/config/rules/typescript/happy-dom-teardown.yaml +43 -0
- package/spur-cli/config/rules/typescript/no-leaky-module-mocks.yaml +71 -0
- package/spur-cli/config/rules/ui/ui-import-boundary.yaml +51 -0
- package/spur-cli/config/tasks/section-matrix.yaml +188 -0
- package/spur-cli/config/tasks/templates/brainstorm.md +29 -0
- package/spur-cli/config/tasks/templates/feature-impl.md +50 -0
- package/spur-cli/config/tasks/templates/issue.md +33 -0
- package/spur-cli/config/tasks/templates/meta.md +31 -0
- package/spur-cli/config/tasks/templates/review.md +48 -0
- package/spur-cli/config/tasks/templates/standard.md +48 -0
- package/spur-cli/config/templates/bdd/checklist.md +1 -0
- package/spur-cli/config/templates/bdd/gherkin.md +8 -0
- package/spur-cli/config/templates/docs/00_ADR.md +27 -0
- package/spur-cli/config/templates/docs/01_PRD.md +37 -0
- package/spur-cli/config/templates/docs/02_ROADMAP.md +22 -0
- package/spur-cli/config/templates/docs/03_ARCHITECTURE.md +27 -0
- package/spur-cli/config/templates/docs/04_DESIGN.md +30 -0
- package/spur-cli/config/templates/docs/05_FEATURES.md +23 -0
- package/spur-cli/config/templates/docs/99_PROJECT_CONSTITUTION.md +65 -0
- package/spur-cli/config/templates/feature/default.md +41 -0
- package/spur-cli/config/templates/task/brainstorm.md +29 -0
- package/spur-cli/config/templates/task/feature-impl.md +50 -0
- package/spur-cli/config/templates/task/issue.md +33 -0
- package/spur-cli/config/templates/task/meta.md +31 -0
- package/spur-cli/config/templates/task/review.md +52 -0
- package/spur-cli/config/templates/task/standard.md +48 -0
- package/spur-cli/config/workflows/feature-dev.yaml +88 -80
- package/spur-cli/config/workflows/feature-lifecycle.yaml +100 -0
- package/spur-cli/config/workflows/planning-pipeline.yaml +166 -0
- package/spur-cli/config/workflows/task-lifecycle.yaml +128 -0
- package/spur-cli/config/workflows/task-pipeline.yaml +238 -0
- package/spur.js +69295 -52352
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
$schema: "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
2
|
+
# Planning pipeline — front-half of the development workflow (task 0088, design §6).
|
|
3
|
+
#
|
|
4
|
+
# Orchestration is configuration (ADR-022 / §3.2): this is YAML over the existing
|
|
5
|
+
# dual-workflow engine — zero new engine code. The pipeline automates steps 3–6 of the
|
|
6
|
+
# ideation→implementation chain: phasing → feature-ID derivation → design-doc generation →
|
|
7
|
+
# design-doc approval. It terminates at a drafted feature list and hands off to sp:spur-dev
|
|
8
|
+
# (steps 7–12), which is already shipped and unchanged.
|
|
9
|
+
#
|
|
10
|
+
# Every authoritative-doc touch invokes sp:doc-evolve to honor the constitution §5 sync triggers.
|
|
11
|
+
# Derived docs (docs/design/*, docs/features/*, 05_FEATURES index) are auto-written; high-risk
|
|
12
|
+
# authoritative edits (02_ROADMAP phasing, 04_DESIGN index) are STAGED to docs/plans/ for human
|
|
13
|
+
# commit (Q3 — hybrid by risk).
|
|
14
|
+
#
|
|
15
|
+
# Shape: phasing(HITL) → feature-id → design-gen → design-approval(HITL) → handoff
|
|
16
|
+
# (phasing/design-approval skipped when profile=auto).
|
|
17
|
+
#
|
|
18
|
+
# Vars (passed as a JSON object via `--vars`):
|
|
19
|
+
# slug — the design-doc slug, e.g. `--vars '{"slug":"auth-migration"}'`
|
|
20
|
+
# profile — set `--vars '{"profile":"auto"}'` to skip the HITL gates (Q4)
|
|
21
|
+
# feature — optional parent feature ID for derivation context
|
|
22
|
+
#
|
|
23
|
+
# Seeded by `spur init`; adapt the agent.run inputs to your project's command set.
|
|
24
|
+
|
|
25
|
+
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
26
|
+
kind: state-machine
|
|
27
|
+
name: planning-pipeline
|
|
28
|
+
description: "Front-half planning pipeline (phasing → feature-ID → design-gen → design-approval → handoff)"
|
|
29
|
+
iterationBound: 20
|
|
30
|
+
initialState: start
|
|
31
|
+
terminalStates:
|
|
32
|
+
- handoff
|
|
33
|
+
- cancelled
|
|
34
|
+
vars:
|
|
35
|
+
slug: "draft"
|
|
36
|
+
profile: "interactive"
|
|
37
|
+
feature: ""
|
|
38
|
+
|
|
39
|
+
states:
|
|
40
|
+
- id: start
|
|
41
|
+
description: >
|
|
42
|
+
Entry pass-through. Routes to the phasing HITL gate (interactive) or straight to
|
|
43
|
+
feature-id (profile=auto), so the phasing onEnter hitl.confirm only fires when a
|
|
44
|
+
human is in the loop. No actions — a pure routing node.
|
|
45
|
+
|
|
46
|
+
- id: phasing
|
|
47
|
+
description: >
|
|
48
|
+
Decide whether this requirement needs a new phase in 02_ROADMAP. The HITL gate asks
|
|
49
|
+
the operator; if a new phase is needed, the edit is STAGED to docs/plans/ (never
|
|
50
|
+
auto-written — Q3). Under profile=auto this state is never entered (start routes
|
|
51
|
+
around it straight to feature-id).
|
|
52
|
+
onEnter:
|
|
53
|
+
- kind: hitl.confirm
|
|
54
|
+
options:
|
|
55
|
+
prompt: "Does this requirement need a new phase in 02_ROADMAP? (Stage the edit if yes.)"
|
|
56
|
+
|
|
57
|
+
- id: feature-id
|
|
58
|
+
description: >
|
|
59
|
+
Derive the feature ID by scanning docs/features + 05_FEATURES for the parent and
|
|
60
|
+
allocating the child id (same rule as `spur feature create`). Result is recorded for
|
|
61
|
+
the design-doc generation step. Invokes sp:doc-evolve if 05_FEATURES index needs a
|
|
62
|
+
placeholder row.
|
|
63
|
+
onEnter:
|
|
64
|
+
- kind: agent.run
|
|
65
|
+
options:
|
|
66
|
+
input: "Derive the feature ID for ${vars.slug} (scan docs/features + docs/05_FEATURES.md; allocate child id under parent ${vars.feature}). Record the derived id."
|
|
67
|
+
|
|
68
|
+
- id: design-gen
|
|
69
|
+
description: >
|
|
70
|
+
Author the design doc at docs/design/${vars.slug}.md from the brainstormed feature
|
|
71
|
+
draft (docs/plans/${vars.slug}-drafted.md). ADVISORY: use the highest-quality coding
|
|
72
|
+
agent available (Q8 — tier rule is guidance, not enforced). Invokes sp:doc-evolve to
|
|
73
|
+
stage the 04_DESIGN index edit (never auto-write the authoritative index — Q3).
|
|
74
|
+
onEnter:
|
|
75
|
+
- kind: agent.run
|
|
76
|
+
options:
|
|
77
|
+
input: "Author the design doc at docs/design/${vars.slug}.md from docs/plans/${vars.slug}-drafted.md. Invoke sp:doc-evolve to stage the 04_DESIGN index edit."
|
|
78
|
+
|
|
79
|
+
- id: design-approval
|
|
80
|
+
description: >
|
|
81
|
+
HITL gate — the highest-leverage gate in the pipeline. Loop until the operator
|
|
82
|
+
approves the design doc. Under profile=auto this state is never entered (design-gen
|
|
83
|
+
routes around it straight to handoff).
|
|
84
|
+
onEnter:
|
|
85
|
+
- kind: hitl.confirm
|
|
86
|
+
options:
|
|
87
|
+
prompt: "Approve the design doc at docs/design/${vars.slug}.md? (Loop back to design-gen for revisions.)"
|
|
88
|
+
|
|
89
|
+
- id: handoff
|
|
90
|
+
description: >
|
|
91
|
+
Terminal — design doc approved; the drafted feature list is ready. Hand off to
|
|
92
|
+
sp:spur-dev (steps 7–12), which starts at `spur feature create`. The seam is the
|
|
93
|
+
drafted-feature-list file at docs/plans/${vars.slug}-drafted.md.
|
|
94
|
+
|
|
95
|
+
- id: cancelled
|
|
96
|
+
description: Terminal — planning abandoned by the operator.
|
|
97
|
+
|
|
98
|
+
transitions:
|
|
99
|
+
# ── start: route AROUND the phasing HITL gate when profile=auto ──
|
|
100
|
+
# Auto-skip guard tried FIRST: under profile=auto the run goes straight to
|
|
101
|
+
# feature-id and never ENTERS phasing (whose onEnter hitl.confirm would block).
|
|
102
|
+
# Otherwise fall through to phasing for the human gate.
|
|
103
|
+
- from: start
|
|
104
|
+
to: feature-id
|
|
105
|
+
description: profile=auto — skip the phasing HITL gate.
|
|
106
|
+
guard:
|
|
107
|
+
kind: shell
|
|
108
|
+
options:
|
|
109
|
+
command: 'test "${vars.profile}" = auto'
|
|
110
|
+
- from: start
|
|
111
|
+
to: phasing
|
|
112
|
+
description: Interactive — enter the phasing HITL gate.
|
|
113
|
+
guard:
|
|
114
|
+
kind: always
|
|
115
|
+
|
|
116
|
+
# ── phasing: HITL gate (reached only when profile != auto) ──
|
|
117
|
+
- from: phasing
|
|
118
|
+
to: feature-id
|
|
119
|
+
description: Phasing decided — proceed to feature-ID derivation.
|
|
120
|
+
guard:
|
|
121
|
+
kind: always
|
|
122
|
+
- from: phasing
|
|
123
|
+
to: cancelled
|
|
124
|
+
description: Operator cancelled planning.
|
|
125
|
+
guard:
|
|
126
|
+
kind: always
|
|
127
|
+
|
|
128
|
+
# ── linear body ──
|
|
129
|
+
- from: feature-id
|
|
130
|
+
to: design-gen
|
|
131
|
+
description: Feature ID derived — generate the design doc.
|
|
132
|
+
guard:
|
|
133
|
+
kind: always
|
|
134
|
+
|
|
135
|
+
# ── design-gen → design-approval, OR skip the HITL gate when profile=auto ──
|
|
136
|
+
# Auto-skip guard tried FIRST: under profile=auto, route straight to handoff
|
|
137
|
+
# without entering design-approval (whose onEnter hitl.confirm would block).
|
|
138
|
+
- from: design-gen
|
|
139
|
+
to: handoff
|
|
140
|
+
description: profile=auto — skip the design-approval HITL gate, hand off directly.
|
|
141
|
+
guard:
|
|
142
|
+
kind: shell
|
|
143
|
+
options:
|
|
144
|
+
command: 'test "${vars.profile}" = auto'
|
|
145
|
+
- from: design-gen
|
|
146
|
+
to: design-approval
|
|
147
|
+
description: Design doc drafted — gate on human approval.
|
|
148
|
+
guard:
|
|
149
|
+
kind: always
|
|
150
|
+
|
|
151
|
+
# ── design-approval: HITL gate (reached only when profile != auto) ──
|
|
152
|
+
- from: design-approval
|
|
153
|
+
to: handoff
|
|
154
|
+
description: Design approved — hand off to sp:spur-dev.
|
|
155
|
+
guard:
|
|
156
|
+
kind: always
|
|
157
|
+
- from: design-approval
|
|
158
|
+
to: design-gen
|
|
159
|
+
description: Rework — operator requested design revisions.
|
|
160
|
+
guard:
|
|
161
|
+
kind: always
|
|
162
|
+
- from: design-approval
|
|
163
|
+
to: cancelled
|
|
164
|
+
description: Operator cancelled planning.
|
|
165
|
+
guard:
|
|
166
|
+
kind: always
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
$schema: "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
2
|
+
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
3
|
+
kind: state-machine
|
|
4
|
+
name: task-lifecycle
|
|
5
|
+
description: >
|
|
6
|
+
Task lifecycle FSM (design §2.3, §5.1). States are the canonical TaskStatus
|
|
7
|
+
vocabulary (DD-01); transitions encode the §2.3 graph; guards invoke
|
|
8
|
+
`${vars.spurBin} task check` at the wip→testing and testing→done placements.
|
|
9
|
+
`done` is re-enterable (reopen with a warning); `cancelled` is terminal.
|
|
10
|
+
Guard commands reference the check verb (0051) — structural validation passes
|
|
11
|
+
today, behavioral wiring activates as the verb ships. Unconditional transitions
|
|
12
|
+
use the `always` guard (externally-driven via `requestTransition`, not
|
|
13
|
+
auto-advance).
|
|
14
|
+
initialState: backlog
|
|
15
|
+
terminalStates:
|
|
16
|
+
- cancelled
|
|
17
|
+
vars:
|
|
18
|
+
spurBin: 'spur'
|
|
19
|
+
wbs: '0000'
|
|
20
|
+
|
|
21
|
+
states:
|
|
22
|
+
- id: backlog
|
|
23
|
+
description: Initial state — task exists but has not been triaged.
|
|
24
|
+
- id: todo
|
|
25
|
+
description: Triaged and ready to start.
|
|
26
|
+
- id: wip
|
|
27
|
+
description: Actively being worked on.
|
|
28
|
+
- id: testing
|
|
29
|
+
description: Implementation complete; under test/verification.
|
|
30
|
+
- id: blocked
|
|
31
|
+
description: Impediment; work suspended.
|
|
32
|
+
- id: done
|
|
33
|
+
description: Completed. Re-enterable (reopen with warning + mandatory History entry).
|
|
34
|
+
- id: cancelled
|
|
35
|
+
description: Terminal — abandoned.
|
|
36
|
+
|
|
37
|
+
transitions:
|
|
38
|
+
# Forward path: backlog → todo → wip → testing → done
|
|
39
|
+
- from: backlog
|
|
40
|
+
to: todo
|
|
41
|
+
description: Triage — task is ready to start
|
|
42
|
+
guard:
|
|
43
|
+
kind: always
|
|
44
|
+
- from: todo
|
|
45
|
+
to: wip
|
|
46
|
+
description: Start work
|
|
47
|
+
guard:
|
|
48
|
+
kind: always
|
|
49
|
+
- from: wip
|
|
50
|
+
to: testing
|
|
51
|
+
description: Implementation complete; enter testing
|
|
52
|
+
guard:
|
|
53
|
+
kind: shell
|
|
54
|
+
options:
|
|
55
|
+
command: '${vars.spurBin} task check ${vars.wbs}'
|
|
56
|
+
- from: testing
|
|
57
|
+
to: done
|
|
58
|
+
description: Testing passed — task complete
|
|
59
|
+
guard:
|
|
60
|
+
kind: shell
|
|
61
|
+
options:
|
|
62
|
+
command: '${vars.spurBin} task check ${vars.wbs} --strict-core'
|
|
63
|
+
|
|
64
|
+
# Reopen: done → wip (warned, mandatory History entry)
|
|
65
|
+
- from: done
|
|
66
|
+
to: wip
|
|
67
|
+
description: Reopen — work resumed (warning + mandatory History entry)
|
|
68
|
+
guard:
|
|
69
|
+
kind: always
|
|
70
|
+
|
|
71
|
+
# Blocked ↔ (todo, wip, testing) — bidirectional from/to active states
|
|
72
|
+
- from: todo
|
|
73
|
+
to: blocked
|
|
74
|
+
description: Blocked by an impediment
|
|
75
|
+
guard:
|
|
76
|
+
kind: always
|
|
77
|
+
- from: blocked
|
|
78
|
+
to: todo
|
|
79
|
+
description: Impediment resolved — back to todo
|
|
80
|
+
guard:
|
|
81
|
+
kind: always
|
|
82
|
+
- from: wip
|
|
83
|
+
to: blocked
|
|
84
|
+
description: Blocked by an impediment
|
|
85
|
+
guard:
|
|
86
|
+
kind: always
|
|
87
|
+
- from: blocked
|
|
88
|
+
to: wip
|
|
89
|
+
description: Impediment resolved — back to wip
|
|
90
|
+
guard:
|
|
91
|
+
kind: always
|
|
92
|
+
- from: testing
|
|
93
|
+
to: blocked
|
|
94
|
+
description: Blocked by an impediment
|
|
95
|
+
guard:
|
|
96
|
+
kind: always
|
|
97
|
+
- from: blocked
|
|
98
|
+
to: testing
|
|
99
|
+
description: Impediment resolved — back to testing
|
|
100
|
+
guard:
|
|
101
|
+
kind: always
|
|
102
|
+
|
|
103
|
+
# Cancel: any non-terminal → cancelled (terminal)
|
|
104
|
+
- from: backlog
|
|
105
|
+
to: cancelled
|
|
106
|
+
description: Cancel task
|
|
107
|
+
guard:
|
|
108
|
+
kind: always
|
|
109
|
+
- from: todo
|
|
110
|
+
to: cancelled
|
|
111
|
+
description: Cancel task
|
|
112
|
+
guard:
|
|
113
|
+
kind: always
|
|
114
|
+
- from: wip
|
|
115
|
+
to: cancelled
|
|
116
|
+
description: Cancel task
|
|
117
|
+
guard:
|
|
118
|
+
kind: always
|
|
119
|
+
- from: testing
|
|
120
|
+
to: cancelled
|
|
121
|
+
description: Cancel task
|
|
122
|
+
guard:
|
|
123
|
+
kind: always
|
|
124
|
+
- from: blocked
|
|
125
|
+
to: cancelled
|
|
126
|
+
description: Cancel task
|
|
127
|
+
guard:
|
|
128
|
+
kind: always
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
$schema: "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
2
|
+
# Task execution pipeline — one run per execution attempt of a task (design §6).
|
|
3
|
+
#
|
|
4
|
+
# Orchestration is configuration (ADR-022 / §3.2): this is YAML over the existing
|
|
5
|
+
# dual-workflow engine — zero new engine code. The pipeline NEVER touches files
|
|
6
|
+
# directly — `record` writes via `spur task update --section`, and status moves go
|
|
7
|
+
# through the normal `spur task update <wbs> <status>` verb so the lifecycle guards
|
|
8
|
+
# (0055) apply identically. Run linkage is written to `task_run_links` (kind=pipeline).
|
|
9
|
+
#
|
|
10
|
+
# Shape: precheck → implement → test → review → approve(HITL) → verify → record → done
|
|
11
|
+
# (precheck failure short-circuits to `failed`).
|
|
12
|
+
#
|
|
13
|
+
# Vars (passed as a JSON object via `--vars`):
|
|
14
|
+
# wbs — the task WBS (required), e.g. `--vars '{"wbs":"0042"}'`
|
|
15
|
+
# profile — set `--vars '{"profile":"auto"}'` to skip the HITL approve gate (R4)
|
|
16
|
+
#
|
|
17
|
+
# Seeded by `spur init`; adapt the agent.run inputs to your project's command set.
|
|
18
|
+
|
|
19
|
+
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
20
|
+
kind: state-machine
|
|
21
|
+
name: task-pipeline
|
|
22
|
+
description: "Standard task execution pipeline (precheck → implement → test → review → approve → verify → record → done)"
|
|
23
|
+
iterationBound: 20
|
|
24
|
+
initialState: precheck
|
|
25
|
+
terminalStates:
|
|
26
|
+
- done
|
|
27
|
+
- failed
|
|
28
|
+
vars:
|
|
29
|
+
wbs: "0000"
|
|
30
|
+
profile: "standard"
|
|
31
|
+
# PATH-independent spur invocation for shell guards/actions. The CLI overrides this
|
|
32
|
+
# at run start (resolveSpurBin); the literal default is a safe fallback so direct/dry
|
|
33
|
+
# runs and `workflow validate` resolve the template. (See AGENTS.md / ADR-026.)
|
|
34
|
+
spurBin: "spur"
|
|
35
|
+
# Agent the pipeline's agent.run steps invoke. Override per run with
|
|
36
|
+
# `--vars '{"agent":"claude"}'`. Pinned (not left to the AiRunner's <default>
|
|
37
|
+
# selection) so a broken/misconfigured agent on the box can't silently capture the run.
|
|
38
|
+
agent: "omp"
|
|
39
|
+
# Step-level timeout for agent.run steps in milliseconds. A timed-out agent
|
|
40
|
+
# subprocess is killed (not abandoned) and the step fails → pipeline routes to
|
|
41
|
+
# `failed`. Override per run with `--vars '{"stepTimeoutMs":120000}'`.
|
|
42
|
+
stepTimeoutMs: "600000"
|
|
43
|
+
|
|
44
|
+
states:
|
|
45
|
+
- id: precheck
|
|
46
|
+
description: >
|
|
47
|
+
Pre-flight structural validation. The precheck→implement guard runs
|
|
48
|
+
`spur task check <wbs>`; a failure routes to `failed` (blocked report).
|
|
49
|
+
onEnter:
|
|
50
|
+
- kind: note
|
|
51
|
+
options:
|
|
52
|
+
# R4 task_run_links linkage (kind=pipeline) is written by a WorkflowService
|
|
53
|
+
# hook at run start — see the 0062 follow-up note; there is no link-writing
|
|
54
|
+
# CLI verb to call from a shell step, so it cannot live in pure YAML.
|
|
55
|
+
message: "Pipeline start for task ${vars.wbs}."
|
|
56
|
+
|
|
57
|
+
- id: implement
|
|
58
|
+
description: >
|
|
59
|
+
Move the task to `wip` (via the normal verb — lifecycle guards apply, R3),
|
|
60
|
+
then run agent-driven implementation via /sp:dev-run --mode implement.
|
|
61
|
+
NOTE: this step must NOT call /sp:dev-run in full mode (the default) —
|
|
62
|
+
that command DRIVES this pipeline, so calling it here recurses.
|
|
63
|
+
--mode implement is the single-step implement entry.
|
|
64
|
+
onEnter:
|
|
65
|
+
- kind: shell
|
|
66
|
+
options:
|
|
67
|
+
command: "${vars.spurBin} task update ${vars.wbs} wip --no-lifecycle"
|
|
68
|
+
- kind: agent.run
|
|
69
|
+
options:
|
|
70
|
+
agent: ${vars.agent}
|
|
71
|
+
input: /sp:dev-run --mode implement ${vars.wbs} --auto
|
|
72
|
+
timeoutMs: ${vars.stepTimeoutMs}
|
|
73
|
+
|
|
74
|
+
- id: test
|
|
75
|
+
description: Test execution + coverage via /sp:dev-unit.
|
|
76
|
+
onEnter:
|
|
77
|
+
- kind: agent.run
|
|
78
|
+
options:
|
|
79
|
+
agent: ${vars.agent}
|
|
80
|
+
input: /sp:dev-unit ${vars.wbs} --auto
|
|
81
|
+
timeoutMs: ${vars.stepTimeoutMs}
|
|
82
|
+
|
|
83
|
+
- id: review
|
|
84
|
+
description: SECU code review via /sp:dev-review.
|
|
85
|
+
onEnter:
|
|
86
|
+
- kind: agent.run
|
|
87
|
+
options:
|
|
88
|
+
agent: ${vars.agent}
|
|
89
|
+
input: /sp:dev-review ${vars.wbs} --auto
|
|
90
|
+
timeoutMs: ${vars.stepTimeoutMs}
|
|
91
|
+
|
|
92
|
+
- id: approve
|
|
93
|
+
description: >
|
|
94
|
+
Human-in-the-loop approval gate. Under `profile=auto` this state is never entered
|
|
95
|
+
(review routes around it straight to verify). NOTE: to make this state PAUSE for `spur workflow continue`
|
|
96
|
+
(E3), add `pause: true` here — deferred until the globally-installed `@gobing-ai/spur`
|
|
97
|
+
schema (currently a stale 0.2.5 that lacks the `pause` field) is refreshed, so
|
|
98
|
+
full `spur workflow validate` stays green. The workspace schema already supports it.
|
|
99
|
+
onEnter:
|
|
100
|
+
- kind: hitl.confirm
|
|
101
|
+
options:
|
|
102
|
+
prompt: "Approve task ${vars.wbs} to proceed to verification?"
|
|
103
|
+
|
|
104
|
+
- id: verify
|
|
105
|
+
description: >
|
|
106
|
+
Functional verification (BDD + traceability) via /sp:dev-verify. The agent's
|
|
107
|
+
captured answer is written to `.spur/run/<wbs>-verify-answer.txt`; a deterministic
|
|
108
|
+
shell step then derives the verdict and writes the gate artifact
|
|
109
|
+
`.spur/run/<wbs>-verdict.json` (so the completion gate reads a real verdict, not
|
|
110
|
+
agent discretion — R9). The verdict is PASS only if the agent both reported PASS
|
|
111
|
+
AND `spur task check` passes; otherwise FAIL.
|
|
112
|
+
onEnter:
|
|
113
|
+
- kind: agent.run
|
|
114
|
+
options:
|
|
115
|
+
agent: ${vars.agent}
|
|
116
|
+
input: /sp:dev-verify ${vars.wbs} --auto --fix all
|
|
117
|
+
timeoutMs: ${vars.stepTimeoutMs}
|
|
118
|
+
answerFile: .spur/run/${vars.wbs}-verify-answer.txt
|
|
119
|
+
- kind: shell
|
|
120
|
+
options:
|
|
121
|
+
command: "${vars.spurBin} task verdict ${vars.wbs} --from-answer .spur/run/${vars.wbs}-verify-answer.txt"
|
|
122
|
+
|
|
123
|
+
- id: record
|
|
124
|
+
description: >
|
|
125
|
+
Record pipeline results into the task file via `spur task record` —
|
|
126
|
+
Testing/Review from the verdict, Solution backfilled from git diff as a
|
|
127
|
+
safety net, optional transition to testing. A single verb replaces the
|
|
128
|
+
previous ~50 lines of embedded shell (0108; ADR-022).
|
|
129
|
+
onEnter:
|
|
130
|
+
- kind: shell
|
|
131
|
+
options:
|
|
132
|
+
command: "${vars.spurBin} task record ${vars.wbs} --solution-from-diff --transition testing"
|
|
133
|
+
|
|
134
|
+
- id: done
|
|
135
|
+
description: >
|
|
136
|
+
Terminal — task executed, verified, and recorded. The `record → done`
|
|
137
|
+
guard runs `spur task check` before certifying; a genuinely non-compliant
|
|
138
|
+
task routes to `failed` instead of a silent bad `done`.
|
|
139
|
+
onEnter:
|
|
140
|
+
- kind: shell
|
|
141
|
+
options:
|
|
142
|
+
command: "${vars.spurBin} task update ${vars.wbs} done --no-lifecycle"
|
|
143
|
+
- kind: note
|
|
144
|
+
options:
|
|
145
|
+
message: "Pipeline complete for task ${vars.wbs} (done gate cleared)."
|
|
146
|
+
|
|
147
|
+
- id: failed
|
|
148
|
+
description: Terminal — precheck (or a gated step) failed; reported, not advanced.
|
|
149
|
+
|
|
150
|
+
transitions:
|
|
151
|
+
# ── precheck: shell guard runs `spur task check`; pass → implement, fail → failed ──
|
|
152
|
+
- from: precheck
|
|
153
|
+
to: implement
|
|
154
|
+
description: Precheck passed — begin implementation.
|
|
155
|
+
guard:
|
|
156
|
+
kind: shell
|
|
157
|
+
options:
|
|
158
|
+
command: "${vars.spurBin} task check ${vars.wbs}"
|
|
159
|
+
- from: precheck
|
|
160
|
+
to: failed
|
|
161
|
+
description: Precheck failed — report and stop (no fall-through to implement).
|
|
162
|
+
guard:
|
|
163
|
+
kind: shell
|
|
164
|
+
options:
|
|
165
|
+
command: '! ${vars.spurBin} task check ${vars.wbs}'
|
|
166
|
+
|
|
167
|
+
# ── linear body ──
|
|
168
|
+
- from: implement
|
|
169
|
+
to: test
|
|
170
|
+
description: Implementation done — run tests.
|
|
171
|
+
guard:
|
|
172
|
+
kind: always
|
|
173
|
+
- from: test
|
|
174
|
+
to: review
|
|
175
|
+
description: Tests done — run review.
|
|
176
|
+
guard:
|
|
177
|
+
kind: always
|
|
178
|
+
# ── review → approve, OR skip the HITL gate entirely when profile=auto (R4) ──
|
|
179
|
+
# Declaration order matters: the auto-skip guard is tried FIRST, so under
|
|
180
|
+
# profile=auto the run routes straight to verify and never ENTERS approve
|
|
181
|
+
# (whose onEnter hitl.confirm would otherwise block). Otherwise fall through
|
|
182
|
+
# to approve for the human gate.
|
|
183
|
+
- from: review
|
|
184
|
+
to: verify
|
|
185
|
+
description: profile=auto — skip the human approval gate, go straight to verify.
|
|
186
|
+
guard:
|
|
187
|
+
kind: shell
|
|
188
|
+
options:
|
|
189
|
+
command: 'test "${vars.profile}" = auto'
|
|
190
|
+
- from: review
|
|
191
|
+
to: approve
|
|
192
|
+
description: Review done — gate on human approval.
|
|
193
|
+
guard:
|
|
194
|
+
kind: always
|
|
195
|
+
|
|
196
|
+
# ── approve: HITL gate (reached only when profile != auto) ──
|
|
197
|
+
- from: approve
|
|
198
|
+
to: verify
|
|
199
|
+
description: Approved — proceed to verification.
|
|
200
|
+
guard:
|
|
201
|
+
kind: always
|
|
202
|
+
# ── completion gate (the YAML-native replacement for rd3's default-on --postflight-verify) ──
|
|
203
|
+
# The verify step emits .spur/run/<wbs>-verdict.json. Only `verdict: PASS` clears
|
|
204
|
+
# the gate to `record`; any non-PASS (PARTIAL/FAIL), a missing file, or malformed
|
|
205
|
+
# JSON routes to `failed`. Declaration order: PASS guard tried FIRST.
|
|
206
|
+
- from: verify
|
|
207
|
+
to: record
|
|
208
|
+
description: Verification verdict is PASS — record results and proceed to done.
|
|
209
|
+
guard:
|
|
210
|
+
kind: shell
|
|
211
|
+
options:
|
|
212
|
+
command: 'test "$(jq -r .verdict .spur/run/${vars.wbs}-verdict.json 2>/dev/null)" = PASS'
|
|
213
|
+
- from: verify
|
|
214
|
+
to: failed
|
|
215
|
+
description: Verification verdict is not PASS (PARTIAL/FAIL/missing) — block before done.
|
|
216
|
+
guard:
|
|
217
|
+
kind: shell
|
|
218
|
+
options:
|
|
219
|
+
command: 'test "$(jq -r .verdict .spur/run/${vars.wbs}-verdict.json 2>/dev/null)" != PASS'
|
|
220
|
+
# ── done gate: record → done/failed gated on `spur task check` (ADR-026 amendment 2026-06-23) ──
|
|
221
|
+
# The record step guarantees every done-required section ([Solution, Testing, Review])
|
|
222
|
+
# has real content (each owned by its pipeline step). This guard is defense-in-depth —
|
|
223
|
+
# it certifies the matrix before done; a genuinely non-compliant task routes to failed.
|
|
224
|
+
# Declaration order: pass guard first.
|
|
225
|
+
- from: record
|
|
226
|
+
to: done
|
|
227
|
+
description: Task check passed — certify done.
|
|
228
|
+
guard:
|
|
229
|
+
kind: shell
|
|
230
|
+
options:
|
|
231
|
+
command: "${vars.spurBin} task check ${vars.wbs}"
|
|
232
|
+
- from: record
|
|
233
|
+
to: failed
|
|
234
|
+
description: Task check failed — block before done; investigate missing sections.
|
|
235
|
+
guard:
|
|
236
|
+
kind: shell
|
|
237
|
+
options:
|
|
238
|
+
command: '! ${vars.spurBin} task check ${vars.wbs}'
|