@gobing-ai/spur 0.3.85 → 0.3.87
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/pipeline-budgets.json +0 -25
- package/config/plugin-scripts.json +4 -5
- package/config/rules/boundary/env-var-hygiene.yaml +41 -0
- package/config/templates/feature/default.md +2 -0
- package/config/templates/task/brainstorm.md +2 -2
- package/config/templates/task/feature-impl.md +2 -2
- package/config/templates/task/issue.md +2 -2
- package/config/templates/task/meta.md +2 -2
- package/config/templates/task/review.md +2 -2
- package/config/templates/task/standard.md +2 -2
- package/config/transition-shims.json +1 -1
- package/config/workflow-candidates.json +19 -0
- package/config/workflows/feature-lifecycle.yaml +10 -2
- package/config/workflows/feature-verification.yaml +71 -0
- package/config/workflows/idea-pipeline.yaml +87 -39
- package/config/workflows/task-pipeline.yaml +43 -12
- package/config/workflows/wrapup-pipeline.yaml +70 -7
- package/package.json +9 -9
- package/plugins/sp/README.md +7 -7
- package/plugins/sp/commands/dev-idea.md +9 -2
- package/plugins/sp/commands/dev-refactor.md +33 -0
- package/plugins/sp/hooks/agent-hint.ts +5 -4
- package/plugins/sp/hooks/careful-guard.ts +3 -1
- package/plugins/sp/hooks/context-post-tool.ts +2 -1
- package/plugins/sp/hooks/context-session-start.ts +4 -3
- package/plugins/sp/hooks/context-session-stop.ts +2 -1
- package/plugins/sp/hooks/pi/guard-extension.ts +4 -3
- package/plugins/sp/hooks/task-write-guard.ts +4 -3
- package/plugins/sp/lib/idea-handoff.generated.mjs +260 -260
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/references/roles.md +1 -1
- package/plugins/sp/scripts/daily-summary/daily-summary.mjs +13 -4
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +6 -4
- package/plugins/sp/scripts/feature-sync-bounded.mjs +10 -2
- package/plugins/sp/scripts/feature-sync-bounded.ts +2 -1
- package/plugins/sp/scripts/idea-coverage-check.ts +168 -0
- package/plugins/sp/scripts/idea-handoff.mjs +6 -1
- package/plugins/sp/scripts/idea-handoff.ts +4 -1
- package/plugins/sp/scripts/inline-pipeline-parity-check.ts +115 -4
- package/plugins/sp/scripts/inline-run-setup.ts +195 -2
- package/plugins/sp/scripts/pr-reviewing.mjs +8 -1
- package/plugins/sp/scripts/pr-reviewing.ts +2 -1
- package/plugins/sp/scripts/quality-gate.mjs +8 -1
- package/plugins/sp/scripts/quality-gate.ts +2 -1
- package/plugins/sp/scripts/surface-drift-inventory.ts +1 -4
- package/plugins/sp/scripts/task-evidence-precheck.ts +2 -1
- package/plugins/sp/scripts/task-size-precheck.ts +6 -4
- package/plugins/sp/scripts/verify-answer-lint.ts +2 -1
- package/plugins/sp/scripts/workflow-step-profile.mjs +10 -2
- package/plugins/sp/scripts/workflow-step-profile.ts +2 -1
- package/plugins/sp/scripts/wrapup-steps.mjs +8 -1
- package/plugins/sp/scripts/wrapup-steps.ts +2 -1
- package/plugins/sp/skills/brainstorm/SKILL.md +4 -0
- package/plugins/sp/skills/code-refactoring/SKILL.md +155 -0
- package/plugins/sp/skills/code-refactoring/references/finding-schema.md +74 -0
- package/plugins/sp/skills/code-refactoring/references/fix-ladder.md +52 -0
- package/plugins/sp/skills/code-refactoring/references/focus-detection.md +44 -0
- package/plugins/sp/skills/code-refactoring/references/refactor-finding.schema.json +95 -0
- package/plugins/sp/skills/spec-decomposition/references/decomposition.md +23 -16
- package/plugins/sp/skills/spur-cli/references/features/acceptance-criteria.md +4 -2
- package/plugins/sp/skills/spur-cli/references/features.md +6 -1
- package/plugins/sp/skills/spur-cli/references/workflows/authoring-workflows.md +2 -2
- package/plugins/sp/skills/spur-cli/references/workflows/operations.md +3 -3
- package/plugins/sp/skills/spur-cli/references/workflows/workflow-fit-and-tuning.md +1 -1
- package/plugins/sp/skills/spur-cli/references/workflows.md +8 -7
- package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +24 -0
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +4 -5
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +18 -2
- package/plugins/sp/skills/spur-dev/references/done-housekeeping.md +1 -1
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +22 -3
- package/plugins/sp/skills/spur-dev/references/gate-checklists.md +2 -2
- package/plugins/sp/skills/spur-dev/references/idea-evaluation.md +11 -1
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +91 -1
- package/plugins/sp/skills/taste-refactoring-api/SKILL.md +44 -1
- package/plugins/sp/skills/taste-refactoring-api/references/protocol-modes.md +31 -0
- package/plugins/sp/skills/taste-refactoring-architect/SKILL.md +43 -0
- package/plugins/sp/skills/taste-refactoring-tests/SKILL.md +42 -0
- package/plugins/sp/skills/taste-refactoring-ui/SKILL.md +43 -0
- package/schemas/state-machine-workflow.schema.json +5 -0
- package/schemas/task-batch.schema.json +2 -2
- package/schemas/transition-flow-workflow.schema.json +5 -0
- package/spur.js +21106 -20669
- package/web/_astro/BoardApp.CJiqp5pS.js +1 -0
- package/web/_astro/{BoardApp.yW425dRZ.js → BoardApp.yBBcFXWP.js} +4 -4
- package/web/_astro/{TaskDetail.CGwJAinW.js → TaskDetail.DqFJbRFc.js} +1 -1
- package/web/_astro/{arc.B-qNXzSO.js → arc.DL-BpHoi.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.Bdg-xiji.js → architectureDiagram-3BPJPVTR.9IdQYyDq.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.DFArg3Kp.js → blockDiagram-GPEHLZMM.BKsFCqTl.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.BkOLcjyx.js → c4Diagram-AAUBKEIU.DhwI0dh1.js} +1 -1
- package/web/_astro/channel.CX5453qQ.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.DN3c20V4.js → chunk-2J33WTMH.B3QVmQ9S.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.B64K0Ttd.js → chunk-4BX2VUAB.DBSuqs9F.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.C-AJvNwa.js → chunk-55IACEB6.BSYTWAYD.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.DMkr46uS.js → chunk-727SXJPM.cWVuxXfS.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.4_WGbI64.js → chunk-AQP2D5EJ.DpU_Ob3d.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.cbFW_lVE.js → chunk-FMBD7UC4.BykFkyji.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.CDW_-2O1.js → chunk-ND2GUHAM.DwgHlMdY.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.BoYOt_yg.js → chunk-QZHKN3VN.CusXUGWM.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.BNT6XpHQ.js → classDiagram-4FO5ZUOK.fx0ObzkN.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.BNT6XpHQ.js → classDiagram-v2-Q7XG4LA2.fx0ObzkN.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.CI1DQxR3.js → cose-bilkent-S5V4N54A.Z4HgOlsd.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.A8ARH61p.js → cynefin-OW5HDTMX.B5dIZHJu.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.u28ZmqwR.js → dagre-BM42HDAG.DT70Q_Yw.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.Dx8JcrGc.js → diagram-2AECGRRQ.DqHA3XBF.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.BXJUG9GY.js → diagram-5GNKFQAL.BmCem957.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.BtlMuWqW.js → diagram-KO2AKTUF.sn0-hrE0.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.C4T7qaAy.js → diagram-LMA3HP47.BSHe9tVc.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.D5HECE10.js → diagram-OG6HWLK6.DHIc-86k.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.B_hMR3yw.js → erDiagram-TEJ5UH35.Bxayrs7v.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.BAmpcYl3.js → flowDiagram-I6XJVG4X.BkzoE_5I.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.DV1bSWK-.js → ganttDiagram-6RSMTGT7.okT6CvTo.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.DxYKkKrJ.js → gitGraphDiagram-PVQCEYII.CJuYbhC7.js} +1 -1
- package/web/_astro/{infoDiagram-5YYISTIA.BAWY2xMb.js → infoDiagram-5YYISTIA.RqLy7nBo.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.CZssRn6V.js → ishikawaDiagram-YF4QCWOH.BwIcoagw.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.C--muARd.js → journeyDiagram-JHISSGLW.UB1VbWtH.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.D_QCK4et.js → kanban-definition-UN3LZRKU.AaxMKpTk.js} +1 -1
- package/web/_astro/{linear.DzrmTtZ0.js → linear.Nv_xOUjP.js} +1 -1
- package/web/_astro/{mermaid.core.Da03W3iu.js → mermaid.core.Bc4LqQgX.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.6cy-8hR_.js → mindmap-definition-RKZ34NQL.oKUvU_qi.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.C5rS1pdU.js → pieDiagram-4H26LBE5.DQk0oo03.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.w56GZZ6Q.js → quadrantDiagram-W4KKPZXB.BdDjESDa.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.CipX3Pwu.js → requirementDiagram-4Y6WPE33.C2u9hUeH.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.C0VVzgJm.js → sankeyDiagram-5OEKKPKP.CDEoiJST.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.BX2dUUbF.js → sequenceDiagram-3UESZ5HK.D_hT_GAT.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.ypCdgODQ.js → stateDiagram-AJRCARHV.DI8RYG0b.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.In0baEtg.js → stateDiagram-v2-BHNVJYJU.Bkxz4DnP.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.CJN4Vkvl.js → timeline-definition-PNZ67QCA.DSY-kH3-.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.Cf8KkIPY.js → vennDiagram-CIIHVFJN.CpaDtuGr.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.DCZBo9xw.js → wardleyDiagram-YWT4CUSO.DujQWvo8.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.Cm4v_MiJ.js → xychartDiagram-2RQKCTM6.DcM5Y4b9.js} +1 -1
- package/web/index.html +1 -1
- package/config/workflows/basic.yaml +0 -146
- package/config/workflows/docs-pipeline.yaml +0 -350
- package/config/workflows/feature-dev.yaml +0 -288
- package/plugins/sp/scripts/feature-dev-precheck.mjs +0 -146
- package/plugins/sp/scripts/feature-dev-precheck.ts +0 -238
- package/web/_astro/BoardApp.CHenHFia.js +0 -1
- package/web/_astro/channel.CbDHK5UQ.js +0 -1
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-refactoring
|
|
3
|
+
description: "Refactoring coordinator with a preservation contract: routes the four taste lenses (api, architect, tests, ui), normalizes findings to a shared schema and P1–P4 severities, gates applies, runs a fix ladder with revert-on-regression. Use for 'dev-refactor', 'safe refactor', 'refactor while keeping every feature'."
|
|
4
|
+
license: Apache-2.0
|
|
5
|
+
metadata:
|
|
6
|
+
author: spur
|
|
7
|
+
version: "1.0"
|
|
8
|
+
platforms: "claude-code,codex,openclaw,opencode,antigravity"
|
|
9
|
+
category: execution
|
|
10
|
+
interactions:
|
|
11
|
+
- technique
|
|
12
|
+
operations:
|
|
13
|
+
- refactor
|
|
14
|
+
openclaw:
|
|
15
|
+
emoji: "♻️"
|
|
16
|
+
see_also:
|
|
17
|
+
- sp:taste-refactoring-api
|
|
18
|
+
- sp:taste-refactoring-architect
|
|
19
|
+
- sp:taste-refactoring-tests
|
|
20
|
+
- sp:taste-refactoring-ui
|
|
21
|
+
- sp:code-simplification
|
|
22
|
+
- sp:code-review
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
# code-refactoring — the lens-routed refactoring coordinator
|
|
26
|
+
|
|
27
|
+
Own everything a cheaper executor must get right **mechanically**: routing, finding schema,
|
|
28
|
+
severity normalization, preservation classification, gate policy, apply loop, artifacts. The four
|
|
29
|
+
taste lenses keep their principles and judgments; this skill turns their prose into gated,
|
|
30
|
+
checkable, reversible work.
|
|
31
|
+
|
|
32
|
+
The product is **analysis first, edits second**: with the default `--fix none` the run writes two
|
|
33
|
+
artifacts and performs no edit at all.
|
|
34
|
+
|
|
35
|
+
Backs `/sp:dev-refactor`. Authority: `docs/design/dev-refactor-command.md` (§2 phases, §4–§8
|
|
36
|
+
contracts, §11 invariants).
|
|
37
|
+
|
|
38
|
+
## Inputs
|
|
39
|
+
|
|
40
|
+
| Input | Meaning | Default |
|
|
41
|
+
| --- | --- | --- |
|
|
42
|
+
| `--scope <path>` | Path bound; no edit may land outside it. | working tree (recent changes) |
|
|
43
|
+
| `--focus <api\|architect\|tests\|ui\|auto>` | Lens set; comma list allowed. | `auto` |
|
|
44
|
+
| `--fix <none\|blockers-first\|all>` | Apply policy. | `none` |
|
|
45
|
+
| `--check <cmd>` | Baseline and per-fix verification command. | project gate (`bun run spur-check` when present) |
|
|
46
|
+
| `--auto` | Skips **objective** gates only. | off |
|
|
47
|
+
| free-text description | Steering for the lenses (e.g. "pagination consistency"). | — |
|
|
48
|
+
|
|
49
|
+
## Stop rules (design §11 — verbatim, non-negotiable)
|
|
50
|
+
|
|
51
|
+
1. **No edit outside `--scope`; no edit before a green baseline.**
|
|
52
|
+
2. **`cutting` / `breaking` findings are applied only after an explicit operator `yes`.**
|
|
53
|
+
3. **Tests are never removed or weakened by an `auto` fix.**
|
|
54
|
+
4. **Every finding carries `file:line` evidence inside scope.**
|
|
55
|
+
5. **The command file contains no orchestration logic** — this skill owns the orchestration, the
|
|
56
|
+
command stays a thin wrapper.
|
|
57
|
+
|
|
58
|
+
Violating any stop rule ends the run as a failure; do not "finish" a partial apply.
|
|
59
|
+
|
|
60
|
+
## Phase 0 — Resolve
|
|
61
|
+
|
|
62
|
+
1. Resolve `--scope` to a concrete path; list the in-scope files.
|
|
63
|
+
2. Resolve the lens set: if `--focus auto`, classify per
|
|
64
|
+
[focus-detection.md](./references/focus-detection.md) (ordered globs, first match per file,
|
|
65
|
+
union across files); otherwise use the given lens list.
|
|
66
|
+
3. **Report the lens set before any lens runs** (one line, per focus-detection.md).
|
|
67
|
+
4. Run the `--check` command for a **green baseline**. Red baseline = hard stop: write the report,
|
|
68
|
+
apply nothing, end the run.
|
|
69
|
+
|
|
70
|
+
## Phase 1 — Analyze (dispatch lenses)
|
|
71
|
+
|
|
72
|
+
For each lens in the resolved set, dispatch the taste skill and collect native findings:
|
|
73
|
+
|
|
74
|
+
```text
|
|
75
|
+
Skill(sp:taste-refactoring-tests) # if tests in lens set
|
|
76
|
+
Skill(sp:taste-refactoring-ui) # if ui in lens set
|
|
77
|
+
Skill(sp:taste-refactoring-api) # if api in lens set
|
|
78
|
+
Skill(sp:taste-refactoring-architect) # if architect in lens set
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Pass each lens the scope path and the free-text description. Read the lens's `## Spur contract`
|
|
82
|
+
section, then map its native findings into the shared schema
|
|
83
|
+
([finding-schema.md](./references/finding-schema.md)): lens-native rung is kept in `rung`,
|
|
84
|
+
severity is translated through the §5 map, and every finding carries a **preserved-behavior
|
|
85
|
+
inventory** input from the lens (endpoints / modules / test-protected behaviors / UI controls in
|
|
86
|
+
scope) before any proposal.
|
|
87
|
+
|
|
88
|
+
Classification rules when mapping (enforced by the structural check):
|
|
89
|
+
|
|
90
|
+
- `preservation`: does the proposal remove a user-visible feature/test/endpoint/control
|
|
91
|
+
(`cutting`), change a contract or observable behavior for a consumer (`breaking`), or keep
|
|
92
|
+
behavior identical (`preserving`)?
|
|
93
|
+
- `fix_eligibility`: `auto` only for mechanical, behavior-preserving, checkable changes;
|
|
94
|
+
`confirm` when an operator answer is needed; `suggest` for report-only plans.
|
|
95
|
+
- A `cutting`/`breaking` finding is never below P2 and never `auto`.
|
|
96
|
+
|
|
97
|
+
## Phase 2 — Merge
|
|
98
|
+
|
|
99
|
+
1. Dedupe by `(evidence file, line span, rung)`: identical span + rung from two lenses is one
|
|
100
|
+
finding — keep the higher severity, keep the first lens's `id` (renumber gaps afterward).
|
|
101
|
+
2. Rank P1 → P4 (then focus order tests, ui, api, architect).
|
|
102
|
+
3. Re-assert scope: every `evidence[].file` is inside `--scope`; drop or fail findings that are
|
|
103
|
+
not (stop rule 4).
|
|
104
|
+
|
|
105
|
+
## Phase 3 — Gate
|
|
106
|
+
|
|
107
|
+
| Gate | Class | `--auto` | Headless (`--agent auto\|name`) |
|
|
108
|
+
| --- | --- | --- | --- |
|
|
109
|
+
| Confirm resolved scope + lens set | objective | skipped | skipped |
|
|
110
|
+
| Confirm apply batch of `auto` findings | objective | skipped | skipped |
|
|
111
|
+
| Each `cutting` / `breaking` finding | **taste** | **pauses** | no pause: `status: deferred`, listed as SUGGEST in the report |
|
|
112
|
+
| Baseline check red | hard stop | stop, report | stop, report |
|
|
113
|
+
|
|
114
|
+
- **Objective gates** ask yes/no about facts (scope, lens set, batch contents); `--auto` answers
|
|
115
|
+
them from the resolved state.
|
|
116
|
+
- **Taste gates** — every `cutting` or `breaking` finding — **always pause for an explicit
|
|
117
|
+
operator answer** in an interactive session, regardless of `--auto`. Under a headless executor
|
|
118
|
+
there is no operator to ask: set `status: deferred` and list the finding as SUGGEST in the
|
|
119
|
+
report. A headless run never applies a cutting/breaking finding.
|
|
120
|
+
- A declined taste gate marks the finding `status: rejected` (operator "no") — never silently
|
|
121
|
+
dropped.
|
|
122
|
+
|
|
123
|
+
## Phase 4 — Apply (fix ladder)
|
|
124
|
+
|
|
125
|
+
Execute `--fix` per [fix-ladder.md](./references/fix-ladder.md): green baseline first, one finding
|
|
126
|
+
at a time, re-run `--check` after each, revert only the failed finding's own edits (reverse-apply
|
|
127
|
+
its hunks — never a file-level checkout; see fix-ladder.md) and mark
|
|
128
|
+
`reverted`. `none` skips this phase entirely (both artifacts still written).
|
|
129
|
+
|
|
130
|
+
## Phase 5 — Report
|
|
131
|
+
|
|
132
|
+
Run the structural check from [finding-schema.md](./references/finding-schema.md), then write both
|
|
133
|
+
artifacts:
|
|
134
|
+
|
|
135
|
+
1. `.spur/run/<run-id>-refactor-findings.json` — the bare findings array (validated schema).
|
|
136
|
+
2. `.spur/run/<run-id>-refactor-report.md` — containing:
|
|
137
|
+
- the resolved **lens set** (and per-file counts for `auto`),
|
|
138
|
+
- a **P1–P4 findings table** with `file:line`,
|
|
139
|
+
- a **preservation summary** (preserving / cutting / breaking counts),
|
|
140
|
+
- **applied / reverted / deferred** lists (plus rejected when taste gates declined findings).
|
|
141
|
+
|
|
142
|
+
`<run-id>` is the enclosing pipeline run id when invoked inside one, otherwise
|
|
143
|
+
`refactor-<yyyymmdd>-<hhmmss>`. `--fix none` still writes both artifacts.
|
|
144
|
+
|
|
145
|
+
## Cheaper executor — minimum reading
|
|
146
|
+
|
|
147
|
+
A reduced-context executor may run this skill with exactly these files:
|
|
148
|
+
|
|
149
|
+
1. this `SKILL.md` (phases, gates, stop rules),
|
|
150
|
+
2. `references/finding-schema.md` (fields, severity map, structural check),
|
|
151
|
+
3. `references/fix-ladder.md` (apply loop, revert rule),
|
|
152
|
+
4. `references/focus-detection.md` (globs, report-before-run),
|
|
153
|
+
5. each selected lens's `## Spur contract` section only.
|
|
154
|
+
|
|
155
|
+
Do not skip the structural check or the stop rules; everything else is compressible.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Refactor finding schema
|
|
2
|
+
|
|
3
|
+
Every finding produced by a taste lens is normalized into one shared object before the coordinator
|
|
4
|
+
merges, gates, or applies anything. The findings artifact is a **bare JSON array** of these objects
|
|
5
|
+
at `.spur/run/<run-id>-refactor-findings.json`, machine-checked by
|
|
6
|
+
[`refactor-finding.schema.json`](./refactor-finding.schema.json) (JSON Schema draft-07).
|
|
7
|
+
|
|
8
|
+
Authority: `docs/design/dev-refactor-command.md` §4 (fields) and §5 (severity map).
|
|
9
|
+
|
|
10
|
+
## Fields
|
|
11
|
+
|
|
12
|
+
| Field | Type | Values / notes |
|
|
13
|
+
| --- | --- | --- |
|
|
14
|
+
| `id` | string | `RF-<focus>-<nnn>` (e.g. `RF-api-001`) |
|
|
15
|
+
| `focus` | enum | `api` `architect` `tests` `ui` — the lens that produced the finding |
|
|
16
|
+
| `severity` | enum | `P1` `P2` `P3` `P4` (see the map below; `P0` does not exist in this schema) |
|
|
17
|
+
| `rung` | string | lens-native rung kept verbatim (`A0–A7`, `T0–T7`, api compatibility class, ui pass name) |
|
|
18
|
+
| `title` | string | one line |
|
|
19
|
+
| `evidence` | `{file, line}[]` | at least one `file:line` inside `--scope` |
|
|
20
|
+
| `preservation` | enum | `preserving` (behavior identical) · `cutting` (a user-visible feature/test/endpoint/control is removed) · `breaking` (contract or behavior changes for a consumer) |
|
|
21
|
+
| `fix_eligibility` | enum | `auto` (mechanical, behavior-preserving, checkable) · `confirm` (needs an operator answer) · `suggest` (report only) |
|
|
22
|
+
| `proposal` | string | what to change, imperative |
|
|
23
|
+
| `verify` | string | command or check that proves the fix (defaults to the `--check` command) |
|
|
24
|
+
| `status` | enum | `open` `applied` `reverted` `deferred` `rejected` |
|
|
25
|
+
|
|
26
|
+
Severity authority: `plugins/sp/agents/super-reviewer.md` (P1 blocker · P2 major · P3 minor ·
|
|
27
|
+
P4 advisory). The map below is the only place lens-native severities are translated.
|
|
28
|
+
|
|
29
|
+
## Lens-native → P1–P4 severity map
|
|
30
|
+
|
|
31
|
+
| Lens | Native scale | → P1 (blocker) | → P2 (major) | → P3 (minor) | → P4 (advisory) |
|
|
32
|
+
| --- | --- | --- | --- | --- | --- |
|
|
33
|
+
| tests | P0–P3 | P0 | P1 | P2 | P3 |
|
|
34
|
+
| ui | P0–P3 | P0 | P1 | P2 | P3 |
|
|
35
|
+
| architect | A-ladder + 7-axis scores | any axis ≤1 with a correctness/safety consequence | axis ≤2 or A5–A7 seam problems | A3–A4 | A0–A2, ADR candidates, deferred questions |
|
|
36
|
+
| api | compatibility class | `breaking` change already shipped or contract ambiguity that corrupts data | `risky` inconsistency across ≥2 endpoints | `additive` cleanups | naming/docs |
|
|
37
|
+
|
|
38
|
+
Rule (structural, enforced by the check below): a `cutting` or `breaking` finding is **never below
|
|
39
|
+
P2** and **never `fix_eligibility: auto`**.
|
|
40
|
+
|
|
41
|
+
## Structural check (no new dependency)
|
|
42
|
+
|
|
43
|
+
Run this documented `bun -e` snippet before writing the report — it asserts required keys and enum
|
|
44
|
+
membership and fails loudly on the two hard rules above (it must reject `severity: P0` and any
|
|
45
|
+
`cutting`/`breaking` finding marked `fix_eligibility: auto`):
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
bun -e '
|
|
49
|
+
const f = require("fs");
|
|
50
|
+
const FINDINGS = JSON.parse(f.readFileSync(process.argv[process.argv.length - 1], "utf8"));
|
|
51
|
+
const REQ = ["id", "focus", "severity", "rung", "title", "evidence", "preservation", "fix_eligibility", "proposal", "verify", "status"];
|
|
52
|
+
const ENUM = {
|
|
53
|
+
focus: ["api", "architect", "tests", "ui"],
|
|
54
|
+
severity: ["P1", "P2", "P3", "P4"],
|
|
55
|
+
preservation: ["preserving", "cutting", "breaking"],
|
|
56
|
+
fix_eligibility: ["auto", "confirm", "suggest"],
|
|
57
|
+
status: ["open", "applied", "reverted", "deferred", "rejected"],
|
|
58
|
+
};
|
|
59
|
+
if (!Array.isArray(FINDINGS)) throw new Error("findings artifact must be a bare JSON array");
|
|
60
|
+
for (const x of FINDINGS) {
|
|
61
|
+
const id = x.id ?? "(no id)";
|
|
62
|
+
for (const k of REQ) if (!(k in x)) throw new Error(`${id}: missing required key ${k}`);
|
|
63
|
+
for (const [k, vals] of Object.entries(ENUM)) if (!vals.includes(x[k])) throw new Error(`${id}: ${k}=${JSON.stringify(x[k])} not in ${vals.join("|")}`);
|
|
64
|
+
if (!Array.isArray(x.evidence) || x.evidence.length < 1 || !x.evidence.every((e) => e && typeof e.file === "string" && e.file.length > 0 && Number.isInteger(e.line) && e.line >= 1)) throw new Error(`${id}: evidence must be a non-empty [{file, line}] array`);
|
|
65
|
+
if ((x.preservation === "cutting" || x.preservation === "breaking") && x.severity !== "P1" && x.severity !== "P2") throw new Error(`${id}: cutting/breaking is never below P2`);
|
|
66
|
+
if ((x.preservation === "cutting" || x.preservation === "breaking") && x.fix_eligibility === "auto") throw new Error(`${id}: cutting/breaking is never fix_eligibility auto`);
|
|
67
|
+
}
|
|
68
|
+
console.log(`refactor-findings: ${FINDINGS.length} finding(s) structurally valid`);
|
|
69
|
+
' .spur/run/<run-id>-refactor-findings.json
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The coordinator runs this check after mapping and again after every status change, before the
|
|
73
|
+
report is written. A failed check is a hard stop — the artifact is not written and the run reports
|
|
74
|
+
the failure instead of applying anything.
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Fix ladder and eligibility
|
|
2
|
+
|
|
3
|
+
Authority: `docs/design/dev-refactor-command.md` §6. The ladder ranks what may be applied
|
|
4
|
+
mechanically versus what needs an operator answer. It reuses the repository severity meaning
|
|
5
|
+
(P1 blocker / P2 major — see [finding-schema.md](./finding-schema.md)) so `--fix blockers-first`
|
|
6
|
+
keeps the glossary sense.
|
|
7
|
+
|
|
8
|
+
## Rungs
|
|
9
|
+
|
|
10
|
+
| Rung | Example | `preservation` | `fix_eligibility` |
|
|
11
|
+
| --- | --- | --- | --- |
|
|
12
|
+
| Rename / move / dedupe / inline with identical behavior | A3 consolidate, T3 fixture cleanup, ui token normalization | preserving | `auto` |
|
|
13
|
+
| Add missing test, contract field, a11y attribute | T4, api additive, ui P3 | preserving | `auto` |
|
|
14
|
+
| Remove dead code with zero references | A1 direct removal proven dead | preserving | `auto` only when a reference search finds no caller; else `confirm` |
|
|
15
|
+
| Remove a test, endpoint, control, or code path with callers | T1, A1/A2 live, api removal, ui control removal | cutting | `confirm` |
|
|
16
|
+
| Change a contract or observable behavior | api breaking, A5–A7 seam moves | breaking | `confirm` (or `suggest` when multi-task) |
|
|
17
|
+
| Architectural migration plan | A6–A7, ADR candidates | — | `suggest` |
|
|
18
|
+
|
|
19
|
+
Hard rules:
|
|
20
|
+
|
|
21
|
+
- An `auto` fix **never deletes or weakens a test** (no skipped assertions, no loosened
|
|
22
|
+
expectations, no deleted cases).
|
|
23
|
+
- `cutting` and `breaking` findings are **never `auto`** and **never below P2**.
|
|
24
|
+
|
|
25
|
+
## Apply policies (`--fix`)
|
|
26
|
+
|
|
27
|
+
| Policy | Meaning |
|
|
28
|
+
| --- | --- |
|
|
29
|
+
| `none` (default) | Write both artifacts, perform no edit. |
|
|
30
|
+
| `blockers-first` | Apply P1/P2 findings with `fix_eligibility: auto`. |
|
|
31
|
+
| `all` | Apply every `auto` finding, then queue every `confirm` finding for the taste gate. |
|
|
32
|
+
|
|
33
|
+
## Apply loop (mirrors `sp:code-simplification` / `dev-simplify`: test-after-each, revert on regression)
|
|
34
|
+
|
|
35
|
+
1. **Green baseline first.** Run the `--check` command before any edit. A red baseline is a hard
|
|
36
|
+
stop: write the report, apply nothing.
|
|
37
|
+
2. **One finding at a time.** Apply a single finding (highest severity first: P1 → P4), limited to
|
|
38
|
+
the finding's evidence files.
|
|
39
|
+
3. **Re-run `--check`.**
|
|
40
|
+
- Passes → mark the finding `status: applied`, continue with the next.
|
|
41
|
+
- Fails → revert **only that finding's own edits**: reverse-apply the exact hunks the finding
|
|
42
|
+
introduced (keep the finding's diff; `git apply -R`, or re-edit the specific lines). Never
|
|
43
|
+
`git checkout -- <file>` a whole evidence file — a later finding may share that file with an
|
|
44
|
+
earlier **applied** finding, and a file-level checkout would revert that applied work too.
|
|
45
|
+
Mark the finding `status: reverted`, continue with the next. Never revert another finding's
|
|
46
|
+
work.
|
|
47
|
+
4. After the last finding: re-run the structural check on the findings artifact, then write the
|
|
48
|
+
report (see `SKILL.md` phase 5).
|
|
49
|
+
|
|
50
|
+
`confirm` findings in the `all` policy follow the taste gate: applied only after an explicit
|
|
51
|
+
operator `yes` (see the gate matrix in `SKILL.md`). A declined finding is marked `status: rejected`
|
|
52
|
+
or `status: deferred` (headless) per the operator answer — never silently dropped.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Focus auto-detection
|
|
2
|
+
|
|
3
|
+
Authority: `docs/design/dev-refactor-command.md` §8. Lens selection is **deterministic globs, not
|
|
4
|
+
model judgment**, so a misroute is auditable: re-running detection on the same tree yields the same
|
|
5
|
+
set.
|
|
6
|
+
|
|
7
|
+
## Classification
|
|
8
|
+
|
|
9
|
+
First matching table row per file; the lens set is the **union** across all files in `--scope`.
|
|
10
|
+
|
|
11
|
+
| Order | Glob | Lens |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| 1 | `**/tests/**`, `**/*.test.*`, `**/*.spec.*`, `**/__tests__/**` | tests |
|
|
14
|
+
| 2 | `apps/web/**`, `**/*.astro`, `**/*.tsx`, `**/*.jsx`, `**/*.css`, `**/*.vue` | ui |
|
|
15
|
+
| 3 | `packages/contracts/**`, `**/routes/**`, `**/openapi*`, `**/*.proto`, `**/*.graphql`, `apps/cli/src/commands/**`, `apps/server/src/**` | api |
|
|
16
|
+
| 4 | anything else (fallback) | architect |
|
|
17
|
+
|
|
18
|
+
Rules:
|
|
19
|
+
|
|
20
|
+
- Evaluate rows in order; a file matching an earlier row is never classified by a later one
|
|
21
|
+
(a `*.test.tsx` file is a **tests** file, not ui).
|
|
22
|
+
- Every file in scope resolves to exactly one lens; the scope set is the union of resolved lenses.
|
|
23
|
+
- An empty scope set (no files matched anything) collapses to the `architect` fallback for the
|
|
24
|
+
whole scope only when scope itself is non-empty; an empty scope is a resolve-phase error.
|
|
25
|
+
|
|
26
|
+
## `--focus` flag
|
|
27
|
+
|
|
28
|
+
| Value | Behavior |
|
|
29
|
+
| --- | --- |
|
|
30
|
+
| `auto` (default) | Run detection above and use its lens set. |
|
|
31
|
+
| single lens (`api` \| `architect` \| `tests` \| `ui`) | Run only that lens. |
|
|
32
|
+
| comma list (`api,tests`) | Run exactly the listed lenses, in the operator's order. |
|
|
33
|
+
|
|
34
|
+
## Report before run
|
|
35
|
+
|
|
36
|
+
The resolved lens set **must be reported before any lens runs** — one line naming the chosen
|
|
37
|
+
lenses and, for `auto`, the per-file counts that selected them, e.g.:
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
focus=auto → lenses: tests (14 files), api (3 files)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Under `--auto` this line is still written (to the report and the session output); what `--auto`
|
|
44
|
+
skips is only the *interactive* confirmation of scope and lens set, never the report.
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://spur.gobing.ai/schemas/refactor-finding.schema.json",
|
|
4
|
+
"title": "Refactor findings artifact (bare array of findings, design H13 §4)",
|
|
5
|
+
"description": "One JSON object per refactoring finding; the artifact written to .spur/run/<run-id>-refactor-findings.json is a bare array of these objects.",
|
|
6
|
+
"type": "array",
|
|
7
|
+
"items": {
|
|
8
|
+
"type": "object",
|
|
9
|
+
"additionalProperties": false,
|
|
10
|
+
"required": [
|
|
11
|
+
"id",
|
|
12
|
+
"focus",
|
|
13
|
+
"severity",
|
|
14
|
+
"rung",
|
|
15
|
+
"title",
|
|
16
|
+
"evidence",
|
|
17
|
+
"preservation",
|
|
18
|
+
"fix_eligibility",
|
|
19
|
+
"proposal",
|
|
20
|
+
"verify",
|
|
21
|
+
"status"
|
|
22
|
+
],
|
|
23
|
+
"properties": {
|
|
24
|
+
"id": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"pattern": "^RF-(api|architect|tests|ui)-[0-9]{3}$",
|
|
27
|
+
"description": "RF-<focus>-<nnn>, e.g. RF-api-001."
|
|
28
|
+
},
|
|
29
|
+
"focus": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"enum": ["api", "architect", "tests", "ui"],
|
|
32
|
+
"description": "The lens that produced the finding."
|
|
33
|
+
},
|
|
34
|
+
"severity": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"enum": ["P1", "P2", "P3", "P4"],
|
|
37
|
+
"description": "Repository-authority severity after the lens-native → P1–P4 map (finding-schema.md §5). P0 is outside the map."
|
|
38
|
+
},
|
|
39
|
+
"rung": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"description": "Lens-native rung kept verbatim: architect A0–A7, tests T0–T7, api compatibility class, ui pass name."
|
|
42
|
+
},
|
|
43
|
+
"title": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"minLength": 1,
|
|
46
|
+
"description": "One line."
|
|
47
|
+
},
|
|
48
|
+
"evidence": {
|
|
49
|
+
"type": "array",
|
|
50
|
+
"minItems": 1,
|
|
51
|
+
"items": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"additionalProperties": false,
|
|
54
|
+
"required": ["file", "line"],
|
|
55
|
+
"properties": {
|
|
56
|
+
"file": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"minLength": 1
|
|
59
|
+
},
|
|
60
|
+
"line": {
|
|
61
|
+
"type": "integer",
|
|
62
|
+
"minimum": 1
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"description": "At least one file:line inside --scope."
|
|
67
|
+
},
|
|
68
|
+
"preservation": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"enum": ["preserving", "cutting", "breaking"],
|
|
71
|
+
"description": "preserving: behavior identical; cutting: a user-visible feature/test/endpoint/control is removed; breaking: contract or behavior changes for a consumer."
|
|
72
|
+
},
|
|
73
|
+
"fix_eligibility": {
|
|
74
|
+
"type": "string",
|
|
75
|
+
"enum": ["auto", "confirm", "suggest"],
|
|
76
|
+
"description": "auto: mechanical, behavior-preserving, checkable; confirm: needs an operator answer; suggest: report only."
|
|
77
|
+
},
|
|
78
|
+
"proposal": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"minLength": 1,
|
|
81
|
+
"description": "What to change, imperative."
|
|
82
|
+
},
|
|
83
|
+
"verify": {
|
|
84
|
+
"type": "string",
|
|
85
|
+
"minLength": 1,
|
|
86
|
+
"description": "Command or check that proves the fix; defaults to the --check command."
|
|
87
|
+
},
|
|
88
|
+
"status": {
|
|
89
|
+
"type": "string",
|
|
90
|
+
"enum": ["open", "applied", "reverted", "deferred", "rejected"],
|
|
91
|
+
"description": "Lifecycle of the finding through the gate/apply loop."
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -134,14 +134,13 @@ single run-on paragraph on render, even though they look like separate items in
|
|
|
134
134
|
"requirements": "- [ ] R1. <text>\n- [ ] R2. <text>\n- [ ] R3. <text>"
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
-
`R1. <text>\nR2. <text>`
|
|
138
|
-
|
|
139
|
-
as an unreadable paragraph in Board preview. Do not rely on `check` to catch this. Keep the `Rn.`
|
|
137
|
+
`R1. <text>\nR2. <text>` and `- R1 — <text>` are the traps: `spur task check` reports
|
|
138
|
+
`L3.requirements-checkbox` and a bare run renders as one paragraph in Board preview. Keep the `Rn.`
|
|
140
139
|
(period) token inside the marker so R-numbering still resolves; see the canonical rule in
|
|
141
140
|
`sp:spur-dev` → `references/planning-workflow.md`.
|
|
142
141
|
|
|
143
142
|
Applies to the other body fields too: `plan` as an ordered list (`1. …\n2. …`), `acceptance_criteria`
|
|
144
|
-
as
|
|
143
|
+
as `- [ ] AC1 — <feature scenario title without its R-number>` bullets (see § Idea-pipeline emission), and any enumeration inside `background` or `design` as a
|
|
145
144
|
`- ` list.
|
|
146
145
|
|
|
147
146
|
### Design at create (default) vs `--skip-design`
|
|
@@ -207,20 +206,22 @@ scenarios are numbered in the **feature's** namespace. Both appear in a task's
|
|
|
207
206
|
|
|
208
207
|
The rule:
|
|
209
208
|
|
|
210
|
-
- **
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
209
|
+
- **Task AC items are numbered `AC1, AC2, …` (task-local), never `R<n>`** — `R<n>` is the
|
|
210
|
+
Requirements namespace, and two R-numberings in one file is how they get confused.
|
|
211
|
+
- **Bind a scenario to the task requirement it covers with `(req: R<n>)`** —
|
|
212
|
+
`Scenario: AC1 — <observable outcome> (req: R3)` (semicolons for several: `R1; R2`). Tasks
|
|
213
|
+
declaring `ac_numbering: task-local` get these cross-checked by `spur task check`
|
|
214
|
+
(`L3.ac-requirement-coverage`): a requirement with no scenario, or a scenario citing a requirement
|
|
215
|
+
that does not exist, is reported.
|
|
216
|
+
- **Scenarios carried from the feature (for DD-09 traceability) copy the title text after the
|
|
217
|
+
feature's `R<n> —`** — `- [ ] AC2 — <title>`. `normalizeTitle`
|
|
218
|
+
(`packages/domain/src/bdd/coverage.ts`) strips both `AC\d+` and `R\d+` before matching, so the
|
|
219
|
+
prefix is invisible to feature coverage; the feature's number is never read as a task id.
|
|
219
220
|
|
|
220
221
|
**Legacy tasks are exempt.** Most existing tasks predate this and copied feature AC wholesale,
|
|
221
222
|
carrying the feature's numbers. The coverage check is opt-in precisely so they emit nothing —
|
|
222
|
-
absent `ac_numbering`, only DD-09 applies.
|
|
223
|
-
break traceability. New tasks get `ac_numbering: task-local` from the templates automatically;
|
|
223
|
+
absent `ac_numbering`, only DD-09 applies. Legacy `Scenario: R<n> —` titles still bind by prefix, so
|
|
224
|
+
opting an old task in cannot break traceability. New tasks get `ac_numbering: task-local` from the templates automatically;
|
|
224
225
|
`spur task update <wbs> --ac-numbering task-local` opts in an existing one.
|
|
225
226
|
|
|
226
227
|
Edge-case scenarios may map to tasks, merge into a sibling, or be deferred. Record deferrals
|
|
@@ -523,7 +524,7 @@ The payload is a top-level JSON **array** (no `tasks` wrapper):
|
|
|
523
524
|
"requirements": "- [ ] R1. Accept a title and an optional description on POST /tasks.\n- [ ] R2. Reject an empty title with a 400 and a reason.\n- [ ] R3. Allocate the task file through the CLI-gated write path.",
|
|
524
525
|
"design": "Approach: POST /tasks via existing TaskService.create.\nRejected: ad-hoc SQL in handler.\nInvariants: CLI-gated corpus writes only.",
|
|
525
526
|
"plan": "1. Contract\n2. Handler\n3. Tests",
|
|
526
|
-
"acceptance_criteria": "
|
|
527
|
+
"acceptance_criteria": "- [ ] AC1 — User can create a task with required fields"
|
|
527
528
|
},
|
|
528
529
|
{
|
|
529
530
|
"name": "Implement task listing endpoint",
|
|
@@ -559,6 +560,12 @@ fields and normal default planning fills them from your analysis; the per-task r
|
|
|
559
560
|
batch-create still deepens them when a task needs more detail. Validate locally against the
|
|
560
561
|
schema before emitting.
|
|
561
562
|
|
|
563
|
+
**Pass the deterministic task check.** Each `acceptance_criteria` bullet must be an exact feature
|
|
564
|
+
scenario title (L4.uncovered-task-scenario); add task-local checks as prose after the bullets, not
|
|
565
|
+
as extra bullets. No section body may use `HITL`, `approval`/`approved`, `merged`/`merge event`,
|
|
566
|
+
`content-gate`, `GATED`, or `capstone` as standalone words (L4.gate-language) — say "operator
|
|
567
|
+
answer" / "accepted" instead, and keep enum values out of that list too.
|
|
568
|
+
|
|
562
569
|
**The order sidecar.** Also emit the private task-order sidecar at
|
|
563
570
|
`.spur/run/<runId>-idea-task-order.json`: a JSON array (one entry per batch item) of
|
|
564
571
|
`{ name: <exact batch item name>, depends_on_names: [<batch item names>] }` declaring
|
|
@@ -47,8 +47,10 @@ Every scenario/item carries an `R1, R2, …` prefix:
|
|
|
47
47
|
|
|
48
48
|
- **Sequential within a feature**, starting at R1.
|
|
49
49
|
- **Stable forever.** Never renumber once tasks exist — tasks match AC by **normalized scenario
|
|
50
|
-
title** (the `R<n> —` prefix is stripped on comparison
|
|
51
|
-
but *rewording* a title breaks the coverage edge.
|
|
50
|
+
title** (the `R<n> —` prefix is stripped on comparison, as is the task side's `AC<n> —`), so
|
|
51
|
+
renumbering around a title is safe but *rewording* a title breaks the coverage edge.
|
|
52
|
+
- **`R<n>` is the feature's namespace.** Task AC items are numbered `AC<n>` (task-local) and copy
|
|
53
|
+
the feature title after their own prefix; task Requirements own `R<n>.` inside the task.
|
|
52
54
|
- **One R-number = one scenario.** Don't split one requirement across scenarios under a single
|
|
53
55
|
R-number; don't merge two requirements into one scenario.
|
|
54
56
|
|
|
@@ -58,6 +58,9 @@ spur feature create "Planning layer" --parent H # → H<n>
|
|
|
58
58
|
spur feature create "Task CLI" --parent H1 # → H1<n>
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
+
`create --json` returns `{ ref: { kind, id, filePath, folder }, content }` — read the id from
|
|
62
|
+
`.ref.id`, not a top-level `.id`.
|
|
63
|
+
|
|
61
64
|
To restructure, use `move` — never hand-edit an ID. `move <id> --parent <new>` re-parents the
|
|
62
65
|
subtree and **cascade-renames** every descendant; omit `--parent` to lift it to a top-level group.
|
|
63
66
|
|
|
@@ -184,7 +187,9 @@ spur feature check --strict --json # warnings → failures
|
|
|
184
187
|
```
|
|
185
188
|
|
|
186
189
|
The 4-layer validator (frontmatter, AC syntax, children-limit/structure, L4 traceability) emits its
|
|
187
|
-
verdict and findings as JSON
|
|
190
|
+
verdict and findings as a JSON **array**, one entry per feature (`jq '.[0].pass'`, `.[0].findings[].code`),
|
|
191
|
+
like `spur task check --json`. Gherkin AC must keep its `Feature:` line (`L3.ac-bdd-error`) and
|
|
192
|
+
every `Scenario:` title is the identity key tasks reference verbatim. **Query this, don't re-derive it** — the rules live in the CLI, never
|
|
188
193
|
restated as prose here. This is what `sp:spur-dev`'s feature-check gate loop runs.
|
|
189
194
|
|
|
190
195
|
## Status sync - `sync`
|
|
@@ -137,8 +137,8 @@ runner (see [validation-and-extension.md](validation-and-extension.md)).
|
|
|
137
137
|
Order matters for both guards and conditions: **the first that passes wins.** Put the discriminating
|
|
138
138
|
guard before the unconditional fallback (`always` / no-guard edge). For multi-condition gates (doctor
|
|
139
139
|
+ task check, quality gate + attempt cap), prefer a **soft probe** shell that writes PASS|FAIL and
|
|
140
|
-
always exits 0, then branch with ordered status-file guards — see shipped
|
|
141
|
-
`task-pipeline.yaml` (more reliable than `action-ok` alone when more than one condition decides the edge).
|
|
140
|
+
always exits 0, then branch with ordered status-file guards — see shipped
|
|
141
|
+
`task-pipeline.yaml` / `wrapup-pipeline.yaml` (more reliable than `action-ok` alone when more than one condition decides the edge).
|
|
142
142
|
|
|
143
143
|
## Template variables
|
|
144
144
|
|
|
@@ -28,9 +28,9 @@ Authored workflows default to a project-local directory, grouped by purpose:
|
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
A `--file <path>` argument overrides the default. Keep one workflow per file, named for what it does
|
|
31
|
-
(`approval.yaml`, `import-file.yaml`), not for its mode.
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
(`approval.yaml`, `import-file.yaml`), not for its mode. Copy real schema shapes from a
|
|
32
|
+
retained definition such as `task-pipeline.yaml` rather than from a half-remembered
|
|
33
|
+
snippet.
|
|
34
34
|
|
|
35
35
|
## Sub-procedure: mode-selection gate
|
|
36
36
|
|
|
@@ -95,7 +95,7 @@ and orders capabilities, it does not contain them** (ADR-069).
|
|
|
95
95
|
between them are one judgment step.
|
|
96
96
|
- [ ] **Soft status-file probe over repeated probing.** Run the expensive check once in an action
|
|
97
97
|
that always exits 0 and writes its verdict to a run-scoped file; branch with ordered cheap
|
|
98
|
-
guards that read that file. One subprocess instead of one per branch — the
|
|
98
|
+
guards that read that file. One subprocess instead of one per branch — the
|
|
99
99
|
`task-pipeline.yaml` quality-gate idiom.
|
|
100
100
|
- [ ] **Order guards cheapest-discriminating-first.** The first passing guard wins, so a `test -f`
|
|
101
101
|
ahead of a `spur … --json` parse skips the expensive call on the common path.
|
|
@@ -62,7 +62,7 @@ ordered guards, and may loop back on itself (e.g. implement → check → fix
|
|
|
62
62
|
| Node typing | none (states are untyped) | `type: action\|gate\|parallel\|decision` |
|
|
63
63
|
| Required keys | `name, initialState, states, transitions` | `kind, name, initialNode, nodes, edges` |
|
|
64
64
|
| `kind` field | optional (defaults to state-machine) | **required** — `kind: transition-flow` |
|
|
65
|
-
| Canonical example | implement→check→fix loop (`
|
|
65
|
+
| Canonical example | implement→check→fix loop (`task-pipeline.yaml` — quality-gate hop with bounded fixall) | read→validate→transform→write pipeline |
|
|
66
66
|
|
|
67
67
|
**Heuristic:** loops / retries / one-active-state → **state-machine**; pipeline / fan-out / action-per-node → **transition-flow**.
|
|
68
68
|
|
|
@@ -95,7 +95,7 @@ Use this skill to:
|
|
|
95
95
|
|
|
96
96
|
The skill's logic divides by **whether the LLM adds value**:
|
|
97
97
|
|
|
98
|
-
- **Direct CLI** (`validate`, `run`, `list`, `trace`, `continue`, `cancel`, `clean`) — deterministic,
|
|
98
|
+
- **Direct CLI** (`validate`, `run`, `list`, `trace`, `progress`, `continue`, `cancel`, `clean`) — deterministic,
|
|
99
99
|
single-verb commands. Run them straight. A slash-command wrapper would only forward flags and add
|
|
100
100
|
drift; **there is no command for these — use the CLI**. The skill still drives them in natural
|
|
101
101
|
language (interpreting a failed validate, reading a run trace).
|
|
@@ -113,6 +113,7 @@ The skill's logic divides by **whether the LLM adds value**:
|
|
|
113
113
|
| `clean` | `spur workflow clean` (CLI) | `[--older-than <min>] [--force] [--logs] [--dry-run]` | Bulk-finalize stale `running`/`pending` runs as failed **and** reclaim retained run logs older than `workflow.logRetentionDays` (30d default) |
|
|
114
114
|
| `list` | `spur workflow list` (CLI) | — | Available workflow **YAML definition files** (not run records) |
|
|
115
115
|
| `trace` | `spur workflow trace` (CLI) | `[run-id] [--workflow <n>] [--status <s>] [--since <iso>] [--last <n>] [--follow] [--poll <ms>] [--output]` | Run history list or per-run timeline |
|
|
116
|
+
| `progress` | `spur workflow progress` (CLI) | `<run-id>` | The `projectWorkflowProgress` projection for that run — current state, per-action attempts, candidate next transitions, diagnostics. Read-only; the verb renders, `packages/app` derives. Unknown run id exits 1 with `Run <id> not found.` |
|
|
116
117
|
| `add` | agent procedure | `"<nl-description>" [--kind <state-machine\|transition-flow>] [--file <path>]` | **Mode chosen (confirmed)** → first reconciled against existing workflows (extend an existing flow rather than duplicate) → YAML authored in real schema shape → **validated AND dry-run** (reaches the expected terminal state) → [add](workflows/operations.md#add) |
|
|
117
118
|
| `refine` | agent procedure | `<workflow-file> [--intent "<goal>"] [--dry-run]` | Smallest change meeting the intent, re-validated and re-dry-run; `--dry-run` emits a diff only → [refine](workflows/operations.md#refine) |
|
|
118
119
|
|
|
@@ -262,6 +263,7 @@ spur workflow cancel <run-id> [--json]
|
|
|
262
263
|
spur workflow clean [--older-than <minutes>] [--force] [--logs] [--dry-run] [--json]
|
|
263
264
|
spur workflow list [--json]
|
|
264
265
|
spur workflow trace [run-id] [--workflow <name>] [--status <s>] [--since <iso>] [--last <n>] [--follow] [--poll <ms>] [--output] [--json]
|
|
266
|
+
spur workflow progress <run-id> [--json]
|
|
265
267
|
```
|
|
266
268
|
|
|
267
269
|
### `show` - project a definition
|
|
@@ -359,7 +361,7 @@ the workflow's actions (`shell`, custom runners) do that; this skill builds and
|
|
|
359
361
|
(transition-flow) wins. Order the specific case before the fallback. Simple shell checks can use
|
|
360
362
|
`action-ok` before an unconditional retry edge; multi-condition gates prefer a **soft status-file
|
|
361
363
|
probe** (always exit 0) with ordered shell guards for PASS / FAIL / exhausted (see
|
|
362
|
-
`
|
|
364
|
+
`task-pipeline.yaml`'s quality-gate hop).
|
|
363
365
|
4. **`env.allow` is an allowlist.** `${env.X}` resolves only if `X` is listed under `env.allow`;
|
|
364
366
|
otherwise it resolves empty. A workflow that "loses" an environment value usually forgot to allow it.
|
|
365
367
|
5. **Extensions are fail-closed.** The CLI loads YAML-declared extension modules itself
|
|
@@ -400,10 +402,9 @@ the workflow's actions (`shell`, custom runners) do that; this skill builds and
|
|
|
400
402
|
custom action/guard runners, the trust-gated extension loader, and CLI-vs-library capability gaps.
|
|
401
403
|
- `@gobing-ai/ts-dual-workflow-engine` README — authoritative library reference (both drivers,
|
|
402
404
|
RunLifecycle, persistence, the full event map, every built-in capability).
|
|
403
|
-
- `
|
|
404
|
-
(status-file branching
|
|
405
|
-
|
|
406
|
-
verify, record) when you need the complete reliability pattern set.
|
|
405
|
+
- `task-pipeline.yaml` — the canonical state-machine implement→soft-check→fixall loop and
|
|
406
|
+
full production pipeline (status-file branching, `qualityGateMaxFixAttempts`, quality gate,
|
|
407
|
+
HITL, verify, record); copy real shapes from here.
|
|
407
408
|
|
|
408
409
|
## Platform Notes
|
|
409
410
|
|