@gobing-ai/spur 0.3.73 → 0.3.75
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/config.example.yaml +15 -4
- package/config/pipeline-budgets.json +4 -4
- package/config/rules/strict/runtime-boundaries.yaml +1 -1
- package/config/templates/AGENTS.md +5 -0
- package/config/templates/docs/99_PROJECT_CONSTITUTION.md +2 -0
- package/config/workflows/basic.yaml +5 -2
- package/config/workflows/docs-pipeline.yaml +117 -55
- package/config/workflows/feature-dev.yaml +185 -121
- package/config/workflows/feature-lifecycle.yaml +1 -0
- package/config/workflows/history-anatomy.yaml +8 -4
- package/config/workflows/idea-pipeline.yaml +123 -43
- package/config/workflows/pr-review.yaml +31 -8
- package/config/workflows/task-lifecycle.yaml +1 -0
- package/config/workflows/task-pipeline.yaml +965 -905
- package/config/workflows/wayfinder-resolution.yaml +124 -90
- package/config/workflows/wrapup-pipeline.yaml +304 -107
- package/package.json +1 -1
- package/plugins/sp/agents/expert-spur.md +7 -4
- package/plugins/sp/commands/dev-idea.md +1 -1
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/history-anatomy-cache.mjs +7 -1
- package/plugins/sp/scripts/history-anatomy-cache.ts +11 -2
- package/plugins/sp/scripts/pr-reviewing.mjs +5 -1
- package/plugins/sp/scripts/pr-reviewing.ts +7 -1
- package/plugins/sp/scripts/transition-shim-check.ts +6 -6
- package/plugins/sp/skills/pr-reviewing/SKILL.md +4 -1
- package/plugins/sp/skills/spur-cli/references/self.md +17 -3
- package/plugins/sp/skills/spur-cli/references/tasks.md +4 -2
- package/plugins/sp/skills/spur-cli/references/workflows/workflow-fit-and-tuning.md +5 -5
- package/plugins/sp/skills/spur-cli/references/workflows.md +4 -4
- package/plugins/sp/skills/spur-dev/SKILL.md +17 -0
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +23 -8
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +1 -1
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +3 -3
- package/plugins/sp/skills/spur-dev/references/gate-checklists.md +5 -3
- package/plugins/sp/skills/wayfinder/SKILL.md +1 -1
- package/spur.js +2999 -1010
- package/web/_astro/{BoardApp.BYCNkMOn.js → BoardApp.BOW1815F.js} +101 -100
- package/web/_astro/BoardApp.C4zimv1U.js +1 -0
- package/web/_astro/{TaskDetail.CgUreSP2.js → TaskDetail.CX6C7dSJ.js} +1 -1
- package/web/_astro/{arc.BySSh34M.js → arc.CwSvH1ji.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.DM46TS_h.js → architectureDiagram-3BPJPVTR.Dxne1RvP.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.tZhvNUHA.js → blockDiagram-GPEHLZMM.D9Fb14rC.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.PT4Or4Nf.js → c4Diagram-AAUBKEIU.DmvWSFvv.js} +1 -1
- package/web/_astro/channel.CQHdDVp9.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.J9r0_Bbe.js → chunk-2J33WTMH.BCCktIrE.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.hzyeIvhR.js → chunk-4BX2VUAB.BzCk9q27.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.B0rO7qVh.js → chunk-55IACEB6.CZXVgTk4.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.wE_Uk5D4.js → chunk-727SXJPM.CJL8UIpX.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.DqEEjQw7.js → chunk-AQP2D5EJ.Dz8MDEdw.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.CDoD9sBX.js → chunk-FMBD7UC4.CkQeYYUw.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.CtX5nF9P.js → chunk-ND2GUHAM.KiC1QgzH.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.CK_EwfaT.js → chunk-QZHKN3VN.AJ08mw2e.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.DLt5a8Lh.js → classDiagram-4FO5ZUOK.DRWRDKzK.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.DLt5a8Lh.js → classDiagram-v2-Q7XG4LA2.DRWRDKzK.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.CMCWP49h.js → cose-bilkent-S5V4N54A.CavydfLP.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.HyXw_vdS.js → cynefin-OW5HDTMX.D_7o_a0B.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.BTuAzh01.js → dagre-BM42HDAG.LWk2dKgg.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.D9dr9wfT.js → diagram-2AECGRRQ.82lbq6aB.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.C4Rot0hj.js → diagram-5GNKFQAL.hyNL1pwY.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.B_TK5uWC.js → diagram-KO2AKTUF.BaLqVf-b.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.JkXKK7CO.js → diagram-LMA3HP47.DQJxj0oz.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.BzMN8Bd6.js → diagram-OG6HWLK6.DhlL8cG2.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.DVZaWGUd.js → erDiagram-TEJ5UH35.HoTnXwkF.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.rjEiWUfR.js → flowDiagram-I6XJVG4X.DkFKlZIZ.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.C_EgAarK.js → ganttDiagram-6RSMTGT7.9qPfOTDb.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.B-QQSDsK.js → gitGraphDiagram-PVQCEYII.B4G18Dwc.js} +1 -1
- package/web/_astro/index.Dcr_8fiK.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.DlWesz7T.js → infoDiagram-5YYISTIA.e3KJkXAM.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.BUMZOawi.js → ishikawaDiagram-YF4QCWOH.CY3yddhD.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.CWfkxfjY.js → journeyDiagram-JHISSGLW.DaAKIO1t.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.B-YpMwXf.js → kanban-definition-UN3LZRKU.DGkDddtc.js} +1 -1
- package/web/_astro/{linear.D7uqzENp.js → linear.DnDPcpd1.js} +1 -1
- package/web/_astro/{mermaid.core.CxrNppBD.js → mermaid.core.1uBmxa9t.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.B4Qe7cM2.js → mindmap-definition-RKZ34NQL.XFJVayxw.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.Ds-5j2ro.js → pieDiagram-4H26LBE5.BXm2OgHh.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.tBd38uNC.js → quadrantDiagram-W4KKPZXB.DgB2p5fc.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.sFENkWl3.js → requirementDiagram-4Y6WPE33.jBR39bnS.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.BeB-Hk7C.js → sankeyDiagram-5OEKKPKP.N3u9OhE3.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.DnTeaSpx.js → sequenceDiagram-3UESZ5HK.CZhqZXNL.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.B-8Jt5EJ.js → stateDiagram-AJRCARHV.DlNu1VEa.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.Br7xoqMW.js → stateDiagram-v2-BHNVJYJU.BEqi8NQV.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.C-3WdOyi.js → timeline-definition-PNZ67QCA.BPcexclc.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.DCIs7Lc6.js → vennDiagram-CIIHVFJN.BZalxKGQ.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.rGAL-bbz.js → wardleyDiagram-YWT4CUSO.BqOljod9.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.hkfQKiRl.js → xychartDiagram-2RQKCTM6.tkO4ppN5.js} +1 -1
- package/web/index.html +2 -2
- package/config/corpus-baseline.json +0 -1805
- package/config/workflow-composition-baseline.json +0 -615
- package/web/_astro/BoardApp.E12MFjOS.js +0 -1
- package/web/_astro/channel.5cYKr5cs.js +0 -1
- package/web/_astro/index.B5MTfe7k.css +0 -1
|
@@ -1,36 +1,57 @@
|
|
|
1
|
-
# Feature development loop —
|
|
1
|
+
# Feature development loop — existing-feature reuse: precheck → execute-tasks →
|
|
2
|
+
# feature-verify → integration-review → done.
|
|
3
|
+
#
|
|
4
|
+
# 0782 (feature D6): the caller supplies a featureId whose AC are already accepted and whose
|
|
5
|
+
# task roster already exists. This definition REUSES them — the brainstorm/plan states and
|
|
6
|
+
# their edges are deleted, so no model hop ever re-plans or re-decomposes an existing feature
|
|
7
|
+
# (explicit planning stays at /sp:dev-plan and /sp:dev-idea, outside this consumer).
|
|
2
8
|
#
|
|
3
9
|
# Reliability (aligned with task-pipeline / ADR-043):
|
|
4
|
-
# - Soft precheck (
|
|
10
|
+
# - Soft precheck (feature identity + roster structural contract) → failed via transitions,
|
|
11
|
+
# never a raw lifecycle abort. The doctor probe is gone: the precheck now performs the
|
|
12
|
+
# essential feature/roster validation itself; task execution owns executor availability.
|
|
5
13
|
# - Every agent.run input is a pure slash command (no free-form pipeline essays)
|
|
6
14
|
# - featureId must be supplied by the caller (workflow vars are not agent-mutated)
|
|
7
|
-
# - feature-verify
|
|
15
|
+
# - feature-verify runs `feature check --as done --json` EXACTLY ONCE (0782 R3): the shell
|
|
16
|
+
# captures the exit code and JSON result, writes the decision to a .status sibling, and
|
|
17
|
+
# the guards only read that status — no sibling guard re-runs the check.
|
|
18
|
+
# - A nonempty all-terminal roster routes straight to feature-verify with zero execution
|
|
19
|
+
# model calls (0782 R2).
|
|
8
20
|
|
|
9
21
|
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
10
22
|
kind: state-machine
|
|
11
23
|
name: feature-dev
|
|
24
|
+
version: "3"
|
|
12
25
|
description: >
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
26
|
+
Existing-feature execution loop: precheck → execute-tasks → feature-verify → done.
|
|
27
|
+
Drives a WHOLE feature whose accepted AC and task roster already exist — the caller
|
|
28
|
+
supplies featureId, this loop reuses the roster and runs it to verified completion.
|
|
29
|
+
For planning a NEW feature use /sp:dev-plan / /sp:dev-idea; for one deliverable use
|
|
30
|
+
task-pipeline.yaml directly.
|
|
17
31
|
|
|
18
|
-
The engine has no native for-each over a dynamic task list, so
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
32
|
+
The engine has no native for-each over a dynamic task list, so execution DELEGATES the
|
|
33
|
+
fan-out: the precheck resolves the linked roster via `spur feature show` + `spur task
|
|
34
|
+
list --feature` (captured once under .spur/run/<runId>-feature-dev-{feature,roster}.json),
|
|
35
|
+
validates the essential structural contract (identity, roster shape, duplicate/mismatched
|
|
36
|
+
identities, refinement-blocking statuses), and freezes the sorted todo WBS list into
|
|
37
|
+
.spur/run/<runId>-feature-dev-tasks.txt. The execute-tasks step reads that frozen list
|
|
38
|
+
into a var (file.read.into-var) and dispatches ONE pure-slash /sp:dev-runall --tasks run
|
|
39
|
+
with it — the child never re-enumerates membership (0782 R2).
|
|
23
40
|
|
|
24
|
-
Shape: precheck →
|
|
25
|
-
|
|
41
|
+
Shape: precheck → execute-tasks[-auto](delegated frozen batch) → feature-verify →
|
|
42
|
+
integration-review → done
|
|
43
|
+
(precheck failure, a non-PASS completion check, or a blocking review short-circuits
|
|
44
|
+
to `failed`; a nonempty all-terminal roster skips execution entirely).
|
|
26
45
|
|
|
27
46
|
Vars (passed as a JSON object via `--vars`):
|
|
28
|
-
featureId — the feature ID this loop drives (required), e.g. `--vars '{"featureId":"B3"}'`
|
|
47
|
+
featureId — the existing feature ID this loop drives (required), e.g. `--vars '{"featureId":"B3"}'`
|
|
29
48
|
agent — agent the agent.run steps invoke; override `--vars '{"agent":"claude"}'`
|
|
30
|
-
profile — `auto` selects execute-tasks-auto (`/sp:dev-runall … --auto`); otherwise
|
|
49
|
+
profile — `auto` selects execute-tasks-auto (`/sp:dev-runall --tasks … --auto`); otherwise
|
|
31
50
|
interactive execute-tasks (no --auto, per-task HITL remains)
|
|
51
|
+
featureTaskIds — frozen todo WBS list projected from the roster file at run time; declared
|
|
52
|
+
empty so template resolution cannot throw before the read lands (0782 design)
|
|
32
53
|
spurBin — PATH-independent spur invocation (CLI overrides at run start; literal fallback)
|
|
33
|
-
stepTimeoutMs — agent.run budget (ms) for
|
|
54
|
+
stepTimeoutMs — agent.run budget (ms) for the runall dispatch
|
|
34
55
|
|
|
35
56
|
Reliability (ADR-043): every agent.run input is a pure slash command. featureId must be
|
|
36
57
|
supplied by the caller (workflow vars cannot be mutated by the agent). profile is wired
|
|
@@ -46,6 +67,7 @@ vars:
|
|
|
46
67
|
featureId: ""
|
|
47
68
|
agent: "auto"
|
|
48
69
|
profile: "standard"
|
|
70
|
+
featureTaskIds: ""
|
|
49
71
|
spurBin: "spur"
|
|
50
72
|
stepTimeoutMs: "1800000"
|
|
51
73
|
# Integration review (D5-P, feature R11). Advisory by default: a pending, timed-out, or
|
|
@@ -59,102 +81,135 @@ vars:
|
|
|
59
81
|
states:
|
|
60
82
|
- id: precheck
|
|
61
83
|
description: >
|
|
62
|
-
Soft precheck:
|
|
63
|
-
|
|
84
|
+
Soft precheck (0782 R1): resolve the supplied feature and its linked task roster via
|
|
85
|
+
existing CLI reads, then validate the essential structural contract — non-empty
|
|
86
|
+
featureId/runId, a feature-show JSON whose identity matches, a nonempty array roster
|
|
87
|
+
with unique non-empty WBS identities and known statuses, no backlog/wip/testing/blocked
|
|
88
|
+
member (planning/refine/resume handoff), and at least one linked task. Rejects fail
|
|
89
|
+
closed with an actionable handoff BEFORE any model dispatch; nothing is auto-created
|
|
90
|
+
or re-planned. Always exit 0 so failures route to `failed` via transitions.
|
|
64
91
|
onEnter:
|
|
65
92
|
- kind: shell
|
|
66
93
|
options:
|
|
67
94
|
command: >-
|
|
68
|
-
mkdir -p .spur/run
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
95
|
+
mkdir -p .spur/run;
|
|
96
|
+
FEATURE_JSON=".spur/run/$__runId-feature-dev-feature.json";
|
|
97
|
+
ROSTER_JSON=".spur/run/$__runId-feature-dev-roster.json";
|
|
98
|
+
TASKS_TXT=".spur/run/$__runId-feature-dev-tasks.txt";
|
|
99
|
+
PRECHECK_STATUS=".spur/run/$__runId-feature-dev-precheck.status";
|
|
100
|
+
rm -f "$PRECHECK_STATUS";
|
|
101
|
+
set +e;
|
|
102
|
+
test -n "$featureId" && test -n "$__runId";
|
|
103
|
+
id_rc=$?;
|
|
104
|
+
if [ "$id_rc" -eq 0 ]; then $spurBin feature show "$featureId" --json > "$FEATURE_JSON" 2>&1; show_rc=$?; else show_rc=1; fi;
|
|
105
|
+
if [ "$id_rc" -eq 0 ] && [ "$show_rc" -eq 0 ] && jq -e --arg id "$featureId" 'type == "object" and .id == $id' "$FEATURE_JSON" > /dev/null 2>&1; then $spurBin task list --feature "$featureId" --json > "$ROSTER_JSON" 2>&1; list_rc=$?; else list_rc=1; fi;
|
|
106
|
+
if [ "$id_rc" -ne 0 ] || [ "$show_rc" -ne 0 ] || [ "$list_rc" -ne 0 ]; then
|
|
107
|
+
echo "feature-dev precheck: missing featureId/runId, unknown feature '$featureId', or unreadable roster (rc $id_rc/$show_rc/$list_rc) — supply an existing planned feature via /sp:dev-plan or /sp:dev-idea; nothing was auto-created or re-planned" >&2;
|
|
108
|
+
printf 'FAIL\n' > "$PRECHECK_STATUS";
|
|
109
|
+
elif ! jq -e 'type == "array" and length > 0' "$ROSTER_JSON" > /dev/null 2>&1; then
|
|
110
|
+
echo "feature-dev precheck: roster at $ROSTER_JSON is malformed, not an array, or empty — plan the feature first via /sp:dev-plan; refusing to replan or run an empty batch" >&2;
|
|
111
|
+
printf 'FAIL\n' > "$PRECHECK_STATUS";
|
|
112
|
+
elif ! jq -e 'all(.[]; (.wbs | type == "string" and length > 0)) and ([.[].wbs] | length == (unique | length)) and all(.[]; .status == "todo" or .status == "done" or .status == "cancelled" or .status == "backlog" or .status == "wip" or .status == "testing" or .status == "blocked")' "$ROSTER_JSON" > /dev/null 2>&1; then
|
|
113
|
+
echo "feature-dev precheck: roster has empty/duplicate/mismatched WBS identities or unknown statuses at $ROSTER_JSON — repair the task corpus; refusing to batch a broken roster" >&2;
|
|
114
|
+
printf 'FAIL\n' > "$PRECHECK_STATUS";
|
|
115
|
+
elif [ "$(jq '[.[] | select(.status == "backlog" or .status == "wip" or .status == "testing" or .status == "blocked")] | length' "$ROSTER_JSON")" -gt 0 ]; then
|
|
116
|
+
echo "feature-dev precheck: linked task(s) are backlog/wip/testing/blocked — refine or resume them through their own task pipelines before batching; refusing to launch overlapping work" >&2;
|
|
117
|
+
printf 'FAIL\n' > "$PRECHECK_STATUS";
|
|
75
118
|
else
|
|
76
|
-
printf '
|
|
77
|
-
|
|
119
|
+
printf '%s' "$(jq -r '[.[] | select(.status == "todo") | .wbs] | sort | join(",")' "$ROSTER_JSON")" > "$TASKS_TXT";
|
|
120
|
+
printf 'PASS\n' > "$PRECHECK_STATUS";
|
|
121
|
+
fi;
|
|
78
122
|
exit 0
|
|
79
123
|
- kind: note
|
|
80
124
|
options:
|
|
81
|
-
message: "Feature-dev start for feature ${vars.featureId}."
|
|
82
|
-
|
|
83
|
-
- id: brainstorm
|
|
84
|
-
description: >
|
|
85
|
-
Validate / expand the feature with BDD AC via pure slash /sp:dev-brainstorm.
|
|
86
|
-
Caller must pass vars.featureId (required for plan/runall).
|
|
87
|
-
onEnter:
|
|
88
|
-
- kind: agent.run
|
|
89
|
-
options:
|
|
90
|
-
agent: ${vars.agent}
|
|
91
|
-
input: /sp:dev-brainstorm --feature ${vars.featureId}
|
|
92
|
-
# Declared Layer-1 role (0538 R2): routing reason beside the agent: pin.
|
|
93
|
-
role: planner
|
|
94
|
-
timeoutMs: ${vars.stepTimeoutMs}
|
|
95
|
-
|
|
96
|
-
- id: plan
|
|
97
|
-
description: >
|
|
98
|
-
Decompose the validated feature into a CLI-validated task batch via pure slash
|
|
99
|
-
/sp:dev-plan. The batch-create gate rejects a malformed decomposition.
|
|
100
|
-
onEnter:
|
|
101
|
-
- kind: agent.run
|
|
102
|
-
options:
|
|
103
|
-
agent: ${vars.agent}
|
|
104
|
-
input: /sp:dev-plan --feature ${vars.featureId}
|
|
105
|
-
# Declared Layer-1 role (0538 R2): routing reason beside the agent: pin.
|
|
106
|
-
role: planner
|
|
107
|
-
timeoutMs: ${vars.stepTimeoutMs}
|
|
125
|
+
message: "Feature-dev start for existing feature ${vars.featureId} (0782 reuse path): roster resolved under .spur/run/${vars.__runId}-feature-dev-roster.json; frozen todo list at .spur/run/${vars.__runId}-feature-dev-tasks.txt."
|
|
108
126
|
|
|
109
127
|
# Two pure-slash execute hops so vars.profile actually controls --auto (ADR-043).
|
|
110
128
|
# Engine templates cannot branch a single input string on profile; declaration-order
|
|
111
|
-
# transitions pick the correct hop (auto first, then interactive fallback).
|
|
129
|
+
# transitions pick the correct hop (auto first, then interactive fallback). Each hop first
|
|
130
|
+
# reads the FROZEN todo list produced by precheck into featureTaskIds, then dispatches the
|
|
131
|
+
# explicit list — the runall child never re-enumerates membership (0782 R2).
|
|
112
132
|
- id: execute-tasks-auto
|
|
113
133
|
description: >
|
|
114
|
-
profile=auto —
|
|
115
|
-
(skips per-task HITL inside task-pipeline).
|
|
134
|
+
profile=auto — dispatch the frozen todo WBS list via pure slash
|
|
135
|
+
/sp:dev-runall --tasks <list> --auto (skips per-task HITL inside task-pipeline).
|
|
116
136
|
onEnter:
|
|
137
|
+
- kind: file.read.into-var
|
|
138
|
+
options:
|
|
139
|
+
path: .spur/run/${vars.__runId}-feature-dev-tasks.txt
|
|
140
|
+
var: featureTaskIds
|
|
141
|
+
trim: true
|
|
117
142
|
- kind: agent.run
|
|
118
143
|
options:
|
|
119
144
|
agent: ${vars.agent}
|
|
120
|
-
input: /sp:dev-runall --
|
|
145
|
+
input: /sp:dev-runall --tasks ${vars.featureTaskIds} --auto
|
|
121
146
|
# Declared Layer-1 role (0538 R2): routing reason beside the agent: pin.
|
|
122
147
|
role: planner
|
|
123
148
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
124
149
|
|
|
125
150
|
- id: execute-tasks
|
|
126
151
|
description: >
|
|
127
|
-
Interactive —
|
|
128
|
-
--auto (per-task HITL approve gates remain
|
|
152
|
+
Interactive — dispatch the frozen todo WBS list via pure slash
|
|
153
|
+
/sp:dev-runall --tasks <list> without --auto (per-task HITL approve gates remain
|
|
154
|
+
available).
|
|
129
155
|
onEnter:
|
|
156
|
+
- kind: file.read.into-var
|
|
157
|
+
options:
|
|
158
|
+
path: .spur/run/${vars.__runId}-feature-dev-tasks.txt
|
|
159
|
+
var: featureTaskIds
|
|
160
|
+
trim: true
|
|
130
161
|
- kind: agent.run
|
|
131
162
|
options:
|
|
132
163
|
agent: ${vars.agent}
|
|
133
|
-
input: /sp:dev-runall --
|
|
164
|
+
input: /sp:dev-runall --tasks ${vars.featureTaskIds}
|
|
134
165
|
# Declared Layer-1 role (0538 R2): routing reason beside the agent: pin.
|
|
135
166
|
role: planner
|
|
136
167
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
137
168
|
|
|
138
169
|
- id: feature-verify
|
|
139
170
|
description: >
|
|
140
|
-
Certify the feature is complete: `spur feature check
|
|
141
|
-
|
|
142
|
-
|
|
171
|
+
Certify the feature is complete (0782 R3): run `spur feature check --as done --json`
|
|
172
|
+
EXACTLY ONCE, capture the exit code and JSON result at
|
|
173
|
+
.spur/run/<runId>-feature-dev-verify.json, and decide PASS/FAIL into the .status
|
|
174
|
+
sibling. PASS requires exit 0, a nonempty result array, and every returned scope
|
|
175
|
+
pass === true. The guards below only read the captured status — no sibling guard
|
|
176
|
+
re-runs the check. No --strict elevation, no whole-corpus scan.
|
|
177
|
+
onEnter:
|
|
178
|
+
- kind: shell
|
|
179
|
+
options:
|
|
180
|
+
command: >-
|
|
181
|
+
mkdir -p .spur/run;
|
|
182
|
+
VERIFY_JSON=".spur/run/$__runId-feature-dev-verify.json";
|
|
183
|
+
VERIFY_STATUS=".spur/run/$__runId-feature-dev-verify.status";
|
|
184
|
+
VERIFY_TMP=".spur/run/$__runId-feature-dev-verify.status.tmp";
|
|
185
|
+
rm -f "$VERIFY_STATUS";
|
|
186
|
+
set +e;
|
|
187
|
+
$spurBin feature check "$featureId" --as done --json > "$VERIFY_JSON" 2>&1;
|
|
188
|
+
check_rc=$?;
|
|
189
|
+
set -e;
|
|
190
|
+
if [ "$check_rc" -eq 0 ] && jq -e 'type == "array" and length > 0 and all(.[]; .pass == true)' "$VERIFY_JSON" > /dev/null 2>&1; then printf 'PASS\n' > "$VERIFY_TMP" && mv -f "$VERIFY_TMP" "$VERIFY_STATUS"; else printf 'FAIL\n' > "$VERIFY_TMP" && mv -f "$VERIFY_TMP" "$VERIFY_STATUS"; fi
|
|
191
|
+
- kind: note
|
|
192
|
+
options:
|
|
193
|
+
message: "Feature verification for ${vars.featureId}: decision at .spur/run/${vars.__runId}-feature-dev-verify.status (evidence at .spur/run/${vars.__runId}-feature-dev-verify.json) — exactly one `feature check --as done --json` invocation per run (0782 R3)."
|
|
143
194
|
|
|
144
195
|
- id: integration-review
|
|
145
196
|
description: >
|
|
146
|
-
Advisory integration review (D5-P, feature R11)
|
|
147
|
-
|
|
197
|
+
Advisory integration review (D5-P, feature R11), upgraded by 0770 to a
|
|
198
|
+
truthful collect: ONE Codex review request per HEAD, then exactly ONE
|
|
199
|
+
`pr-reviewing.ts collect --head <captured-head>` against that same HEAD —
|
|
200
|
+
no wait loop, no polling. The request verdict (`REQUESTED` /
|
|
201
|
+
`ALREADY_REQUESTED` / `ALREADY_REVIEWED`) describes request state only;
|
|
202
|
+
it is NEVER clean evidence. The ONLY clean evidence is the collected
|
|
203
|
+
status (CLEAN / FINDINGS / PENDING) persisted run-scoped at
|
|
204
|
+
.spur/run/<runId>-integration-review-collect.status. A missing head SHA,
|
|
205
|
+
an invalid collect result, or head drift records collect FAIL.
|
|
148
206
|
|
|
149
|
-
D8 decision D1 (0753 R3):
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
all count as PASS (review is initiated for this HEAD); only a request
|
|
156
|
-
error counts as FAIL. `softFail` is gone: a real failure now records FAIL
|
|
157
|
-
loudly. `requireCleanReview=true` still turns that FAIL into a blocking edge.
|
|
207
|
+
D8 decision D1 (0753 R3): invoking `pr-reviewing.ts request` directly —
|
|
208
|
+
the same per-HEAD dedupe-or-request logic `config/workflows/pr-review.yaml`
|
|
209
|
+
uses internally. Request FAIL records FAIL in
|
|
210
|
+
.spur/run/<runId>-integration-review.status, loudly (no softFail).
|
|
211
|
+
`requireCleanReview=true` turns a non-clean COLLECTED verdict (including
|
|
212
|
+
collect FAIL) into the blocking edge declared first below.
|
|
158
213
|
onEnter:
|
|
159
214
|
- kind: shell
|
|
160
215
|
options:
|
|
@@ -162,63 +217,69 @@ states:
|
|
|
162
217
|
mkdir -p .spur/run &&
|
|
163
218
|
STATUS_FILE=".spur/run/$__runId-integration-review.status" &&
|
|
164
219
|
REQUEST_JSON=".spur/run/$__runId-integration-review.json" &&
|
|
220
|
+
COLLECT_JSON=".spur/run/$__runId-integration-review-collect.json" &&
|
|
221
|
+
COLLECT_STATUS=".spur/run/$__runId-integration-review-collect.status" &&
|
|
165
222
|
PR_REQUEST_STATUS=".spur/run/$__runId-integration-review-pr-request.status" &&
|
|
166
223
|
set +e &&
|
|
167
224
|
bun "$(superskill script path sp pr-reviewing.ts)" request --base "$baseBranch" --json
|
|
168
225
|
--status-file "$PR_REQUEST_STATUS" > "$REQUEST_JSON" 2>&1;
|
|
169
|
-
|
|
170
|
-
if [ "$
|
|
226
|
+
request_rc=$?; set -e &&
|
|
227
|
+
if [ "$request_rc" -eq 0 ]; then
|
|
171
228
|
printf 'PASS\n' > "$STATUS_FILE";
|
|
172
229
|
else
|
|
173
230
|
printf 'FAIL\n' > "$STATUS_FILE";
|
|
174
231
|
fi &&
|
|
232
|
+
HEAD_SHA=$(jq -r '.head // empty' "$REQUEST_JSON" 2>/dev/null || true) &&
|
|
233
|
+
if [ -z "$HEAD_SHA" ]; then
|
|
234
|
+
echo "integration-review: request result carried no head SHA — collect not run, recorded FAIL" >&2;
|
|
235
|
+
printf 'FAIL\n' > "$COLLECT_STATUS";
|
|
236
|
+
else
|
|
237
|
+
set +e &&
|
|
238
|
+
bun "$(superskill script path sp pr-reviewing.ts)" collect --head "$HEAD_SHA" --json
|
|
239
|
+
--status-file "$COLLECT_STATUS" > "$COLLECT_JSON" 2>&1;
|
|
240
|
+
collect_rc=$?; set -e &&
|
|
241
|
+
echo "integration-review: collect rc=$collect_rc status=$(cat "$COLLECT_STATUS" 2>/dev/null || echo missing) — verdict captured to $COLLECT_JSON" >&2;
|
|
242
|
+
fi;
|
|
175
243
|
exit 0
|
|
176
244
|
- kind: note
|
|
177
245
|
options:
|
|
178
|
-
message: "Integration review for feature ${vars.featureId}
|
|
246
|
+
message: "Integration review for feature ${vars.featureId}: request verdict at .spur/run/${vars.__runId}-integration-review.status (request state only — REQUESTED/ALREADY_* are NEVER clean evidence); collected verdict at .spur/run/${vars.__runId}-integration-review-collect.status (CLEAN/FINDINGS/PENDING — the only clean evidence). Advisory unless requireCleanReview=true."
|
|
179
247
|
|
|
180
248
|
- id: done
|
|
181
|
-
description: Terminal —
|
|
182
|
-
# Checkpoint write: record session state for resume (Phase 4, task 0171 R3)
|
|
183
|
-
onEnter:
|
|
184
|
-
- kind: shell
|
|
185
|
-
options:
|
|
186
|
-
command: 'mkdir -p .spur/memory/sessions && echo "checkpoint: feature-dev done featureId=$featureId ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)" > .spur/memory/sessions/$featureId-checkpoint.md'
|
|
249
|
+
description: Terminal — the existing feature's roster executed and the feature verified.
|
|
187
250
|
|
|
188
251
|
- id: failed
|
|
189
|
-
description: Terminal —
|
|
252
|
+
description: Terminal — the roster contract was rejected at precheck, a task failed verification, or the feature check did not pass.
|
|
190
253
|
|
|
191
254
|
transitions:
|
|
255
|
+
# ── precheck routing (0782 R1/R2) — declaration order matters: the all-terminal
|
|
256
|
+
# direct-verify edge is tried FIRST so a fully-terminal roster reaches completion
|
|
257
|
+
# verification with zero execution model calls; then the profile hops; then the
|
|
258
|
+
# failure fallback. ──
|
|
192
259
|
- from: precheck
|
|
193
|
-
to:
|
|
194
|
-
description:
|
|
260
|
+
to: feature-verify
|
|
261
|
+
description: Roster PASS and every linked task is already terminal — verify the feature directly, zero execution model calls.
|
|
195
262
|
guard:
|
|
196
263
|
kind: shell
|
|
197
264
|
options:
|
|
198
|
-
command: 'test "$(cat .spur/run/$__runId-feature-dev-precheck.status 2>/dev/null)" = PASS'
|
|
265
|
+
command: 'test "$(cat ".spur/run/$__runId-feature-dev-precheck.status" 2>/dev/null)" = PASS && test -f ".spur/run/$__runId-feature-dev-feature.json" && test ! -s ".spur/run/$__runId-feature-dev-tasks.txt"'
|
|
199
266
|
- from: precheck
|
|
200
|
-
to: failed
|
|
201
|
-
description: Precheck FAIL (missing featureId and/or doctor red) — stop.
|
|
202
|
-
guard:
|
|
203
|
-
kind: always
|
|
204
|
-
|
|
205
|
-
# Linear prefix: brainstorm → plan → execute-tasks
|
|
206
|
-
- from: brainstorm
|
|
207
|
-
to: plan
|
|
208
|
-
description: Feature validated; decompose it into tasks
|
|
209
|
-
guard:
|
|
210
|
-
kind: always
|
|
211
|
-
|
|
212
|
-
- from: plan
|
|
213
267
|
to: execute-tasks-auto
|
|
214
|
-
description:
|
|
268
|
+
description: Roster PASS with todo work; profile=auto — batch the frozen list with --auto (no per-task HITL).
|
|
215
269
|
guard:
|
|
216
270
|
kind: shell
|
|
217
271
|
options:
|
|
218
|
-
command: 'test "$profile" = auto'
|
|
219
|
-
- from:
|
|
272
|
+
command: 'test "$(cat ".spur/run/$__runId-feature-dev-precheck.status" 2>/dev/null)" = PASS && test -s ".spur/run/$__runId-feature-dev-tasks.txt" && test "$profile" = auto'
|
|
273
|
+
- from: precheck
|
|
220
274
|
to: execute-tasks
|
|
221
|
-
description:
|
|
275
|
+
description: Roster PASS with todo work; interactive — batch the frozen list without --auto.
|
|
276
|
+
guard:
|
|
277
|
+
kind: shell
|
|
278
|
+
options:
|
|
279
|
+
command: 'test "$(cat ".spur/run/$__runId-feature-dev-precheck.status" 2>/dev/null)" = PASS && test -s ".spur/run/$__runId-feature-dev-tasks.txt"'
|
|
280
|
+
- from: precheck
|
|
281
|
+
to: failed
|
|
282
|
+
description: Precheck FAIL (missing identity, malformed/empty roster, duplicate identities, or refinement-blocking statuses) — stop before any model dispatch.
|
|
222
283
|
guard:
|
|
223
284
|
kind: always
|
|
224
285
|
|
|
@@ -233,37 +294,40 @@ transitions:
|
|
|
233
294
|
guard:
|
|
234
295
|
kind: always
|
|
235
296
|
|
|
236
|
-
# ── feature-verify gate:
|
|
237
|
-
# Declaration order: the pass guard is tried FIRST.
|
|
297
|
+
# ── feature-verify gate: the captured one-shot check decision certifies review, else
|
|
298
|
+
# fail. Declaration order: the pass guard is tried FIRST. The check itself is NOT
|
|
299
|
+
# re-run here (0782 R3) — the shell decision file is the only evidence. ──
|
|
238
300
|
- from: feature-verify
|
|
239
301
|
to: integration-review
|
|
240
|
-
description:
|
|
302
|
+
description: Captured completion check is PASS — spend one integration review on this stable HEAD.
|
|
241
303
|
guard:
|
|
242
304
|
kind: shell
|
|
243
305
|
options:
|
|
244
|
-
command: "$
|
|
245
|
-
|
|
306
|
+
command: 'test "$(cat ".spur/run/$__runId-feature-dev-verify.status" 2>/dev/null)" = PASS'
|
|
246
307
|
- from: integration-review
|
|
247
308
|
to: failed
|
|
248
309
|
description: >
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
310
|
+
Collected verdict is not CLEAN (FINDINGS / PENDING / collect FAIL or
|
|
311
|
+
missing) and this branch opted into a hard gate (requireCleanReview=true).
|
|
312
|
+
Exactly one collect ran against the captured request HEAD — no wait loop —
|
|
313
|
+
so pending or findings stop the feature here with the actual reason.
|
|
314
|
+
Ordered before the advisory edge so the strict policy wins when both
|
|
315
|
+
would match.
|
|
252
316
|
guard:
|
|
253
317
|
kind: shell
|
|
254
318
|
options:
|
|
255
|
-
command: 'test "$requireCleanReview" = true && test "$(cat .spur/run/$__runId-integration-review.status 2>/dev/null)"
|
|
319
|
+
command: 'test "$requireCleanReview" = true && test "$(cat .spur/run/$__runId-integration-review-collect.status 2>/dev/null)" != CLEAN'
|
|
256
320
|
- from: integration-review
|
|
257
321
|
to: done
|
|
258
322
|
description: >
|
|
259
|
-
|
|
260
|
-
non-clean advisory
|
|
323
|
+
Collected CLEAN, or advisory mode (requireCleanReview != true) — the
|
|
324
|
+
feature is complete. A non-clean advisory collect never blocks a
|
|
325
|
+
locally-verified feature (feature R11); the note above preserves the
|
|
326
|
+
collected non-clean state in the run record instead of hiding it.
|
|
261
327
|
guard:
|
|
262
328
|
kind: always
|
|
263
329
|
- from: feature-verify
|
|
264
330
|
to: failed
|
|
265
|
-
description:
|
|
331
|
+
description: Completion check missing, malformed, or non-PASS (or its evidence failed to persist) — block BEFORE integration review; no review is requested (0782 R3).
|
|
266
332
|
guard:
|
|
267
|
-
kind:
|
|
268
|
-
options:
|
|
269
|
-
command: '! $spurBin feature check $featureId --strict'
|
|
333
|
+
kind: always
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
10
10
|
kind: state-machine
|
|
11
11
|
name: feature-lifecycle
|
|
12
|
+
version: "1"
|
|
12
13
|
description: >
|
|
13
14
|
Feature lifecycle FSM (design §2.3, §5.1, DD-13). States are the canonical
|
|
14
15
|
FeatureStatus vocabulary; transitions encode the §2.3 graph including the
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
49
49
|
kind: state-machine
|
|
50
50
|
name: history-anatomy
|
|
51
|
+
version: "1"
|
|
51
52
|
description: "Daily/ad-hoc history-anatomy report: cache branch, deterministic analyze/render, skill enrichment, deterministic structure gate, independent evidence validation, bounded correction, atomic publication"
|
|
52
53
|
iterationBound: 20
|
|
53
54
|
initialState: start
|
|
@@ -153,7 +154,7 @@ states:
|
|
|
153
154
|
--baseline .spur/run/$__runId-history-anatomy-baseline.json --target "$HA_TARGET"
|
|
154
155
|
--mode "$mode" --date "$HA_DATE" --recompute "$recompute" --executor "$agent"
|
|
155
156
|
--skill-dir "$HA_SKILL" --contract "$HA_SKILL/references/report-contract.md"
|
|
156
|
-
--workflow "$workflowFile" --contract-version "$contractVersion" --run-id "$__runId"
|
|
157
|
+
--workflow "$workflowFile" --helper "$HA_HELPER" --contract-version "$contractVersion" --run-id "$__runId"
|
|
157
158
|
--out .spur/run/$__runId-provenance.json
|
|
158
159
|
> .spur/run/$__runId-cache-disposition.txt
|
|
159
160
|
|
|
@@ -208,6 +209,9 @@ states:
|
|
|
208
209
|
description: >
|
|
209
210
|
Independent evidence validation via the skill operation sp:history-anatomy validate.
|
|
210
211
|
Publication is reachable only from a PASS here. Same undeclared-write assertion as enrich (0676 R3).
|
|
212
|
+
The publish guard reads the ANCHORED FINAL LINE (0771): only a validation artifact whose last
|
|
213
|
+
line is exactly `Verdict: PASS` publishes — a leading PASS under a later FAIL or a
|
|
214
|
+
`not Verdict: PASS` line can never satisfy it.
|
|
211
215
|
onEnter:
|
|
212
216
|
- kind: shell
|
|
213
217
|
options:
|
|
@@ -390,18 +394,18 @@ transitions:
|
|
|
390
394
|
|
|
391
395
|
- from: validate
|
|
392
396
|
to: stamp
|
|
393
|
-
description: Independent validation PASS — stamp provenance, then publish atomically.
|
|
397
|
+
description: Independent validation PASS (anchored final line, 0771) — stamp provenance, then publish atomically.
|
|
394
398
|
guard:
|
|
395
399
|
kind: shell
|
|
396
400
|
options:
|
|
397
|
-
command: '
|
|
401
|
+
command: 'tail -n 1 .spur/run/$__runId-validation.txt 2>/dev/null | grep -qx "Verdict: PASS"'
|
|
398
402
|
- from: validate
|
|
399
403
|
to: correct
|
|
400
404
|
description: Validation FAIL under the shared two-pass correction cap.
|
|
401
405
|
guard:
|
|
402
406
|
kind: shell
|
|
403
407
|
options:
|
|
404
|
-
command: '!
|
|
408
|
+
command: '! tail -n 1 .spur/run/$__runId-validation.txt 2>/dev/null | grep -qx "Verdict: PASS" && test "$(cat .spur/run/$__runId-correction-count 2>/dev/null || echo 0)" -lt 2'
|
|
405
409
|
- from: validate
|
|
406
410
|
to: failed
|
|
407
411
|
description: Validation FAIL with the correction cap exhausted — terminate without publishing.
|