@gobing-ai/spur 0.3.43 → 0.3.45
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/rules/README.md +12 -4
- package/config/rules/recommended-pre-check.yaml +4 -4
- package/config/rules/strict-check.yaml +7 -4
- package/config/templates/AGENTS.md +5 -0
- package/config/workflows/idea-pipeline.yaml +205 -33
- package/config/workflows/task-pipeline.yaml +1 -1
- package/package.json +9 -9
- package/plugins/sp/commands/dev-find-next.md +20 -12
- package/plugins/sp/commands/dev-runall.md +3 -1
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/skills/next-feature/SKILL.md +17 -13
- package/plugins/sp/skills/next-feature/references/handoff-routing.md +12 -7
- package/plugins/sp/skills/next-feature/references/signal-derivation.md +39 -2
- package/plugins/sp/skills/spur-cli/SKILL.md +3 -0
- package/plugins/sp/skills/spur-cli/references/features/hierarchy-mece.md +8 -3
- package/plugins/sp/skills/spur-cli/references/tasks.md +22 -0
- package/plugins/sp/skills/spur-dev/SKILL.md +12 -4
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +1 -1
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +12 -3
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +6 -5
- package/plugins/sp/skills/spur-dev/references/planning-workflow.md +53 -0
- package/spur.js +1866 -544
- package/web/_astro/BoardApp.Ce6zJYAH.js +1 -0
- package/web/_astro/BoardApp.DKyrGxdo.js +179 -0
- package/web/_astro/{TaskDetail.DOKJP-Kp.js → TaskDetail.6-27_LMa.js} +1 -1
- package/web/_astro/{arc.CTqmqspU.js → arc.Df-9AQvS.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.B0iBdehs.js → architectureDiagram-3BPJPVTR.VAI_-paS.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.BHE5UFj5.js → blockDiagram-GPEHLZMM.DFpUY1ue.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.DQApkgGM.js → c4Diagram-AAUBKEIU.CF8doOpg.js} +1 -1
- package/web/_astro/channel.Uhm9O3UV.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.fPrKLM8v.js → chunk-2J33WTMH.BnjK3fjt.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.DExvwBhn.js → chunk-4BX2VUAB.x6ZDnJKq.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.EjNkjuD6.js → chunk-55IACEB6.zY-0uu7w.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.Dl8xy5rT.js → chunk-727SXJPM.BZxKg_Vi.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.BSzMBgBs.js → chunk-AQP2D5EJ.Cpi9G9Td.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.CAeOHpbb.js → chunk-FMBD7UC4.DWTB-Pif.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.Bbeu_ZOH.js → chunk-ND2GUHAM.BPDQbiOG.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.Da_GaIAZ.js → chunk-QZHKN3VN.BRWIcuoM.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.sMfFYSQp.js → classDiagram-4FO5ZUOK.mGTCZsDO.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.sMfFYSQp.js → classDiagram-v2-Q7XG4LA2.mGTCZsDO.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.PS3IwOH4.js → cose-bilkent-S5V4N54A.D1GEut-z.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.EQCx8a6D.js → dagre-BM42HDAG.BV0XG9Do.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.aEFQl8v8.js → diagram-2AECGRRQ.DzpYxsjo.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.jyN4tqFX.js → diagram-5GNKFQAL.Cm9YzJh4.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.DaTVHnme.js → diagram-KO2AKTUF.BjhottUj.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.DWsGRLAN.js → diagram-LMA3HP47.BFsQW5kb.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.DYxmOViu.js → diagram-OG6HWLK6.8pdpzSWO.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.BKM75coa.js → erDiagram-TEJ5UH35.Bd7KUJmJ.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.B7qpqDW5.js → flowDiagram-I6XJVG4X.7LWffkaE.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.CYBL-hwy.js → ganttDiagram-6RSMTGT7.BeDcO5tI.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.Cz4ly3Kq.js → gitGraphDiagram-PVQCEYII.Ca4n730A.js} +1 -1
- package/web/_astro/{index.yAse9IaO.css → index.Dbvuw6d4.css} +1 -1
- package/web/_astro/{infoDiagram-5YYISTIA.CIS7cXrJ.js → infoDiagram-5YYISTIA.B0OakQYb.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.DQ-pmaOG.js → ishikawaDiagram-YF4QCWOH.DSmNQe-1.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.DVS6F7UM.js → journeyDiagram-JHISSGLW.Cy5ruEUu.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.D8t-G-U6.js → kanban-definition-UN3LZRKU.CUJXub0p.js} +1 -1
- package/web/_astro/{linear.IO8rTd_n.js → linear.DC1jCCXn.js} +1 -1
- package/web/_astro/{mermaid.core.CjVnvFNf.js → mermaid.core.DxVP99Ab.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.DZoEMTnU.js → mindmap-definition-RKZ34NQL.D0MaV6sJ.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.86r1QWnX.js → pieDiagram-4H26LBE5.DCC6_q32.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.DzFA9uPk.js → quadrantDiagram-W4KKPZXB.BeUOAM7C.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.dQvdvfMr.js → requirementDiagram-4Y6WPE33.Dbl4MASO.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.BvC-VNI8.js → sankeyDiagram-5OEKKPKP.HsLg0VS4.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.DDQNwHs8.js → sequenceDiagram-3UESZ5HK.DT7DJTnZ.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.BKwYy-tQ.js → stateDiagram-AJRCARHV.d_ju1Vr1.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.KxCiRYYf.js → stateDiagram-v2-BHNVJYJU.DMCAjMJ4.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.Bh9NDjOx.js → timeline-definition-PNZ67QCA.DNOHr62_.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.DIM-n6us.js → vennDiagram-CIIHVFJN.B7dUy-1W.js} +1 -1
- package/web/_astro/{wardley-L42UT6IY.DW15qOFQ.js → wardley-L42UT6IY.DEqOXvBh.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.UTIvjsG9.js → wardleyDiagram-YWT4CUSO.BCRb2p6x.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.CQYhkVDS.js → xychartDiagram-2RQKCTM6.NxVQLdBh.js} +1 -1
- package/web/index.html +2 -2
- package/web/_astro/BoardApp.8sc6Rb1t.js +0 -179
- package/web/_astro/BoardApp.ChKnY1JD.js +0 -1
- package/web/_astro/channel.B0q9WGrH.js +0 -1
package/config/rules/README.md
CHANGED
|
@@ -15,14 +15,17 @@ install or to ts-libs.
|
|
|
15
15
|
| `structure` | `structure/` | File layout, protected files, no focused/skipped tests |
|
|
16
16
|
| `quality` | `quality/` | Post-test gates (coverage) |
|
|
17
17
|
| `surface` | `surface/` | CLI surface consistency (registerXxxCommand wiring, --json serialization) |
|
|
18
|
+
| `migration` | `migration/` | Transitional helpers for the regex → `rg` evaluator move (`rg-dialect`) |
|
|
19
|
+
| `ui` | `ui/` | Web UI seam boundaries (import seam, daisyUI class leak) |
|
|
18
20
|
|
|
19
21
|
## Presets
|
|
20
22
|
|
|
21
23
|
| Preset | When | Extends |
|
|
22
24
|
|---|---|---|
|
|
23
|
-
| `recommended-pre-check` | Before tests | `typescript`, `structure`, `boundary`, `surface` |
|
|
25
|
+
| `recommended-pre-check` | Before tests | `typescript`, `structure`, `boundary`, `surface`, `ui`, `strict` |
|
|
24
26
|
| `recommended-post-check` | After tests | `quality` |
|
|
25
|
-
| `strict-check` | Opt-in | `strict` |
|
|
27
|
+
| `strict-check` | Opt-in single-category cherry-pick (strict only) | `strict` |
|
|
28
|
+
| `rg-migration` | On-demand during the regex → `rg` evaluator migration | `migration` |
|
|
26
29
|
|
|
27
30
|
## Relationship to ts-libs
|
|
28
31
|
|
|
@@ -32,9 +35,14 @@ ts-libs/.spur/rules/...` header documenting what was re-scoped, omitted, or
|
|
|
32
35
|
tuned for Spur's app-repo layout. After absorption, ts-libs and spur-new
|
|
33
36
|
maintain their rulesets independently.
|
|
34
37
|
|
|
38
|
+
## Transitional helpers
|
|
39
|
+
|
|
40
|
+
- `migration/rg-dialect` (category `migration/`) and the `rg-migration` preset
|
|
41
|
+
are **shipped and live**: run `spur rule run --preset rg-migration` on demand
|
|
42
|
+
during the regex → `rg` evaluator migration. They are intentionally excluded
|
|
43
|
+
from the standing pre/post-check gates (transitional, not a permanent gate).
|
|
44
|
+
|
|
35
45
|
## Not absorbed (Spur-irrelevant)
|
|
36
46
|
|
|
37
47
|
- `typescript/esm-build-conventions` — governs ts-libs' library publish/dist
|
|
38
48
|
flow. Spur apps don't publish libraries this way.
|
|
39
|
-
- `migration/rg-dialect` — one-time grep→rg migration helper.
|
|
40
|
-
- `migration/rg-migration` — one-time grep→rg migration helper.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
$schema: "@gobing-ai/spur/schemas/preset.schema.json"
|
|
2
|
-
# Recommended pre-check preset — portable rule categories
|
|
2
|
+
# Recommended pre-check preset — portable rule categories bundled with the catalog.
|
|
3
3
|
# Use with: spur rule run --preset recommended-pre-check
|
|
4
4
|
#
|
|
5
|
-
# Runs BEFORE tests (test-pre-check).
|
|
6
|
-
# boundary,
|
|
7
|
-
# run in post-check alongside coverage-gate.
|
|
5
|
+
# Runs BEFORE tests (test-pre-check). Extends six categories: typescript,
|
|
6
|
+
# structure, boundary, surface, ui, and strict (ui since bc267cc8, strict since
|
|
7
|
+
# 79186391). TSDoc exports run in post-check alongside coverage-gate.
|
|
8
8
|
name: recommended-pre-check
|
|
9
9
|
extends:
|
|
10
10
|
- typescript
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
$schema: "@gobing-ai/spur/schemas/preset.schema.json"
|
|
2
|
-
# Strict-check preset —
|
|
3
|
-
#
|
|
4
|
-
#
|
|
2
|
+
# Strict-check preset — explicit single-category cherry-pick of the `strict/`
|
|
3
|
+
# category only. The same category is already included in recommended-pre-check
|
|
4
|
+
# (extends: typescript, structure, boundary, surface, ui, strict) since 79186391;
|
|
5
|
+
# use this preset when you want ONLY strict rules, not the full pre-check set.
|
|
6
|
+
# Run explicitly:
|
|
5
7
|
# spur rule run --preset strict-check
|
|
6
8
|
# or cherry-pick a single rule:
|
|
7
9
|
# spur rule run --rule no-third-party-http-clients
|
|
@@ -11,6 +13,7 @@ $schema: "@gobing-ai/spur/schemas/preset.schema.json"
|
|
|
11
13
|
name: strict-check
|
|
12
14
|
description: >
|
|
13
15
|
Opt-in boundary hygiene: HTTP-client centralization, runtime fs/spawn seams,
|
|
14
|
-
and local rule-file structural validation.
|
|
16
|
+
and local rule-file structural validation. Included in recommended-pre-check;
|
|
17
|
+
this preset selects the strict/ category alone.
|
|
15
18
|
extends:
|
|
16
19
|
- strict
|
|
@@ -69,6 +69,11 @@ All product development work goes through the harness by default.
|
|
|
69
69
|
auto`, a name, parallel/headless execution, direct `spur agent run`, and engine-driven workflow
|
|
70
70
|
`agent.run` remain subprocess surfaces.
|
|
71
71
|
|
|
72
|
+
**Task lookup fast path:** Given a WBS, do not search `docs/tasks*` or guess `--folder`. Use
|
|
73
|
+
`spur task show <wbs> --json` for task metadata and content; its response also includes `filePath`.
|
|
74
|
+
Use `spur task path <wbs> --json` only when a filesystem consumer needs the absolute path. Both
|
|
75
|
+
commands resolve across configured task folders. Reuse the first `show` response within the run.
|
|
76
|
+
|
|
72
77
|
**Platform fallback:** Platforms without slash commands and/or subagents still use the harness.
|
|
73
78
|
Install the plugin through Superskill for the target platform, then use skills `sp:spur-dev`,
|
|
74
79
|
`sp:spur-cli`, `sp:code-verification` (and related) plus the `spur` CLI. Do not invent a parallel
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
# Shape: start -> discovery -> idea-eval -> feature-create -> ac-generate -> feature-check
|
|
11
11
|
# -> system-design (conditional: needs_design signal)
|
|
12
12
|
# -> design-approval (taste HITL gate; not auto-clicked by --auto)
|
|
13
|
-
# -> decompose -> batch-create -> handoff
|
|
13
|
+
# -> decompose -> batch-create -> handoff-finalize -> handoff
|
|
14
14
|
# (idea-eval rejection -> cancelled)
|
|
15
15
|
# (feature-check failure routes back to ac-generate; batch-create failure to decompose).
|
|
16
16
|
#
|
|
@@ -128,20 +128,36 @@ states:
|
|
|
128
128
|
- id: feature-create
|
|
129
129
|
description: >
|
|
130
130
|
Create a feature via spur feature create, or select an existing feature id.
|
|
131
|
-
The agent writes the feature id to .spur/run/${vars.__runId}-idea-feature-id.txt
|
|
132
|
-
|
|
131
|
+
The agent writes the feature id to .spur/run/${vars.__runId}-idea-feature-id.txt and body-only
|
|
132
|
+
intent artifacts .spur/run/${vars.__runId}-idea-goal.md / -idea-scope.md. Goal carries
|
|
133
|
+
concise intent only; Scope carries explicit in/out boundaries — task breakdowns and
|
|
134
|
+
checklists never enter Goal. Shell actions then persist both sections through
|
|
135
|
+
`spur feature update --section Goal|Scope --from-file ...`; a missing/empty artifact
|
|
136
|
+
stops the state. Prefer the enhanced idea from .spur/run/${vars.__runId}-idea-eval-report.md as
|
|
133
137
|
context; do not overwrite vars.idea.
|
|
134
138
|
onEnter:
|
|
135
139
|
- kind: agent.run
|
|
136
140
|
options:
|
|
137
141
|
agent: ${vars.agent}
|
|
138
|
-
input: "Create a feature for the idea: ${vars.idea}. Read .spur/run/${vars.__runId}-idea-eval-report.md if present for the enhanced idea and scores. Use 'spur feature create \"<name>\" --json' to create it. Write the feature id to .spur/run/${vars.__runId}-idea-feature-id.txt. If an existing feature is appropriate, use its id instead."
|
|
142
|
+
input: "Create a feature for the idea: ${vars.idea}. Read .spur/run/${vars.__runId}-idea-eval-report.md if present for the enhanced idea and scores. Use 'spur feature create \"<name>\" --json' to create it. Write the feature id to .spur/run/${vars.__runId}-idea-feature-id.txt. If an existing feature is appropriate, use its id instead. Also write two body-only intent artifacts: .spur/run/${vars.__runId}-idea-goal.md with concise Goal intent only (a short statement of what the feature achieves; never task breakdowns, checklists, or how-to steps), and .spur/run/${vars.__runId}-idea-scope.md with explicit in-scope and out-of-scope boundary bullets."
|
|
139
143
|
expectFile: .spur/run/${vars.__runId}-idea-feature-id.txt
|
|
140
144
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
141
145
|
- kind: file.read.into-var
|
|
142
146
|
options:
|
|
143
147
|
path: .spur/run/${vars.__runId}-idea-feature-id.txt
|
|
144
148
|
var: featureId
|
|
149
|
+
- kind: shell
|
|
150
|
+
options:
|
|
151
|
+
command: >-
|
|
152
|
+
mkdir -p .spur/run &&
|
|
153
|
+
test -s .spur/run/$__runId-idea-goal.md &&
|
|
154
|
+
$spurBin feature update "$featureId" --section Goal --from-file .spur/run/$__runId-idea-goal.md
|
|
155
|
+
- kind: shell
|
|
156
|
+
options:
|
|
157
|
+
command: >-
|
|
158
|
+
mkdir -p .spur/run &&
|
|
159
|
+
test -s .spur/run/$__runId-idea-scope.md &&
|
|
160
|
+
$spurBin feature update "$featureId" --section Scope --from-file .spur/run/$__runId-idea-scope.md
|
|
145
161
|
|
|
146
162
|
- id: ac-generate
|
|
147
163
|
description: >
|
|
@@ -177,7 +193,7 @@ states:
|
|
|
177
193
|
options:
|
|
178
194
|
command: >-
|
|
179
195
|
if test -s .spur/run/$__runId-idea-ac-content.md; then
|
|
180
|
-
$spurBin feature update $featureId --section "Acceptance Criteria" --from-file .spur/run/$__runId-idea-ac-content.md && date -u +%Y-%m-%dT%H:%M:%SZ > .spur/run/$__runId-idea-ac-done.txt;
|
|
196
|
+
$spurBin feature update "$featureId" --section "Acceptance Criteria" --from-file .spur/run/$__runId-idea-ac-content.md && date -u +%Y-%m-%dT%H:%M:%SZ > .spur/run/$__runId-idea-ac-done.txt;
|
|
181
197
|
else
|
|
182
198
|
exit 0;
|
|
183
199
|
fi
|
|
@@ -206,13 +222,33 @@ states:
|
|
|
206
222
|
Dispatch sp:sys-architecture to produce the system design. This state runs
|
|
207
223
|
only when needs_design=true (design=auto signal path). The agent creates ADR
|
|
208
224
|
entries, architecture updates, and design satellites through constitution rules.
|
|
209
|
-
|
|
225
|
+
A run-scoped design-review artifact (.spur/run/${vars.__runId}-idea-design-review.md)
|
|
226
|
+
with fixed headings `## Proposed design`, `## Operator feedback`, and `## Reconciliation`
|
|
227
|
+
carries operator rejection feedback back into this state: on the first pass the agent
|
|
228
|
+
fills `## Proposed design`; on retry (operator feedback present) it revises the design,
|
|
229
|
+
records the reconciliation, and updates invalidated Acceptance Criteria through
|
|
230
|
+
`spur feature update` before design can exit. The design-approval state follows (taste gate).
|
|
210
231
|
onEnter:
|
|
232
|
+
- kind: shell
|
|
233
|
+
options:
|
|
234
|
+
command: >-
|
|
235
|
+
mkdir -p .spur/run &&
|
|
236
|
+
REVIEW=".spur/run/$__runId-idea-design-review.md" &&
|
|
237
|
+
test -f "$REVIEW" || printf '## Proposed design\n\n## Operator feedback\n\n## Reconciliation\n' > "$REVIEW"
|
|
211
238
|
- kind: agent.run
|
|
212
239
|
options:
|
|
213
240
|
agent: ${vars.agent}
|
|
214
|
-
input: "Run sp:sys-architecture for feature ${vars.featureId}. Read the brainstorm artifact
|
|
241
|
+
input: "Run sp:sys-architecture for feature ${vars.featureId}. Read the brainstorm artifact, feature AC, and .spur/run/${vars.__runId}-idea-design-review.md. Produce ADR entries, architecture updates, and design satellites (docs/design/<slug>.md) following the constitution edit rules. Do not write task or feature corpus files directly. Design-review contract (.spur/run/${vars.__runId}-idea-design-review.md, fixed headings `## Proposed design`, `## Operator feedback`, `## Reconciliation`): on the first pass write the proposed design summary under `## Proposed design` and leave `## Operator feedback` empty; on retry with operator feedback present, revise the design/ADR artifacts, document the changes under `## Reconciliation`, and when the feedback invalidates an Acceptance Criteria scenario write the revised AC section body to a file and persist it via `$spurBin feature update \"$featureId\" --section \"Acceptance Criteria\" --from-file <file>` — never edit feature corpus files directly."
|
|
242
|
+
expectFile: .spur/run/${vars.__runId}-idea-design-review.md
|
|
215
243
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
244
|
+
# expectFile proves existence only, and the onEnter skeleton pre-creates the file — so an
|
|
245
|
+
# agent no-op would still pass it (0515 P3-2). Fail closed: the `## Proposed design` section
|
|
246
|
+
# must carry non-whitespace content before design can exit to approval/decompose.
|
|
247
|
+
- kind: shell
|
|
248
|
+
options:
|
|
249
|
+
command: >-
|
|
250
|
+
REVIEW=".spur/run/$__runId-idea-design-review.md" &&
|
|
251
|
+
awk '/^## Proposed design/{f=1; next} /^## Operator feedback/{f=0} f' "$REVIEW" | grep -q '[^[:space:]]'
|
|
216
252
|
|
|
217
253
|
- id: design-approval
|
|
218
254
|
description: >
|
|
@@ -226,6 +262,11 @@ states:
|
|
|
226
262
|
agent passes. The onEnter shell increments a reject counter; the `no`
|
|
227
263
|
edge to system-design is capped at 1 revise, after which the run
|
|
228
264
|
terminates as `failed` naming this gate.
|
|
265
|
+
|
|
266
|
+
Rejection must carry operator feedback: before answering `no`, the operator
|
|
267
|
+
records the concrete issue(s) in the run-scoped design-review artifact
|
|
268
|
+
(.spur/run/${vars.__runId}-idea-design-review.md) under `## Operator feedback` —
|
|
269
|
+
the revision pass reads it to reconcile invalidated AC before re-approval.
|
|
229
270
|
pause: true
|
|
230
271
|
onEnter:
|
|
231
272
|
- kind: shell
|
|
@@ -233,23 +274,45 @@ states:
|
|
|
233
274
|
command: 'mkdir -p .spur/run && count=$(cat .spur/run/$__runId-idea-design-reject-count 2>/dev/null || echo 0); echo $((count + 1)) > .spur/run/$__runId-idea-design-reject-count'
|
|
234
275
|
- kind: hitl.confirm
|
|
235
276
|
options:
|
|
236
|
-
prompt: "Review the system design for feature ${vars.featureId}. Approve to proceed to decomposition?"
|
|
277
|
+
prompt: "Review the system design for feature ${vars.featureId}. Approve to proceed to decomposition? To reject, first record your feedback in .spur/run/${vars.__runId}-idea-design-review.md under the `## Operator feedback` heading, then answer no."
|
|
237
278
|
|
|
238
279
|
- id: decompose
|
|
239
280
|
description: >
|
|
240
281
|
Dispatch sp:spec-decomposition with the brainstorm artifact, feature AC, and
|
|
241
282
|
design doc as input. The agent produces a task-batch JSON file at
|
|
242
|
-
.spur/run/${vars.__runId}-idea-task-batch.json, validated against task-batch.schema.json
|
|
283
|
+
.spur/run/${vars.__runId}-idea-task-batch.json, validated against task-batch.schema.json,
|
|
284
|
+
plus the private task-order sidecar .spur/run/${vars.__runId}-idea-task-order.json
|
|
285
|
+
(a JSON array of { name, depends_on_names[] }; `[]` when no ordering exists).
|
|
286
|
+
A post-agent shell action validates the sidecar shape before decomposition
|
|
287
|
+
can exit — missing or ambiguous title-to-WBS resolution fails before handoff.
|
|
243
288
|
onEnter:
|
|
244
289
|
- kind: shell
|
|
245
290
|
options:
|
|
246
|
-
command: 'mkdir -p .spur/run && count=$(cat .spur/run/$__runId-idea-decompose-retry-count 2>/dev/null || echo 0); echo $((count + 1)) > .spur/run/$__runId-idea-decompose-retry-count; rm -f .spur/run/$__runId-idea-task-batch.json .spur/run/$__runId-idea-batch-create.done .spur/run/$__runId-idea-batch-create.failed'
|
|
291
|
+
command: 'mkdir -p .spur/run && count=$(cat .spur/run/$__runId-idea-decompose-retry-count 2>/dev/null || echo 0); echo $((count + 1)) > .spur/run/$__runId-idea-decompose-retry-count; rm -f .spur/run/$__runId-idea-task-batch.json .spur/run/$__runId-idea-task-order.json .spur/run/$__runId-idea-batch-create.done .spur/run/$__runId-idea-batch-create.failed .spur/run/$__runId-idea-batch-create-result.json .spur/run/$__runId-idea-handoff.md'
|
|
247
292
|
- kind: agent.run
|
|
248
293
|
options:
|
|
249
294
|
agent: ${vars.agent}
|
|
250
|
-
input: "Run sp:spec-decomposition for feature ${vars.featureId}. Read the brainstorm artifact, feature AC, and design doc. SIZING FIRST, before any JSON: apply the skill's `Default to NOT decomposing` rubric to the whole unit of work — if it scores 0-2 the correct output is a ONE-entry batch, not many. Scenario count is not task count: merge scenarios that one task delivers (same file surface, same subsystem, or unreadable apart in review), and list every scenario a task covers in its background. Merging never costs AC coverage — one task may carry several scenarios. Do not emit one entry per scenario or per requirement by reflex. Then produce a task-batch JSON array at .spur/run/${vars.__runId}-idea-task-batch.json, validated against apps/cli/schemas/task-batch.schema.json. Schema-permitted fields per entry: `name`, `background`, `requirements`, `feature_id`, `parent_wbs`, `priority`, `tags`, `template` — schema validation rejects anything else. Acceptance Criteria, Design, and Plan sections are filled in by the per-task refine step after batch-create, NOT at decompose time. Validate locally against the schema before emitting."
|
|
295
|
+
input: "Run sp:spec-decomposition for feature ${vars.featureId}. Read the brainstorm artifact, feature AC, and design doc. SIZING FIRST, before any JSON: apply the skill's `Default to NOT decomposing` rubric to the whole unit of work — if it scores 0-2 the correct output is a ONE-entry batch, not many. Scenario count is not task count: merge scenarios that one task delivers (same file surface, same subsystem, or unreadable apart in review), and list every scenario a task covers in its background. Merging never costs AC coverage — one task may carry several scenarios. Do not emit one entry per scenario or per requirement by reflex. Then produce a task-batch JSON array at .spur/run/${vars.__runId}-idea-task-batch.json, validated against apps/cli/schemas/task-batch.schema.json. Schema-permitted fields per entry: `name`, `background`, `requirements`, `feature_id`, `parent_wbs`, `priority`, `tags`, `template` — schema validation rejects anything else. Acceptance Criteria, Design, and Plan sections are filled in by the per-task refine step after batch-create, NOT at decompose time. Validate locally against the schema before emitting. Also emit the private task-order sidecar at .spur/run/${vars.__runId}-idea-task-order.json: a JSON array (one entry per batch item) of `{ name: <exact batch item name>, depends_on_names: [<batch item names>] }` declaring ordering/dependencies between the batch items; use `[]` when no ordering exists. Every `name` and every dependency must match exactly one batch item `name` — it is private workflow data, not part of task-batch.schema.json."
|
|
251
296
|
expectFile: .spur/run/${vars.__runId}-idea-task-batch.json
|
|
252
297
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
298
|
+
# R1 (0518): the task-order sidecar is the ordering contract for handoff-finalize.
|
|
299
|
+
# Validate it fails closed: sidecar must be an array, batch names unique, sidecar names
|
|
300
|
+
# unique, every sidecar name/dependency must refer to exactly one batch name, and — the
|
|
301
|
+
# converse (F2, 0518 verify) — every batch name must appear in the sidecar, so a partial
|
|
302
|
+
# sidecar can never silently skip `task deps` for an unlisted item (`[]` is valid).
|
|
303
|
+
- kind: shell
|
|
304
|
+
options:
|
|
305
|
+
command: >-
|
|
306
|
+
BATCH=".spur/run/$__runId-idea-task-batch.json" &&
|
|
307
|
+
ORDER=".spur/run/$__runId-idea-task-order.json" &&
|
|
308
|
+
jq -e --slurpfile b "$BATCH" '
|
|
309
|
+
(type == "array") and
|
|
310
|
+
(($b[0] | map(.name) | length) == ($b[0] | map(.name) | unique | length)) and
|
|
311
|
+
((map(.name) | length) == (map(.name) | unique | length)) and
|
|
312
|
+
((map(.name) - ($b[0] | map(.name))) | length == 0) and
|
|
313
|
+
((($b[0] | map(.name)) - map(.name)) | length == 0) and
|
|
314
|
+
(((map(.depends_on_names // []) | flatten | unique) - ($b[0] | map(.name))) | length == 0)
|
|
315
|
+
' "$ORDER" >/dev/null
|
|
253
316
|
|
|
254
317
|
- id: batch-create
|
|
255
318
|
description: >
|
|
@@ -269,21 +332,116 @@ states:
|
|
|
269
332
|
description: >
|
|
270
333
|
Executes task batch creation exactly once per decomposition attempt. The
|
|
271
334
|
side effect lives in onEnter and records a sentinel; transition guards only
|
|
272
|
-
inspect sentinel/retry state.
|
|
335
|
+
inspect sentinel/retry state. The `--json` result is captured atomically in
|
|
336
|
+
.spur/run/${vars.__runId}-idea-batch-create-result.json (temp file + mv); the done
|
|
337
|
+
sentinel is written only after the JSON parses and `created == (.wbs | length)`,
|
|
338
|
+
so handoff-finalize can zip batch names to the returned WBS list. Any failure
|
|
339
|
+
(CLI error or malformed result) writes the failed sentinel, preserving the
|
|
340
|
+
existing retry behavior.
|
|
273
341
|
onEnter:
|
|
274
342
|
- kind: shell
|
|
275
343
|
options:
|
|
276
|
-
command:
|
|
344
|
+
command: >-
|
|
345
|
+
if test -f .spur/run/$__runId-idea-batch-create.done; then exit 0; fi;
|
|
346
|
+
rm -f .spur/run/$__runId-idea-batch-create.failed .spur/run/$__runId-idea-batch-create-result.json .spur/run/$__runId-idea-batch-create-result.json.tmp;
|
|
347
|
+
if $spurBin task batch-create --file .spur/run/$__runId-idea-task-batch.json --json > .spur/run/$__runId-idea-batch-create-result.json.tmp && jq -e ".created == (.wbs | length)" .spur/run/$__runId-idea-batch-create-result.json.tmp >/dev/null 2>&1; then
|
|
348
|
+
mv .spur/run/$__runId-idea-batch-create-result.json.tmp .spur/run/$__runId-idea-batch-create-result.json &&
|
|
349
|
+
date -u +%Y-%m-%dT%H:%M:%SZ > .spur/run/$__runId-idea-batch-create.done;
|
|
350
|
+
else
|
|
351
|
+
rm -f .spur/run/$__runId-idea-batch-create-result.json.tmp;
|
|
352
|
+
date -u +%Y-%m-%dT%H:%M:%SZ > .spur/run/$__runId-idea-batch-create.failed;
|
|
353
|
+
fi
|
|
354
|
+
|
|
355
|
+
- id: handoff-finalize
|
|
356
|
+
description: >
|
|
357
|
+
Post-create finalization (0518): zip batch item names to the created WBS
|
|
358
|
+
values from the captured batch-create result (equal-length/unique-name
|
|
359
|
+
checks), apply every non-empty depends_on_names list through
|
|
360
|
+
`spur task deps`, refresh the feature roster, check every created task, and
|
|
361
|
+
write the run-scoped handoff report
|
|
362
|
+
(.spur/run/${vars.__runId}-idea-handoff.md) with exactly one next command.
|
|
363
|
+
A mapping or CLI error fails the run before terminal handoff; an unready
|
|
364
|
+
task is a successful planning outcome recorded as a refineall recommendation.
|
|
365
|
+
onEnter:
|
|
366
|
+
# F1 (0518 verify): the per-task check loop fails closed. A stderr-only (non-JSON)
|
|
367
|
+
# `task check --json` exception would otherwise drop that task from the JSONL results
|
|
368
|
+
# without failing the run (the loop's exit status is its last body command), silently
|
|
369
|
+
# flipping the recommendation to runall — violating R3's any-fail=>refineall invariant.
|
|
370
|
+
# Each `jq -c … >> "$CHECKS"` appends under `|| exit 1`, and a row-count assertion
|
|
371
|
+
# (CHECKS lines == WBS count) gates the recommendation computation.
|
|
372
|
+
- kind: shell
|
|
373
|
+
options:
|
|
374
|
+
command: >-
|
|
375
|
+
BATCH=".spur/run/$__runId-idea-task-batch.json" &&
|
|
376
|
+
RESULT=".spur/run/$__runId-idea-batch-create-result.json" &&
|
|
377
|
+
ORDER=".spur/run/$__runId-idea-task-order.json" &&
|
|
378
|
+
REPORT=".spur/run/$__runId-idea-handoff.md" &&
|
|
379
|
+
DEPMAP=".spur/run/$__runId-idea-dep-map.tsv" &&
|
|
380
|
+
CHECKS=".spur/run/$__runId-idea-check-results.jsonl" &&
|
|
381
|
+
rm -f "$DEPMAP" "$CHECKS" &&
|
|
382
|
+
jq -e --slurpfile b "$BATCH" '(.wbs | length) == ($b[0] | length) and (($b[0] | map(.name) | unique | length) == ($b[0] | length))' "$RESULT" >/dev/null &&
|
|
383
|
+
jq -r --slurpfile b "$BATCH" --slurpfile r "$RESULT" --slurpfile o "$ORDER" '
|
|
384
|
+
($b[0] | map(.name)) as $names | ($r[0].wbs) as $wbs |
|
|
385
|
+
$o[0] | .[] |
|
|
386
|
+
(.name as $n | $names | index($n)) as $i |
|
|
387
|
+
(if $i == null or $wbs[$i] == null then "MISSING" else $wbs[$i] end) as $own |
|
|
388
|
+
((.depends_on_names // []) | map(. as $dep | $names | index($dep) as $d | if $d == null then "MISSING" else $wbs[$d] end) | join(" ")) as $deps |
|
|
389
|
+
[.name, $own, $deps] | @tsv
|
|
390
|
+
' "$ORDER" > "$DEPMAP" &&
|
|
391
|
+
while IFS="$(printf '\t')" read -r name own deps; do
|
|
392
|
+
test "$own" != MISSING || exit 1;
|
|
393
|
+
if test -n "$deps"; then
|
|
394
|
+
for d in $deps; do test "$d" != MISSING || exit 1; done;
|
|
395
|
+
$spurBin task deps "$own" set $deps --json >/dev/null || exit 1;
|
|
396
|
+
fi;
|
|
397
|
+
done < "$DEPMAP" &&
|
|
398
|
+
$spurBin feature refresh --feature "$featureId" --json >/dev/null &&
|
|
399
|
+
WBS_LIST=$(jq -r '.wbs | join(" ")' "$RESULT") &&
|
|
400
|
+
for wbs in $WBS_LIST; do
|
|
401
|
+
TMP=".spur/run/$__runId-idea-check-$wbs.tmp";
|
|
402
|
+
if $spurBin task check "$wbs" --json > "$TMP" 2>&1; then P=true; else P=false; fi;
|
|
403
|
+
jq -c --arg w "$wbs" --argjson p "$P" 'if (type == "array" and length > 0) then {wbs: $w, pass: $p, status: (.[0].status // "unknown")} else {wbs: $w, pass: $p, status: "missing"} end' "$TMP" >> "$CHECKS" || exit 1;
|
|
404
|
+
rm -f "$TMP";
|
|
405
|
+
done &&
|
|
406
|
+
CHECK_ROWS=$(wc -l < "$CHECKS" | tr -d ' ') &&
|
|
407
|
+
WBS_COUNT=$(printf '%s\n' $WBS_LIST | wc -l | tr -d ' ') &&
|
|
408
|
+
test "$CHECK_ROWS" = "$WBS_COUNT" &&
|
|
409
|
+
NEXT=$(jq -r --arg feature "$featureId" --slurpfile c "$CHECKS" 'if ($c | any(.[]; .pass == false)) then "/sp:dev-refineall --feature \($feature) --auto --depth ready" else "/sp:dev-runall --feature \($feature) --auto" end' "$RESULT") &&
|
|
410
|
+
{
|
|
411
|
+
echo "# Idea pipeline handoff report";
|
|
412
|
+
echo;
|
|
413
|
+
echo "Feature: $featureId";
|
|
414
|
+
echo "Run ID: $__runId";
|
|
415
|
+
echo;
|
|
416
|
+
echo "## Created tasks";
|
|
417
|
+
printf '%s\n' $WBS_LIST | sed 's/^/ - /';
|
|
418
|
+
echo;
|
|
419
|
+
echo "## Per-task readiness (spur task check)";
|
|
420
|
+
echo;
|
|
421
|
+
echo "| WBS | Outcome |";
|
|
422
|
+
echo "|-----|---------|";
|
|
423
|
+
while IFS= read -r line; do
|
|
424
|
+
w=$(printf '%s' "$line" | jq -r '.wbs');
|
|
425
|
+
o=$(printf '%s' "$line" | jq -r 'if .pass then "PASS" else "FAIL" end');
|
|
426
|
+
printf '| %s | %s |\n' "$w" "$o";
|
|
427
|
+
done < "$CHECKS";
|
|
428
|
+
echo;
|
|
429
|
+
echo "## Next command";
|
|
430
|
+
echo;
|
|
431
|
+
echo "$NEXT";
|
|
432
|
+
} > "$REPORT"
|
|
277
433
|
|
|
278
434
|
- id: handoff
|
|
279
435
|
description: >
|
|
280
|
-
Terminal — idea pipeline complete.
|
|
281
|
-
the
|
|
282
|
-
|
|
436
|
+
Terminal — idea pipeline complete. Ordering applied, roster refreshed, and
|
|
437
|
+
the handoff report written at .spur/run/${vars.__runId}-idea-handoff.md:
|
|
438
|
+
feature id, task WBS list, per-task readiness, and exactly one recommended
|
|
439
|
+
next command (ready-depth refineall when any task is unready, else auto
|
|
440
|
+
runall). No task execution — the pipeline stops here.
|
|
283
441
|
onEnter:
|
|
284
442
|
- kind: note
|
|
285
443
|
options:
|
|
286
|
-
message: "Idea pipeline handoff. Feature: ${vars.featureId}.
|
|
444
|
+
message: "Idea pipeline handoff. Feature: ${vars.featureId}. See .spur/run/${vars.__runId}-idea-handoff.md for the created task list and recommended next command."
|
|
287
445
|
# Checkpoint write: record session state for resume (Phase 4, task 0171 R3)
|
|
288
446
|
- kind: shell
|
|
289
447
|
options:
|
|
@@ -374,7 +532,7 @@ transitions:
|
|
|
374
532
|
guard:
|
|
375
533
|
kind: shell
|
|
376
534
|
options:
|
|
377
|
-
command: 'test "$profile" = auto && $spurBin feature check $featureId && test "$design" = auto && test "$(jq -r .needs_design .spur/run/$__runId-idea-needs-design.json 2>/dev/null)" != false'
|
|
535
|
+
command: 'test "$profile" = auto && $spurBin feature check "$featureId" && test "$design" = auto && test "$(jq -r .needs_design .spur/run/$__runId-idea-needs-design.json 2>/dev/null)" != false'
|
|
378
536
|
# Auto-skip 2: pass + skip-design route → decompose
|
|
379
537
|
- from: ac-generate
|
|
380
538
|
to: decompose
|
|
@@ -382,7 +540,7 @@ transitions:
|
|
|
382
540
|
guard:
|
|
383
541
|
kind: shell
|
|
384
542
|
options:
|
|
385
|
-
command: 'test "$profile" = auto && $spurBin feature check $featureId && (test "$design" = skip || (test "$design" = auto && test "$(jq -r .needs_design .spur/run/$__runId-idea-needs-design.json 2>/dev/null)" = false))'
|
|
543
|
+
command: 'test "$profile" = auto && $spurBin feature check "$featureId" && (test "$design" = skip || (test "$design" = auto && test "$(jq -r .needs_design .spur/run/$__runId-idea-needs-design.json 2>/dev/null)" = false))'
|
|
386
544
|
# Auto-skip 3: check failed, retry < 3 → loop back to ac-generate (self-loop)
|
|
387
545
|
- from: ac-generate
|
|
388
546
|
to: ac-generate
|
|
@@ -390,7 +548,7 @@ transitions:
|
|
|
390
548
|
guard:
|
|
391
549
|
kind: shell
|
|
392
550
|
options:
|
|
393
|
-
command: 'test "$profile" = auto && ! $spurBin feature check $featureId && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -lt 3'
|
|
551
|
+
command: 'test "$profile" = auto && ! $spurBin feature check "$featureId" && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -lt 3'
|
|
394
552
|
# Auto-skip 4: check failed, retry cap reached → escalate to failed
|
|
395
553
|
- from: ac-generate
|
|
396
554
|
to: failed
|
|
@@ -398,7 +556,7 @@ transitions:
|
|
|
398
556
|
guard:
|
|
399
557
|
kind: shell
|
|
400
558
|
options:
|
|
401
|
-
command: 'test "$profile" = auto && ! $spurBin feature check $featureId && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -ge 3'
|
|
559
|
+
command: 'test "$profile" = auto && ! $spurBin feature check "$featureId" && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -ge 3'
|
|
402
560
|
# Interactive fallback: enter feature-check HITL gate
|
|
403
561
|
- from: ac-generate
|
|
404
562
|
to: feature-check
|
|
@@ -414,28 +572,28 @@ transitions:
|
|
|
414
572
|
guard:
|
|
415
573
|
kind: shell
|
|
416
574
|
options:
|
|
417
|
-
command: 'test "$__hitlAnswer" = yes && $spurBin feature check $featureId && test "$design" = auto && test "$(jq -r .needs_design .spur/run/$__runId-idea-needs-design.json 2>/dev/null)" != false'
|
|
575
|
+
command: 'test "$__hitlAnswer" = yes && $spurBin feature check "$featureId" && test "$design" = auto && test "$(jq -r .needs_design .spur/run/$__runId-idea-needs-design.json 2>/dev/null)" != false'
|
|
418
576
|
- from: feature-check
|
|
419
577
|
to: decompose
|
|
420
578
|
description: Feature check passed, skip-design route — go directly to decompose.
|
|
421
579
|
guard:
|
|
422
580
|
kind: shell
|
|
423
581
|
options:
|
|
424
|
-
command: 'test "$__hitlAnswer" = yes && $spurBin feature check $featureId && (test "$design" = skip || (test "$design" = auto && test "$(jq -r .needs_design .spur/run/$__runId-idea-needs-design.json 2>/dev/null)" = false))'
|
|
582
|
+
command: 'test "$__hitlAnswer" = yes && $spurBin feature check "$featureId" && (test "$design" = skip || (test "$design" = auto && test "$(jq -r .needs_design .spur/run/$__runId-idea-needs-design.json 2>/dev/null)" = false))'
|
|
425
583
|
- from: feature-check
|
|
426
584
|
to: ac-generate
|
|
427
585
|
description: "Feature check failed — revise AC (retry cap: 3)."
|
|
428
586
|
guard:
|
|
429
587
|
kind: shell
|
|
430
588
|
options:
|
|
431
|
-
command: '(test "$__hitlAnswer" = no || ! $spurBin feature check $featureId) && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -lt 3'
|
|
589
|
+
command: '(test "$__hitlAnswer" = no || ! $spurBin feature check "$featureId") && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -lt 3'
|
|
432
590
|
- from: feature-check
|
|
433
591
|
to: failed
|
|
434
592
|
description: Feature check failed after 3 retries — escalate to failed.
|
|
435
593
|
guard:
|
|
436
594
|
kind: shell
|
|
437
595
|
options:
|
|
438
|
-
command: '(test "$__hitlAnswer" = no || ! $spurBin feature check $featureId) && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -ge 3'
|
|
596
|
+
command: '(test "$__hitlAnswer" = no || ! $spurBin feature check "$featureId") && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -ge 3'
|
|
439
597
|
- from: feature-check
|
|
440
598
|
to: cancelled
|
|
441
599
|
description: Operator cancelled the feature-check gate.
|
|
@@ -448,25 +606,32 @@ transitions:
|
|
|
448
606
|
# Declaration order: auto-skip guard tried FIRST (like task-pipeline's review->verify pattern).
|
|
449
607
|
- from: system-design
|
|
450
608
|
to: decompose
|
|
451
|
-
description: profile=auto AND design_approved=true — skip design approval gate.
|
|
609
|
+
description: profile=auto AND design_approved=true AND feature check passes — skip design approval gate.
|
|
452
610
|
guard:
|
|
453
611
|
kind: shell
|
|
454
612
|
options:
|
|
455
|
-
command: 'test "$profile" = auto && test "$design_approved" = true'
|
|
613
|
+
command: 'test "$profile" = auto && test "$design_approved" = true && $spurBin feature check "$featureId"'
|
|
456
614
|
- from: system-design
|
|
457
615
|
to: design-approval
|
|
458
616
|
description: System design done — gate on design approval.
|
|
459
617
|
guard:
|
|
460
618
|
kind: always
|
|
461
619
|
|
|
462
|
-
# ── design-approval -> decompose (after operator approval) ──
|
|
620
|
+
# ── design-approval -> decompose (after operator approval), AC-failure -> feature-check ──
|
|
463
621
|
- from: design-approval
|
|
464
622
|
to: decompose
|
|
465
|
-
description: Design approved — proceed to decomposition.
|
|
623
|
+
description: Design approved and feature check passes — proceed to decomposition.
|
|
466
624
|
guard:
|
|
467
625
|
kind: shell
|
|
468
626
|
options:
|
|
469
|
-
command: 'test "$__hitlAnswer" = yes'
|
|
627
|
+
command: 'test "$__hitlAnswer" = yes && $spurBin feature check "$featureId"'
|
|
628
|
+
- from: design-approval
|
|
629
|
+
to: feature-check
|
|
630
|
+
description: "Design approved but feature check fails — route back through the AC gate (revise AC, retry cap: 3)."
|
|
631
|
+
guard:
|
|
632
|
+
kind: shell
|
|
633
|
+
options:
|
|
634
|
+
command: 'test "$__hitlAnswer" = yes && ! $spurBin feature check "$featureId"'
|
|
470
635
|
- from: design-approval
|
|
471
636
|
to: system-design
|
|
472
637
|
description: "Design rejected - revise system design (cap: 1 revise)."
|
|
@@ -526,10 +691,10 @@ transitions:
|
|
|
526
691
|
options:
|
|
527
692
|
command: 'test "$__hitlAnswer" = cancel'
|
|
528
693
|
|
|
529
|
-
# ── batch-create-run: success -> handoff, failure -> decompose (retry cap: 3), failure+cap -> failed ──
|
|
694
|
+
# ── batch-create-run: success -> handoff-finalize, failure -> decompose (retry cap: 3), failure+cap -> failed ──
|
|
530
695
|
- from: batch-create-run
|
|
531
|
-
to: handoff
|
|
532
|
-
description: Batch created — handoff.
|
|
696
|
+
to: handoff-finalize
|
|
697
|
+
description: Batch created and result captured — apply ordering, refresh roster, write handoff report.
|
|
533
698
|
guard:
|
|
534
699
|
kind: shell
|
|
535
700
|
options:
|
|
@@ -548,3 +713,10 @@ transitions:
|
|
|
548
713
|
kind: shell
|
|
549
714
|
options:
|
|
550
715
|
command: 'test -f .spur/run/$__runId-idea-batch-create.failed && test "$(cat .spur/run/$__runId-idea-decompose-retry-count 2>/dev/null || echo 0)" -ge 3'
|
|
716
|
+
|
|
717
|
+
# ── handoff-finalize: -> handoff (terminal) ──
|
|
718
|
+
- from: handoff-finalize
|
|
719
|
+
to: handoff
|
|
720
|
+
description: Ordering applied, roster refreshed, handoff report written — terminal handoff.
|
|
721
|
+
guard:
|
|
722
|
+
kind: always
|
|
@@ -451,7 +451,7 @@ states:
|
|
|
451
451
|
- kind: agent.run
|
|
452
452
|
options:
|
|
453
453
|
agent: ${vars.agent}
|
|
454
|
-
input: /sp:dev-verify ${vars.wbs} --auto --fix all
|
|
454
|
+
input: /sp:dev-verify ${vars.wbs} --auto --fix all --focus all
|
|
455
455
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
456
456
|
answerFile: .spur/run/${vars.wbs}-verify-answer.txt
|
|
457
457
|
- kind: shell
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/spur",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.45",
|
|
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.30",
|
|
57
|
+
"@gobing-ai/ts-ai-runner": "^0.4.30",
|
|
58
|
+
"@gobing-ai/ts-dual-workflow-engine": "^0.4.30",
|
|
59
|
+
"@gobing-ai/ts-infra": "^0.4.30",
|
|
60
|
+
"@gobing-ai/ts-llm-jsonl-importer": "^0.4.30",
|
|
61
|
+
"@gobing-ai/ts-rule-engine": "^0.4.30",
|
|
62
|
+
"@gobing-ai/ts-runtime": "^0.4.30",
|
|
63
|
+
"@gobing-ai/ts-utils": "^0.4.30",
|
|
64
64
|
"@types/bun": "1.3.14",
|
|
65
65
|
"@types/figlet": "^1.7.0",
|
|
66
66
|
"@types/node-notifier": "8.0.5",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
description: "Prompt-first feature frontier prioritizer — answers 'which feature should we work on now?' with a ranked, evidence-carrying frontier, and emits rank-distorting tree defects as proposals /sp:dev-featurechange consumes. Triggers: find next, which feature, feature ranking, frontier priority, what should I work on."
|
|
3
|
-
argument-hint: "[--task [<feature-id>]] [--agent <inline|auto|name>] [--json]"
|
|
3
|
+
argument-hint: "[--task [<feature-id>]] [--agent <inline|auto|name>] [--auto] [--json]"
|
|
4
4
|
allowed-tools: ["Bash", "Read", "Write", "Grep", "Glob", "Skill"]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -20,6 +20,7 @@ Answers *"which X"* — the question `/sp:dev-next` deliberately does not (next-
|
|
|
20
20
|
| --- | --- | --- |
|
|
21
21
|
| `--task` `[<feature-id>]` | After the report, confirm one target and dispatch the planning half to produce implement-ready tasks. | omitted |
|
|
22
22
|
| `--agent` `<inline\|auto\|name>` | Who runs the model-bearing analysis. | inline |
|
|
23
|
+
| `--auto` | Skip the `--task` confirm (accept the offered target) and forward into dispatched children. | off |
|
|
23
24
|
| `--json` | Emit the ranked frontier, gated list, and proposals as a JSON envelope. | off |
|
|
24
25
|
|
|
25
26
|
For shared semantics, see the [flag glossary](../skills/spur-dev/references/flag-glossary.md).
|
|
@@ -30,23 +31,30 @@ For shared semantics, see the [flag glossary](../skills/spur-dev/references/flag
|
|
|
30
31
|
/sp:dev-find-next
|
|
31
32
|
/sp:dev-find-next --json
|
|
32
33
|
/sp:dev-find-next --task
|
|
34
|
+
/sp:dev-find-next --task --auto
|
|
33
35
|
/sp:dev-find-next --task H1 --auto
|
|
34
36
|
```
|
|
35
37
|
|
|
36
38
|
**`--task` — confirm, then dispatch.** The command offers the rank-1 candidate (or the id you pass),
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
then routes on the tier the ranking already assigned: a **T3** feature with valid AC and no tasks
|
|
40
|
+
goes to `/sp:dev-plan --feature <id>` and then `/sp:dev-refineall --feature <id> --auto --depth ready`;
|
|
41
|
+
a **T1** feature (open tasks already exist) goes to refineall only, never a second decomposition;
|
|
42
|
+
**T2** (blocked), **T4** (stale-done), and T3 with invalid AC stop with their reason. The command
|
|
43
|
+
creates no tasks itself — decomposition and its schema gate belong to `/sp:dev-plan`.
|
|
44
|
+
|
|
45
|
+
**Confirm under `--auto`.** Without `--auto`, confirmation is interactive (accept the offer, name
|
|
46
|
+
another candidate, or decline). With `--auto`, the offered target is accepted automatically —
|
|
47
|
+
rank-1 for bare `--task`, or the explicit `--task <feature-id>` — and dispatch proceeds without a
|
|
48
|
+
HITL pause. Passing `--auto` is operator pre-consent to take the ranking's recommendation; it is
|
|
49
|
+
not a license to invent a different target or to bypass T2/T4 refuse / invalid-AC stop. `--auto`
|
|
50
|
+
is also forwarded to the dispatched children. Declining (interactive path only) writes nothing.
|
|
51
|
+
Without `--task`, `--auto` is a no-op (the ranking report has no HITL gate).
|
|
44
52
|
|
|
45
53
|
Without `--task` the command is read-only with respect to the corpus and docs. Under `--task` the
|
|
46
|
-
only mutation is the one the dispatched commands perform on `docs/tasks*/` after
|
|
47
|
-
command still performs no `spur feature move`, no sync apply, and no write under
|
|
48
|
-
Defect proposals conform to the `docs/plans/feature-tree-restructure-map.md`
|
|
49
|
-
only through `/sp:dev-featurechange` (dry-run → confirm → apply).
|
|
54
|
+
only mutation is the one the dispatched commands perform on `docs/tasks*/` after confirm (or
|
|
55
|
+
auto-accept) — the command still performs no `spur feature move`, no sync apply, and no write under
|
|
56
|
+
`docs/features/**`. Defect proposals conform to the `docs/plans/feature-tree-restructure-map.md`
|
|
57
|
+
schema and are applied only through `/sp:dev-featurechange` (dry-run → confirm → apply).
|
|
50
58
|
|
|
51
59
|
**See also:** skill `sp:next-feature` (SSOT), `sp:next-router` (`/sp:dev-next`),
|
|
52
60
|
`sp:conflict-finding` (the prompt-first template), `sp:spur-cli`.
|
|
@@ -36,7 +36,9 @@ Flags: `--tasks <selector>` (required — explicit WBS list, status pseudo-list,
|
|
|
36
36
|
or `ready`), `--feature <id>` (sugar for `feature:<id>`; when the effective selector is
|
|
37
37
|
feature-derived, `dev-runall` runs `spur feature check <id> --strict --json` **once** before
|
|
38
38
|
resolving tasks — a non-zero strict check aborts the batch with verdict `aborted` and the
|
|
39
|
-
structured findings, before any task pipeline action, task 0510 R2
|
|
39
|
+
structured findings, before any task pipeline action, task 0510 R2; scoped: `L4.scenario-unverified`
|
|
40
|
+
(the expected pre-run state of any not-yet-run feature) is reported verbatim but does not abort —
|
|
41
|
+
any other strict error aborts), `--mode`
|
|
40
42
|
`<sequential|parallel>` (default `sequential`; `parallel` fans out a proven-independent subset —
|
|
41
43
|
see `execution-batch.md` § Parallel Execution), `--keep-going`
|
|
42
44
|
(batch failure policy — skip a failed task's in-batch dependents, continue independents; default
|