@gobing-ai/spur 0.3.80 → 0.3.82
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 +31 -18
- package/config/config.global.yaml +13 -11
- package/config/pipeline-budgets.json +34 -2
- package/config/plugin-scripts.json +25 -0
- package/config/rules/boundary/config-loading-ownership.yaml +0 -3
- package/config/rules/boundary/dao-boundary.yaml +4 -17
- package/config/rules/boundary/planning-folder-hardcode.yaml +0 -1
- package/config/rules/boundary/sp-no-vendor-refs.yaml +3 -2
- package/config/rules/boundary/sp-runtime-path.yaml +3 -14
- package/config/rules/quality/coverage-gate.yaml +3 -14
- package/config/rules/quality/tsdoc-exports.yaml +4 -7
- package/config/rules/strict/http-boundaries.yaml +5 -8
- package/config/rules/strict/runtime-boundaries.yaml +1 -5
- package/config/rules/structure/protected-files.yaml +9 -3
- package/config/rules/structure/test-focus-skip.yaml +0 -2
- package/config/rules/structure/test-location.yaml +0 -5
- package/config/rules/surface/check-cli-surface.yaml +3 -2
- package/config/rules/typescript/bun-tooling.yaml +5 -7
- package/config/rules/typescript/guarded-happy-dom-register.yaml +0 -2
- package/config/rules/typescript/happy-dom-teardown.yaml +0 -2
- package/config/rules/typescript/no-biome-suppressions.yaml +0 -2
- package/config/rules/typescript/no-debugger.yaml +0 -2
- package/config/rules/typescript/no-eslint-suppressions.yaml +0 -4
- package/config/rules/typescript/no-leaky-module-mocks.yaml +6 -13
- package/config/rules/typescript/no-module-scope-import-calls.yaml +0 -2
- package/config/rules/typescript/no-syscall-emulation-in-boundary-mock.yaml +0 -3
- package/config/rules/typescript/no-unmocked-module-eval-side-effects.yaml +0 -3
- package/config/rules/typescript/output-boundaries.yaml +0 -3
- package/config/rules/typescript/prefer-accessible-role-for-button-queries.yaml +0 -3
- package/config/rules/ui/ui-import-boundary.yaml +1 -5
- package/config/templates/docs/99_PROJECT_CONSTITUTION.md +75 -13
- package/config/transition-shims.json +0 -7
- package/config/workflows/basic.yaml +4 -0
- package/config/workflows/docs-pipeline.yaml +13 -14
- package/config/workflows/feature-dev.yaml +20 -65
- package/config/workflows/history-anatomy.yaml +22 -1
- package/config/workflows/idea-pipeline.yaml +53 -97
- package/config/workflows/pr-review.yaml +21 -33
- package/config/workflows/task-pipeline.yaml +87 -330
- package/config/workflows/wayfinder-resolution.yaml +12 -26
- package/config/workflows/wrapup-pipeline.yaml +48 -189
- package/package.json +9 -9
- package/plugins/sp/README.md +12 -3
- package/plugins/sp/agents/expert-spur.md +41 -19
- package/plugins/sp/lib/idea-handoff.generated.d.mts +17 -0
- package/plugins/sp/lib/idea-handoff.generated.mjs +1301 -0
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/feature-dev-precheck.mjs +146 -0
- package/plugins/sp/scripts/feature-dev-precheck.ts +238 -0
- package/plugins/sp/scripts/idea-handoff.mjs +27 -0
- package/plugins/sp/scripts/idea-handoff.ts +44 -0
- package/plugins/sp/scripts/inline-run-setup.ts +69 -1
- package/plugins/sp/scripts/quality-gate.mjs +216 -0
- package/plugins/sp/scripts/quality-gate.ts +287 -0
- package/plugins/sp/scripts/surface-drift-inventory.ts +0 -2
- package/plugins/sp/scripts/task-size-precheck.ts +44 -16
- package/plugins/sp/scripts/verify-answer-lint.ts +17 -1
- package/plugins/sp/scripts/workflow-step-profile.mjs +319 -0
- package/plugins/sp/scripts/workflow-step-profile.ts +456 -0
- package/plugins/sp/scripts/wrapup-steps.mjs +350 -0
- package/plugins/sp/scripts/wrapup-steps.ts +466 -0
- package/plugins/sp/skills/code-review/references/review-lenses.md +3 -0
- package/plugins/sp/skills/parallel-execution/references/dispatch-surface.md +1 -1
- package/plugins/sp/skills/spec-decomposition/references/decomposition.md +29 -0
- package/plugins/sp/skills/spur-cli/SKILL.md +4 -8
- package/plugins/sp/skills/spur-cli/references/agent.md +44 -69
- package/plugins/sp/skills/spur-cli/references/message.md +30 -3
- package/plugins/sp/skills/spur-cli/references/projects.md +25 -1
- package/plugins/sp/skills/spur-cli/references/self.md +6 -5
- package/plugins/sp/skills/spur-cli/references/serve.md +8 -7
- package/plugins/sp/skills/spur-cli/references/tasks.md +1 -1
- package/plugins/sp/skills/spur-cli/references/workflows/operations.md +6 -3
- package/plugins/sp/skills/spur-cli/references/workflows/workflow-fit-and-tuning.md +57 -18
- package/plugins/sp/skills/spur-composer/SKILL.md +145 -0
- package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +10 -0
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +1 -1
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +4 -6
- package/plugins/sp/skills/spur-dev/references/glossary.md +1 -1
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +11 -1
- package/plugins/sp/skills/spur-dev/references/planning-workflow.md +24 -0
- package/plugins/sp/skills/spur-doctor/SKILL.md +138 -0
- package/plugins/sp/skills/taste-refactoring-api/README.md +43 -0
- package/plugins/sp/skills/taste-refactoring-api/SKILL.md +334 -0
- package/plugins/sp/skills/taste-refactoring-api/checklists/daily-api-review.md +71 -0
- package/plugins/sp/skills/taste-refactoring-api/examples/refactor-example.md +72 -0
- package/plugins/sp/skills/taste-refactoring-api/examples/review-template.md +93 -0
- package/plugins/sp/skills/taste-refactoring-api/references/api-refactoring-playbook.md +253 -0
- package/plugins/sp/skills/taste-refactoring-api/references/protocol-modes.md +79 -0
- package/plugins/sp/skills/taste-refactoring-api/references/research-basis.md +58 -0
- package/plugins/sp/skills/taste-refactoring-architect/README.md +26 -0
- package/plugins/sp/skills/taste-refactoring-architect/SKILL.md +471 -0
- package/plugins/sp/skills/taste-refactoring-architect/checklists/daily-architecture-review.md +48 -0
- package/plugins/sp/skills/taste-refactoring-architect/examples/refactor-example.md +55 -0
- package/plugins/sp/skills/taste-refactoring-architect/examples/review-template.md +51 -0
- package/plugins/sp/skills/taste-refactoring-architect/references/architecture-refactoring-playbook.md +173 -0
- package/plugins/sp/skills/taste-refactoring-architect/references/research-basis.md +28 -0
- package/plugins/sp/skills/taste-refactoring-tests/README.md +28 -0
- package/plugins/sp/skills/taste-refactoring-tests/SKILL.md +482 -0
- package/plugins/sp/skills/taste-refactoring-tests/checklists/daily-test-review.md +39 -0
- package/plugins/sp/skills/taste-refactoring-tests/examples/refactor-example.md +85 -0
- package/plugins/sp/skills/taste-refactoring-tests/examples/review-template.md +59 -0
- package/plugins/sp/skills/taste-refactoring-tests/references/research-basis.md +47 -0
- package/plugins/sp/skills/taste-refactoring-tests/references/test-refactoring-playbook.md +222 -0
- package/plugins/sp/skills/taste-refactoring-ui/README.md +12 -0
- package/plugins/sp/skills/taste-refactoring-ui/SKILL.md +290 -0
- package/plugins/sp/skills/taste-refactoring-ui/checklists/daily-ui-review.md +72 -0
- package/plugins/sp/skills/taste-refactoring-ui/examples/review-template.md +51 -0
- package/plugins/sp/skills/taste-refactoring-ui/references/refactoring-ui-playbook.md +170 -0
- package/plugins/sp/skills/wayfinder/SKILL.md +2 -2
- package/plugins/sp/skills/wayfinder/references/pipeline-resolution.md +30 -0
- package/schemas/spur-config.schema.json +105 -85
- package/spur.js +44616 -43320
- package/web/_astro/BoardApp.D-WlxiN2.js +1 -0
- package/web/_astro/{BoardApp.CHQ1lycZ.js → BoardApp.D8bM9pKL.js} +97 -95
- package/web/_astro/{TaskDetail.GKfQJ60c.js → TaskDetail.BPRqgVUE.js} +1 -1
- package/web/_astro/{arc.DWEtA3Tx.js → arc.BPrPES3z.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.DB42oWmP.js → architectureDiagram-3BPJPVTR.qX_7q02P.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.rhv-zNQV.js → blockDiagram-GPEHLZMM.CUZfj5V7.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.Ci4-4VvY.js → c4Diagram-AAUBKEIU.CTaOr8hH.js} +1 -1
- package/web/_astro/channel.DGZaFHZx.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.Cc9veUgf.js → chunk-2J33WTMH.Dt-9wf3h.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.Bec9c4eI.js → chunk-4BX2VUAB.CTC2sdoN.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.DoV8S1iB.js → chunk-55IACEB6.DQcxt2_g.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.DwR-Qlyj.js → chunk-727SXJPM.DXFPSn-a.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.ND_a81WY.js → chunk-AQP2D5EJ.BCx3U4bT.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.Wv_jwG48.js → chunk-FMBD7UC4.DL2tJkdO.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.CXKXCMmp.js → chunk-ND2GUHAM.DZyflMro.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.nkaoNYQq.js → chunk-QZHKN3VN.CUI2mT09.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.cMQcVlQu.js → classDiagram-4FO5ZUOK.g4rX4Fr1.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.cMQcVlQu.js → classDiagram-v2-Q7XG4LA2.g4rX4Fr1.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.OaDJ7Mr2.js → cose-bilkent-S5V4N54A.CzWJLqp0.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.Chi8IphF.js → cynefin-OW5HDTMX.WgsvQeCR.js} +1 -1
- package/web/_astro/{cytoscape.esm.DzSz-X2X.js → cytoscape.esm.BB4DxJjf.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.CzK2t_Fp.js → dagre-BM42HDAG.Dzv6ngql.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.DRvxlVS7.js → diagram-2AECGRRQ.CpJ4a9rU.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.CnYvNdwA.js → diagram-5GNKFQAL.CzPlF_dq.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.CpLpMw5R.js → diagram-KO2AKTUF.TAkZNTcQ.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.JTb78qUA.js → diagram-LMA3HP47.uCjoKSag.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.Bk-1jDIb.js → diagram-OG6HWLK6.eMplIjoK.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.D8hN9GZq.js → erDiagram-TEJ5UH35.Bf7zoXGz.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.-6zQr6m5.js → flowDiagram-I6XJVG4X.B_bHj3gN.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.DboLQ9ca.js → ganttDiagram-6RSMTGT7.BasrHRMj.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.4tYvJKGR.js → gitGraphDiagram-PVQCEYII.C6iphq1x.js} +1 -1
- package/web/_astro/index.DayyIngm.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.Bd9rXpsB.js → infoDiagram-5YYISTIA.HXmDMhW4.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.CvMoaf67.js → ishikawaDiagram-YF4QCWOH.BSmW8NiU.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.Ccy1CA7y.js → journeyDiagram-JHISSGLW.DEQow5fo.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.0MaMqHNS.js → kanban-definition-UN3LZRKU.IVm9cTdc.js} +1 -1
- package/web/_astro/{linear.CHXgcIbN.js → linear.CrsM73_9.js} +1 -1
- package/web/_astro/{mermaid.core.Ca-kcelG.js → mermaid.core.CfBeDJls.js} +6 -6
- package/web/_astro/{mindmap-definition-RKZ34NQL.BUIDlHa0.js → mindmap-definition-RKZ34NQL.C3j60Y-0.js} +1 -1
- package/web/_astro/ordinal.BYWQX77i.js +1 -0
- package/web/_astro/{pieDiagram-4H26LBE5.2dX3CU1s.js → pieDiagram-4H26LBE5.B-aCMeEA.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.B3LBlRiv.js → quadrantDiagram-W4KKPZXB.Cib965yq.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.X12I2uNx.js → requirementDiagram-4Y6WPE33.D61cS4O-.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.BXohIHqx.js → sankeyDiagram-5OEKKPKP.GKF2qVPy.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.C37ZIUzg.js → sequenceDiagram-3UESZ5HK.DZnq8F2h.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.BRgz317z.js → stateDiagram-AJRCARHV.DXUFmdgM.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.7VYSXN9-.js → stateDiagram-v2-BHNVJYJU.BtHmhLEz.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.BVNz_HiN.js → timeline-definition-PNZ67QCA.Cy-WW2ln.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.CHVDkPX4.js → vennDiagram-CIIHVFJN.SLp5b9KI.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.EQQ_qT9v.js → wardleyDiagram-YWT4CUSO.Bww45mWV.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.DrAT9WoP.js → xychartDiagram-2RQKCTM6.DR4swI6a.js} +1 -1
- package/web/apple-touch-icon.png +0 -0
- package/web/favicon.ico +0 -0
- package/web/favicon.svg +17 -4
- package/web/icon-192.png +0 -0
- package/web/icon-512.png +0 -0
- package/web/index.html +2 -2
- package/web/site.webmanifest +31 -0
- package/web/spur_logo.svg +1 -0
- package/plugins/sp/skills/spur-cli/references/team.md +0 -145
- package/web/_astro/BoardApp.DV9kx0wo.js +0 -1
- package/web/_astro/channel.BAI6xLeV.js +0 -1
- package/web/_astro/index.Dcr_8fiK.css +0 -1
- package/web/_astro/ordinal.DBvzRdQf.js +0 -1
|
@@ -163,142 +163,38 @@ states:
|
|
|
163
163
|
Fast deterministic task readiness and size checks. Failures route to the
|
|
164
164
|
`failed` terminal state (not a raw lifecycle abort mid-enter).
|
|
165
165
|
onEnter:
|
|
166
|
-
#
|
|
167
|
-
# a tree already dirty with another task's implementation is how 0485's diff
|
|
168
|
-
# got swept into 0486's run. Corpus dirs are excluded: the pipeline writes
|
|
169
|
-
# those itself.
|
|
170
|
-
# R1 (0511): task-corpus dirt gets its own advisory NOTE — docs/tasks* is
|
|
171
|
-
# excluded from the non-corpus warning, so pre-existing task-corpus edits are
|
|
172
|
-
# invisible at launch yet can be accidentally staged with the current task
|
|
173
|
-
# later. Advisory only (never a block): the pipeline legitimately updates
|
|
174
|
-
# task files, so corpus dirt is launch-time visibility, not a lifecycle guard.
|
|
166
|
+
# (e) git-status hygiene advisory; never fails the run (0487 R6 / 0511 R1).
|
|
175
167
|
- kind: shell
|
|
176
168
|
options:
|
|
177
169
|
command: >-
|
|
178
|
-
|
|
179
|
-
if [ -n "$DIRTY" ]; then
|
|
180
|
-
echo "precheck: WARNING - working tree has uncommitted non-corpus changes; commit or stash before starting a new task:";
|
|
181
|
-
echo "$DIRTY";
|
|
182
|
-
fi;
|
|
183
|
-
CORPUS_DIRTY=$(git status --porcelain -- ':(glob)docs/tasks*/**' 2>/dev/null);
|
|
184
|
-
if [ -n "$CORPUS_DIRTY" ]; then
|
|
185
|
-
echo "precheck: NOTE - task corpus has uncommitted changes; review before staging with this task:";
|
|
186
|
-
echo "$CORPUS_DIRTY";
|
|
187
|
-
fi;
|
|
188
|
-
exit 0
|
|
170
|
+
D=$(git status --porcelain -- . ':(exclude)docs/tasks*' ':(exclude)docs/features' 2>/dev/null); [ -z "$D" ] || printf 'precheck: WARNING - working tree has uncommitted non-corpus changes — commit or stash before starting a new task:\n%s\n' "$D"; C=$(git status --porcelain -- ':(glob)docs/tasks*/**' 2>/dev/null); [ -z "$C" ] || printf 'precheck: NOTE - task corpus has uncommitted changes — review before staging with this task:\n%s\n' "$C"; exit 0
|
|
189
171
|
- kind: note
|
|
190
172
|
options:
|
|
191
173
|
# R4 task_run_links linkage (kind=pipeline) is written by a WorkflowService
|
|
192
174
|
# hook at run start — see the 0062 follow-up note; there is no link-writing
|
|
193
175
|
# CLI verb to call from a shell step, so it cannot live in pure YAML.
|
|
194
176
|
message: "Pipeline start for task ${vars.wbs}."
|
|
195
|
-
#
|
|
196
|
-
# Under profile=auto, resolve feature_id, sync (preferred) or update to active.
|
|
197
|
-
# Under non-auto, leave R4 message to guide the operator.
|
|
198
|
-
# R3 (0723): a real reactivation failure is surfaced, not swallowed —
|
|
199
|
-
# the default 'fail' onEnter policy halts the sequence and routes the
|
|
200
|
-
# run to `failed` before implementation. Verbs stay single-shot:
|
|
201
|
-
# one sync, then one update fallback, never retried in a loop.
|
|
177
|
+
# (e) auto-profile feature reopen; exit 1 when reactivation fails (0723 R3).
|
|
202
178
|
- kind: shell
|
|
203
179
|
options:
|
|
204
180
|
command: >-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
jq -r '.feature_id // .frontmatter.feature_id // empty' 2>/dev/null);
|
|
208
|
-
if [ -n "$FID" ]; then
|
|
209
|
-
if ! $spurBin feature sync "$FID" --force 2>/dev/null; then
|
|
210
|
-
if ! $spurBin feature update "$FID" active 2>/dev/null; then
|
|
211
|
-
echo "precheck: FAIL - feature reactivation $FID failed;" >&2;
|
|
212
|
-
echo "precheck: feature sync + feature update both errored" >&2;
|
|
213
|
-
exit 1;
|
|
214
|
-
fi;
|
|
215
|
-
fi;
|
|
216
|
-
fi;
|
|
217
|
-
fi;
|
|
218
|
-
exit 0
|
|
219
|
-
# R2 (0454, 0723): task size precheck — deterministic count-only
|
|
220
|
-
# evaluation of R-item and Plan-item counts. No executor-tier policy:
|
|
221
|
-
# dispatch-time requiresCapabilities at `agent.run` is the
|
|
222
|
-
# authoritative capability check. Writes PASS/FAIL to
|
|
223
|
-
# .spur/run/<wbs>-precheck-size.status. Always exit 0 (soft action);
|
|
224
|
-
# the precheck→implement guard reads the file, so a missing checker
|
|
225
|
-
# fails closed (writes FAIL, never PASS).
|
|
181
|
+
[ "$profile" = auto ] || exit 0; FID=$($spurBin task show $wbs --json 2>/dev/null | jq -r '.feature_id // .frontmatter.feature_id // empty' 2>/dev/null); [ -z "$FID" ] || $spurBin feature sync "$FID" --force 2>/dev/null || $spurBin feature update "$FID" active 2>/dev/null || { echo "precheck: FAIL - feature reactivation $FID failed — feature sync + feature update both errored" >&2; exit 1; }; exit 0
|
|
182
|
+
# (d) task-size-precheck.ts owns the check; shell only resolves it and fails closed (0454 R2 / 0723 R2).
|
|
226
183
|
- kind: shell
|
|
227
184
|
options:
|
|
228
185
|
command: >-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
SIZE_SCRIPT="plugins/sp/scripts/task-size-precheck.ts";
|
|
232
|
-
[ -f "$SIZE_SCRIPT" ] ||
|
|
233
|
-
SIZE_SCRIPT="$(superskill script path sp task-size-precheck.ts 2>/dev/null)";
|
|
234
|
-
if [ -n "$SIZE_SCRIPT" ] && [ -f "$SIZE_SCRIPT" ]; then
|
|
235
|
-
bun "$SIZE_SCRIPT" "$wbs"
|
|
236
|
-
--spur-bin "$spurBin" --max-reqs "$maxImplementReqs"
|
|
237
|
-
--max-plan-items "$maxImplementPlanItems";
|
|
238
|
-
else
|
|
239
|
-
echo "task-size-precheck failed closed — checker not found in" >&2 &&
|
|
240
|
-
echo "plugins/sp/scripts/ nor staged; run 'superskill install sp'." >&2 &&
|
|
241
|
-
echo "FAIL" > "$SIZE_FILE";
|
|
242
|
-
fi &&
|
|
243
|
-
exit 0
|
|
244
|
-
# 0726 R2: task evidence precheck — deterministic live-data
|
|
245
|
-
# evidence-channel proof before implement dispatch. Writes PASS/FAIL to
|
|
246
|
-
# .spur/run/<wbs>-precheck-evidence.status. Always exit 0 (soft action);
|
|
247
|
-
# the precheck→implement guard reads the file, so a missing checker
|
|
248
|
-
# fails closed (writes FAIL, never PASS).
|
|
186
|
+
mkdir -p .spur/run; S=plugins/sp/scripts/task-size-precheck.ts; [ -f "$S" ] || S="$(superskill script path sp task-size-precheck.ts 2>/dev/null)"; if [ -f "$S" ]; then bun "$S" "$wbs" --spur-bin "$spurBin" --max-reqs "$maxImplementReqs" --max-plan-items "$maxImplementPlanItems"; else echo "task-size-precheck failed closed — checker not found in plugins/sp/scripts/ nor staged — run 'superskill install sp'." >&2; echo "FAIL" > ".spur/run/$wbs-precheck-size.status"; fi; exit 0
|
|
187
|
+
# (d) task-evidence-precheck.ts owns the check; shell only resolves it and fails closed (0726 R2).
|
|
249
188
|
- kind: shell
|
|
250
189
|
options:
|
|
251
190
|
command: >-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
[ -f "$EVID_SCRIPT" ] ||
|
|
256
|
-
EVID_SCRIPT="$(superskill script path sp task-evidence-precheck.ts 2>/dev/null)";
|
|
257
|
-
if [ -n "$EVID_SCRIPT" ] && [ -f "$EVID_SCRIPT" ]; then
|
|
258
|
-
bun "$EVID_SCRIPT" "$wbs"
|
|
259
|
-
--spur-bin "$spurBin";
|
|
260
|
-
else
|
|
261
|
-
echo "task-evidence-precheck failed closed — checker not found" >&2 &&
|
|
262
|
-
echo "in plugins/sp/scripts/ nor staged; run 'superskill install sp'." >&2 &&
|
|
263
|
-
echo "FAIL" > "$EVID_FILE";
|
|
264
|
-
fi &&
|
|
265
|
-
exit 0
|
|
266
|
-
# Proportional route table evaluation (0759 R1/R4). The reason artifact is RUN-scoped, not
|
|
267
|
-
# wbs-scoped: ADR-107 names `.spur/run/<runId>-route-reason.txt`, and a wbs-scoped path lets
|
|
268
|
-
# a re-run of the same task overwrite the earlier run's route claim, so the artifact could
|
|
269
|
-
# not attribute a route to the run that took it (0759 R5). `__runId` is injected by
|
|
270
|
-
# WorkflowAppService.run(); the wbs fallback keeps a driver-less invocation from writing to
|
|
271
|
-
# a bare "-route-reason.txt". The log line carries the run id for the same reason — an
|
|
272
|
-
# unattributed append is log scraping, which R5 explicitly rejects as evidence.
|
|
273
|
-
# 0804 R8: the run id must be a single safe filename component before it
|
|
274
|
-
# becomes REASON_FILE — unresolved interpolation ($ { } vars.), path
|
|
275
|
-
# separators and dot traversal are a failed action (nonzero, no artifact).
|
|
276
|
-
# NOTE: no `#` comments inside the folded scalar — `>-` joins all same-indent
|
|
277
|
-
# lines into ONE shell line, so an in-scalar `#` comments out the rest of the
|
|
278
|
-
# command (the original R8 draft broke exactly this way; the case statement
|
|
279
|
-
# must stay one line).
|
|
191
|
+
mkdir -p .spur/run; S=plugins/sp/scripts/task-evidence-precheck.ts; [ -f "$S" ] || S="$(superskill script path sp task-evidence-precheck.ts 2>/dev/null)"; if [ -f "$S" ]; then bun "$S" "$wbs" --spur-bin "$spurBin"; else echo "task-evidence-precheck failed closed — checker not found in plugins/sp/scripts/ nor staged — run 'superskill install sp'." >&2; echo "FAIL" > ".spur/run/$wbs-precheck-evidence.status"; fi; exit 0
|
|
192
|
+
# (e) route-reason lookup and routes log; proportional-routing tests locate it
|
|
193
|
+
# (0759 R1/R5 run-scoped artifact; 0804 R8 run-id safety — one line, no in-scalar `#`).
|
|
280
194
|
- kind: shell
|
|
281
195
|
options:
|
|
282
196
|
command: >-
|
|
283
|
-
mkdir -p .spur/run .spur/memory
|
|
284
|
-
RUN_ID="$__runId" &&
|
|
285
|
-
if [ -z "$RUN_ID" ]; then RUN_ID="pipeline-$wbs"; fi &&
|
|
286
|
-
case "$RUN_ID" in *'$'*|*'{'*|*'}'*|*vars.*|*/*|*'\'*|*..*) echo "route-reason: refusing unsafe run id: $RUN_ID" >&2; exit 1 ;; *) : ;; esac &&
|
|
287
|
-
REASON_FILE=".spur/run/$RUN_ID-route-reason.txt" &&
|
|
288
|
-
if [ "$mode" = "fast" ]; then
|
|
289
|
-
echo "fast:evidence complete+consistent" > "$REASON_FILE";
|
|
290
|
-
elif [ -z "$mode" ]; then
|
|
291
|
-
echo "safety:standard verification" > "$REASON_FILE";
|
|
292
|
-
elif [ "$mode" = "unknown" ]; then
|
|
293
|
-
echo "safety:unknown evidence quality" > "$REASON_FILE";
|
|
294
|
-
elif [ "$mode" = "conflict" ]; then
|
|
295
|
-
echo "safety:conflicting evidence" > "$REASON_FILE";
|
|
296
|
-
else
|
|
297
|
-
echo "safety:unrecognized evidence (mode=$mode)" > "$REASON_FILE";
|
|
298
|
-
fi &&
|
|
299
|
-
printf '%s %s %s\n' "$RUN_ID" "$wbs" "$(cat "$REASON_FILE")"
|
|
300
|
-
>> .spur/memory/task-pipeline-routes.log &&
|
|
301
|
-
exit 0
|
|
197
|
+
mkdir -p .spur/run .spur/memory; RUN_ID="$__runId"; [ -n "$RUN_ID" ] || RUN_ID="pipeline-$wbs"; case "$RUN_ID" in *'$'*|*'{'*|*'}'*|*vars.*|*/*|*'\'*|*..*) echo "route-reason: refusing unsafe run id: $RUN_ID" >&2; exit 1 ;; esac; REASON_FILE=".spur/run/$RUN_ID-route-reason.txt"; jq -rn --arg m "$mode" '{"fast":"fast:evidence complete+consistent","":"safety:standard verification","unknown":"safety:unknown evidence quality","conflict":"safety:conflicting evidence"}[$m] // "safety:unrecognized evidence (mode=\($m))"' > "$REASON_FILE"; printf '%s %s %s\n' "$RUN_ID" "$wbs" "$(cat "$REASON_FILE")" >> .spur/memory/task-pipeline-routes.log; exit 0
|
|
302
198
|
|
|
303
199
|
- id: implement
|
|
304
200
|
description: >
|
|
@@ -336,23 +232,19 @@ states:
|
|
|
336
232
|
requiresCapabilities:
|
|
337
233
|
fsWrite: available
|
|
338
234
|
processSpawn: available
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
fi;
|
|
353
|
-
return "$RETRY_RC";
|
|
354
|
-
};
|
|
355
|
-
retry_transient $spurBin task update "$wbs" wip --no-lifecycle
|
|
235
|
+
# (c) command.gate owns the wip transition with classified transient retry.
|
|
236
|
+
- kind: command.gate
|
|
237
|
+
options:
|
|
238
|
+
id: task-implement-transition
|
|
239
|
+
executable: "${vars.spurBin}"
|
|
240
|
+
args: ["task", "update", "${vars.wbs}", "wip", "--no-lifecycle"]
|
|
241
|
+
retry:
|
|
242
|
+
maxAttempts: 2
|
|
243
|
+
delayMs: 2000
|
|
244
|
+
on: ["sqlite-busy", "ENOENT", "EBUSY", "ENOTEMPTY"]
|
|
245
|
+
resultFile: .spur/run/${vars.__runId}-implement-transition.status
|
|
246
|
+
softFail: false
|
|
247
|
+
timeoutMs: 120000
|
|
356
248
|
# Post-implement cleanup: auto-format any unformatted output the agent produced.
|
|
357
249
|
# The agent may leave files that don't pass `biome check`, which then fails
|
|
358
250
|
# `bun run lint` downstream. Running format here is cheap (< 1 s) and prevents
|
|
@@ -388,6 +280,7 @@ states:
|
|
|
388
280
|
onEnter:
|
|
389
281
|
# R6/R2 (0703): resolve the task-spec path BEFORE the digest capture. `docs/tasks*` is excluded
|
|
390
282
|
# from the digest's git-tree half, so the spec is folded in explicitly via `taskSpecPath`.
|
|
283
|
+
# (e) task path + priority resolution; fail-closed on an unresolvable spec (0751 R2, 0710 R4).
|
|
391
284
|
- kind: shell
|
|
392
285
|
options:
|
|
393
286
|
# 0710 R4: resolve the spec path, then extract `priority:` from the TASK FILE itself (not the
|
|
@@ -410,6 +303,7 @@ states:
|
|
|
410
303
|
# orphan task (no feature_id) resolves to empty — legitimately omitted via empty-string
|
|
411
304
|
# compatibility; a declared feature whose spec path does not resolve fails closed instead
|
|
412
305
|
# of silently folding a stale spec into the proof.
|
|
306
|
+
# (e) linked feature-spec path resolution; fail-closed when a declared feature does not resolve (0785 R2).
|
|
413
307
|
- kind: shell
|
|
414
308
|
options:
|
|
415
309
|
command: 'mkdir -p .spur/run; FID=$($spurBin task show $wbs --json 2>/dev/null | jq -r ".feature_id // .frontmatter.feature_id // empty" 2>/dev/null); FPATH_OUT=""; if [ -n "$FID" ]; then FPATH_OUT=$($spurBin feature show "$FID" --json | jq -r ".filePath // .path // empty"); if [ -z "$FPATH_OUT" ]; then echo "fail-closed proof chain (0785 R2): feature $FID linked to task $wbs but its spec path did not resolve" >&2; exit 1; fi; fi; printf "%s" "$FPATH_OUT" > ".spur/run/$wbs-featurepath.txt"'
|
|
@@ -425,43 +319,11 @@ states:
|
|
|
425
319
|
var: proofDigest
|
|
426
320
|
taskFile: ${vars.taskSpecPath}
|
|
427
321
|
featureFile: ${vars.featureSpecPath}
|
|
322
|
+
# (d) quality-gate.ts owns the gate; shell only resolves it and fails closed.
|
|
428
323
|
- kind: shell
|
|
429
324
|
options:
|
|
430
325
|
command: >-
|
|
431
|
-
mkdir -p .spur/run &&
|
|
432
|
-
STATUS_FILE=".spur/run/$wbs-test-gate.status" &&
|
|
433
|
-
LOG_FILE=".spur/run/$wbs-test-gate.log" &&
|
|
434
|
-
ATTEMPT_FILE=".spur/run/$wbs-test-fix-attempt" &&
|
|
435
|
-
echo 0 > "$ATTEMPT_FILE" &&
|
|
436
|
-
: > "$LOG_FILE" &&
|
|
437
|
-
gate_attempt=1;
|
|
438
|
-
while [ "$gate_attempt" -le 5 ]; do
|
|
439
|
-
ATTEMPT_LOG="$LOG_FILE.attempt-$gate_attempt";
|
|
440
|
-
sh -c "$qualityGateCmd" > "$ATTEMPT_LOG" 2>&1; gate_rc=$?;
|
|
441
|
-
gate_locked=0;
|
|
442
|
-
grep -Eq 'SQLiteError: database is locked|SQLite database .*is busy|SQLITE_BUSY' "$ATTEMPT_LOG" && gate_locked=1;
|
|
443
|
-
cat "$ATTEMPT_LOG" >> "$LOG_FILE";
|
|
444
|
-
rm -f "$ATTEMPT_LOG";
|
|
445
|
-
if [ "$gate_rc" -eq 0 ] || [ "$gate_locked" -ne 1 ] || [ "$gate_attempt" -ge 5 ]; then break; fi;
|
|
446
|
-
printf 'quality gate: database is locked; retrying (%s/5) in 10s\n' "$gate_attempt" | tee -a "$LOG_FILE";
|
|
447
|
-
sleep 10;
|
|
448
|
-
gate_attempt=$((gate_attempt + 1));
|
|
449
|
-
done &&
|
|
450
|
-
if [ "$gate_rc" -eq 0 ]; then
|
|
451
|
-
printf 'quality gate PASS (attempts: %s; log: %s; bytes: %s)\n' "$gate_attempt" "$LOG_FILE" "$(wc -c < "$LOG_FILE" | tr -d "[:space:]")";
|
|
452
|
-
else
|
|
453
|
-
printf 'quality gate FAIL — last 40 lines follow (full log: %s)\n' "$LOG_FILE";
|
|
454
|
-
tail -n 40 "$LOG_FILE";
|
|
455
|
-
fi &&
|
|
456
|
-
FINDINGS_FILE=".spur/run/$wbs-test-gate.findings" &&
|
|
457
|
-
set +e; grep -oE '[A-Za-z0-9_./-]+\.[A-Za-z]+:[0-9]+' "$LOG_FILE" | sort -u | head -20 | tr '\n' ' ' > "$FINDINGS_FILE"; set -e &&
|
|
458
|
-
if [ "$gate_rc" -eq 0 ]; then
|
|
459
|
-
printf 'PASS\n' > "$STATUS_FILE";
|
|
460
|
-
else
|
|
461
|
-
printf 'FAIL\n' > "$STATUS_FILE";
|
|
462
|
-
fi &&
|
|
463
|
-
printf 'proof-digest: %s\n' "$proofDigest" >> "$LOG_FILE" &&
|
|
464
|
-
exit 0
|
|
326
|
+
mkdir -p .spur/run; if [ -f plugins/sp/scripts/quality-gate.ts ]; then bun plugins/sp/scripts/quality-gate.ts run; elif Q="$(superskill script path sp quality-gate.mjs 2>/dev/null)" && [ -f "$Q" ]; then node "$Q" run; else echo "quality gate failed closed — quality-gate script not found — run 'superskill install sp'" >&2; printf 'FAIL\n' > ".spur/run/$wbs-test-gate.status"; fi; exit 0
|
|
465
327
|
|
|
466
328
|
- id: test-fix
|
|
467
329
|
description: >
|
|
@@ -475,22 +337,12 @@ states:
|
|
|
475
337
|
- kind: shell
|
|
476
338
|
options:
|
|
477
339
|
command: >-
|
|
478
|
-
TASK_POLICY=$($spurBin task show "$wbs" --json |
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
mkdir -p .spur/run &&
|
|
485
|
-
ATTEMPT_FILE=".spur/run/$wbs-test-fix-attempt" &&
|
|
486
|
-
n=$(cat "$ATTEMPT_FILE" 2>/dev/null || echo 0) &&
|
|
487
|
-
printf '%s\n' "$((n + 1))" > "$ATTEMPT_FILE" &&
|
|
488
|
-
if [ -f ".spur/run/$wbs-verdict.json" ]; then
|
|
489
|
-
{ echo '--- verify verdict (remediation input, task 0703 R4) ---';
|
|
490
|
-
cat ".spur/run/$wbs-verdict.json";
|
|
491
|
-
} >> ".spur/run/$wbs-test-gate.log";
|
|
492
|
-
fi;
|
|
493
|
-
exit 0
|
|
340
|
+
TASK_POLICY=$($spurBin task show "$wbs" --json | jq -er '.frontmatter.mutationPolicy // ([.content | scan("(?m)^mutationPolicy:[ \t]*([^ \r\n]+)[ \t]*$") | .[0]] | if length == 0 then "code" elif length == 1 then .[0] else "ambiguous" end)') || exit 1; [ "$mutationPolicy:$TASK_POLICY" = "code:code" ] || { echo "test-fix: mutation policy forbids automatic code repair (run=$mutationPolicy, task=$TASK_POLICY) — gate failure preserved, no agent dispatched" >&2; exit 1; }
|
|
341
|
+
# (e) fixall attempt counter and verdict hand-off to the remediation log.
|
|
342
|
+
- kind: shell
|
|
343
|
+
options:
|
|
344
|
+
command: >-
|
|
345
|
+
mkdir -p .spur/run; A=".spur/run/$wbs-test-fix-attempt"; n=$(cat "$A" 2>/dev/null || echo 0); printf '%s\n' "$((n + 1))" > "$A"; [ ! -f ".spur/run/$wbs-verdict.json" ] || { echo '--- verify verdict (remediation input, task 0703 R4) ---'; cat ".spur/run/$wbs-verdict.json"; } >> ".spur/run/$wbs-test-gate.log"; exit 0
|
|
494
346
|
# R3 (0482): project the extracted gate anchors into a var so the dispatch input
|
|
495
347
|
# can NAME the failing file:line, not merely point at a log. A vars template cannot
|
|
496
348
|
# run a shell, so the read is an action, not an inline `$(cat ...)` substitution.
|
|
@@ -507,6 +359,11 @@ states:
|
|
|
507
359
|
# the full-context escape hatch when the digest is not enough.
|
|
508
360
|
input: /sp:dev-fixall "${vars.qualityGateCmd}" --gate-log .spur/run/${vars.wbs}-test-gate.log --findings "${vars.gateFindings}"
|
|
509
361
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
362
|
+
# The captured remediation answer is the run's dispatch evidence (0823 R1):
|
|
363
|
+
# expectFile fails the step when no answer was captured. Quality stays with
|
|
364
|
+
# the review proof digest and the verify verdict.
|
|
365
|
+
answerFile: .spur/run/${vars.__runId}-test-fix-answer.txt
|
|
366
|
+
expectFile: .spur/run/${vars.__runId}-test-fix-answer.txt
|
|
510
367
|
# 0706 R6: bounded remediation hop — unattended and tree-mutating,
|
|
511
368
|
# so it declares the same minimum requirements as `implement`.
|
|
512
369
|
requiresCapabilities:
|
|
@@ -533,51 +390,12 @@ states:
|
|
|
533
390
|
# a green probe (or empty gateProbeCmd) falls through to the full-gate loop unchanged.
|
|
534
391
|
# Only the full gate writes PASS, so the `test-recheck → review` guard (reads PASS)
|
|
535
392
|
# still means a full green qualityGateCmd ran — invariant preserved by construction.
|
|
393
|
+
# (d) quality-gate.ts owns the recheck; shell only resolves it and fails closed
|
|
394
|
+
# (0587 R3 probe-then-full: a red gateProbeCmd is the recheck verdict, the full gate is skipped).
|
|
536
395
|
- kind: shell
|
|
537
396
|
options:
|
|
538
397
|
command: >-
|
|
539
|
-
mkdir -p .spur/run &&
|
|
540
|
-
STATUS_FILE=".spur/run/$wbs-test-gate.status" &&
|
|
541
|
-
LOG_FILE=".spur/run/$wbs-test-gate.log" &&
|
|
542
|
-
FINDINGS_FILE=".spur/run/$wbs-test-gate.findings" &&
|
|
543
|
-
: > "$LOG_FILE" &&
|
|
544
|
-
probe_rc=0;
|
|
545
|
-
if [ -n "$gateProbeCmd" ]; then
|
|
546
|
-
sh -c "$gateProbeCmd" > "$LOG_FILE.probe" 2>&1; probe_rc=$?;
|
|
547
|
-
cat "$LOG_FILE.probe" >> "$LOG_FILE";
|
|
548
|
-
rm -f "$LOG_FILE.probe";
|
|
549
|
-
fi;
|
|
550
|
-
if [ "$probe_rc" -ne 0 ]; then
|
|
551
|
-
gate_rc=$probe_rc;
|
|
552
|
-
else
|
|
553
|
-
gate_attempt=1;
|
|
554
|
-
while [ "$gate_attempt" -le 5 ]; do
|
|
555
|
-
ATTEMPT_LOG="$LOG_FILE.attempt-$gate_attempt";
|
|
556
|
-
sh -c "$qualityGateCmd" > "$ATTEMPT_LOG" 2>&1; gate_rc=$?;
|
|
557
|
-
gate_locked=0;
|
|
558
|
-
grep -Eq 'SQLiteError: database is locked|SQLite database .*is busy|SQLITE_BUSY' "$ATTEMPT_LOG" && gate_locked=1;
|
|
559
|
-
cat "$ATTEMPT_LOG" >> "$LOG_FILE";
|
|
560
|
-
rm -f "$ATTEMPT_LOG";
|
|
561
|
-
if [ "$gate_rc" -eq 0 ] || [ "$gate_locked" -ne 1 ] || [ "$gate_attempt" -ge 5 ]; then break; fi;
|
|
562
|
-
printf 'quality gate: database is locked; retrying (%s/5) in 10s\n' "$gate_attempt" | tee -a "$LOG_FILE";
|
|
563
|
-
sleep 10;
|
|
564
|
-
gate_attempt=$((gate_attempt + 1));
|
|
565
|
-
done;
|
|
566
|
-
fi &&
|
|
567
|
-
if [ "$gate_rc" -eq 0 ]; then
|
|
568
|
-
printf 'quality gate PASS (attempts: %s; log: %s; bytes: %s)\n' "$gate_attempt" "$LOG_FILE" "$(wc -c < "$LOG_FILE" | tr -d "[:space:]")";
|
|
569
|
-
else
|
|
570
|
-
printf 'quality gate FAIL — last 40 lines follow (full log: %s)\n' "$LOG_FILE";
|
|
571
|
-
tail -n 40 "$LOG_FILE";
|
|
572
|
-
fi &&
|
|
573
|
-
set +e; grep -oE '[A-Za-z0-9_./-]+\.[A-Za-z]+:[0-9]+' "$LOG_FILE" | sort -u | head -20 | tr '\n' ' ' > "$FINDINGS_FILE"; set -e &&
|
|
574
|
-
if [ "$gate_rc" -eq 0 ]; then
|
|
575
|
-
printf 'PASS\n' > "$STATUS_FILE";
|
|
576
|
-
else
|
|
577
|
-
printf 'FAIL\n' > "$STATUS_FILE";
|
|
578
|
-
fi &&
|
|
579
|
-
printf 'proof-digest: %s\n' "$proofDigest" >> "$LOG_FILE" &&
|
|
580
|
-
exit 0
|
|
398
|
+
mkdir -p .spur/run; if [ -f plugins/sp/scripts/quality-gate.ts ]; then bun plugins/sp/scripts/quality-gate.ts recheck; elif Q="$(superskill script path sp quality-gate.mjs 2>/dev/null)" && [ -f "$Q" ]; then node "$Q" recheck; else echo "quality gate failed closed — quality-gate script not found — run 'superskill install sp'" >&2; printf 'FAIL\n' > ".spur/run/$wbs-test-gate.status"; fi; exit 0
|
|
581
399
|
|
|
582
400
|
- id: review
|
|
583
401
|
description: Three-dimensional code review via /sp:dev-review (functional requirements traceability + SECUA framework (Security, Efficiency, Correctness, Usability, Architecture) + architecture depth), findings written to `## Review`.
|
|
@@ -592,6 +410,11 @@ states:
|
|
|
592
410
|
# fail-closed) that a P0/P1 task's review resolves a DIFFERENT executor
|
|
593
411
|
# spec than the implement stage recorded in __agentRouting_implement.
|
|
594
412
|
input: /sp:dev-review ${vars.wbs} --auto
|
|
413
|
+
# The captured review answer is the run's dispatch evidence (0823 R1):
|
|
414
|
+
# expectFile fails the step when no answer was captured. Quality stays with
|
|
415
|
+
# the review proof digest and the verify verdict.
|
|
416
|
+
answerFile: .spur/run/${vars.__runId}-review-answer.txt
|
|
417
|
+
expectFile: .spur/run/${vars.__runId}-review-answer.txt
|
|
595
418
|
# Declared Layer-1 role (0538 R2/0710 R7): routing reason; no executor pin — role routing feeds the distinctness gate.
|
|
596
419
|
role: reviewer
|
|
597
420
|
freshSession: true
|
|
@@ -663,6 +486,7 @@ states:
|
|
|
663
486
|
# evidence-row identity, before the verdict derivation reads it.
|
|
664
487
|
# Hard action: a malformed answer halts the sequence here instead of
|
|
665
488
|
# poisoning the verdict parse downstream.
|
|
489
|
+
# (d) verify-answer-lint.ts owns the answer lint; shell only resolves it.
|
|
666
490
|
- kind: shell
|
|
667
491
|
options:
|
|
668
492
|
command: >-
|
|
@@ -679,43 +503,12 @@ states:
|
|
|
679
503
|
- kind: shell
|
|
680
504
|
options:
|
|
681
505
|
command: "$spurBin task verdict $wbs --from-answer .spur/run/$wbs-verify-answer.txt"
|
|
682
|
-
#
|
|
683
|
-
#
|
|
684
|
-
# the SAME digest value (prose asserting proof validity is insufficient). `runId` closes
|
|
685
|
-
# 0730 §B.2 (task 0757 R4): without it the verified-outcome fold has to accept ANY linked
|
|
686
|
-
# run as certifying, so a dry-run probe linked to the same wbs reads as proof of completion.
|
|
687
|
-
# `definitionDigest` closes 0759 R5: the record binds to the certifying run AND the exact
|
|
688
|
-
# workflow definition it executed — a stale-definition resume or a definition edited
|
|
689
|
-
# between run and record cannot certify silently. `__definitionDigest` is injected at run
|
|
690
|
-
# start (workflow-service.ts) and equals the digest stamped on the run row (task 0603).
|
|
691
|
-
# 0785 R4: review is stamped `completed` only when the run-scoped marker written by the
|
|
692
|
-
# review stage equals the current digest; otherwise `skipped` — an unexecuted review is
|
|
693
|
-
# never reported completed, and the verify→record guard plus the bound record registration
|
|
694
|
-
# refuse a skipped review (the D9 fast route stays dormant, honestly).
|
|
695
|
-
# Also keeps the flat
|
|
696
|
-
# `proof-input-digest` check row for consumers that read `checks[]`. Soft action + hard
|
|
697
|
-
# guard: a missing/malformed stamp fails the `verify → record` guard below, not this step.
|
|
506
|
+
# (e) one jq mutation binds the verdict to the proof digest (0703 R3; runId 0730 §B.2 /
|
|
507
|
+
# 0757 R4; definitionDigest 0759 R5; honest review stamp 0785 R4; soft action + hard guard).
|
|
698
508
|
- kind: shell
|
|
699
509
|
options:
|
|
700
|
-
# One folded line: every content line sits at the block's base indentation so
|
|
701
|
-
# `>-` joins them with spaces. A more-indented continuation would preserve a
|
|
702
|
-
# literal newline and split the jq invocation from its program argument.
|
|
703
510
|
command: >-
|
|
704
|
-
V=".spur/run/$wbs-verdict.json";
|
|
705
|
-
RV="skipped";
|
|
706
|
-
if [ -f ".spur/run/$__runId-review-proof.digest" ] && [ "$(cat ".spur/run/$__runId-review-proof.digest")" = "$proofDigest" ]; then
|
|
707
|
-
RV="completed";
|
|
708
|
-
fi;
|
|
709
|
-
if [ -f "$V" ] && [ -n "$proofDigest" ]; then
|
|
710
|
-
jq --arg d "$proofDigest" --arg r "$__runId" --arg dd "$__definitionDigest" --arg rv "$RV" --arg g "$(cat .spur/run/$wbs-test-gate.status 2>/dev/null || echo UNKNOWN)"
|
|
711
|
-
'. + {proof: {digest: $d, runId: $r, definitionDigest: $dd, capturePoint: "quality-gate-entry", stages: {
|
|
712
|
-
qualityGate: {status: $g, digest: $d},
|
|
713
|
-
review: {status: $rv, digest: $d},
|
|
714
|
-
verification: {status: .verdict, digest: $d}}}}
|
|
715
|
-
| .checks += [{name: "proof-input-digest", status: "pass", evidence: $d}]'
|
|
716
|
-
"$V" > "$V.tmp" && mv "$V.tmp" "$V";
|
|
717
|
-
fi;
|
|
718
|
-
exit 0
|
|
511
|
+
V=".spur/run/$wbs-verdict.json"; [ -f "$V" ] && [ -n "$proofDigest" ] || exit 0; jq --arg d "$proofDigest" --arg r "$__runId" --arg dd "$__definitionDigest" --arg rp "$(cat .spur/run/$__runId-review-proof.digest 2>/dev/null)" --arg g "$(cat .spur/run/$wbs-test-gate.status 2>/dev/null || echo UNKNOWN)" '. + {proof: {digest: $d, runId: $r, definitionDigest: $dd, capturePoint: "quality-gate-entry", stages: {qualityGate: {status: $g, digest: $d}, review: {status: (if $rp == $d then "completed" else "skipped" end), digest: $d}, verification: {status: .verdict, digest: $d}}}} | .checks += [{name: "proof-input-digest", status: "pass", evidence: $d}]' "$V" > "$V.tmp" && mv "$V.tmp" "$V"; exit 0
|
|
719
512
|
|
|
720
513
|
- id: record
|
|
721
514
|
description: >
|
|
@@ -748,46 +541,26 @@ states:
|
|
|
748
541
|
proofBinding: current
|
|
749
542
|
taskFile: ${vars.taskSpecPath}
|
|
750
543
|
featureFile: ${vars.featureSpecPath}
|
|
544
|
+
# (c) command.gate owns the record write with classified transient retry.
|
|
545
|
+
- kind: command.gate
|
|
546
|
+
options:
|
|
547
|
+
id: task-record-transition
|
|
548
|
+
executable: "${vars.spurBin}"
|
|
549
|
+
args: ["task", "record", "${vars.wbs}", "--solution-from-diff", "--transition", "testing"]
|
|
550
|
+
retry:
|
|
551
|
+
maxAttempts: 2
|
|
552
|
+
delayMs: 2000
|
|
553
|
+
on: ["sqlite-busy", "ENOENT", "EBUSY", "ENOTEMPTY"]
|
|
554
|
+
resultFile: .spur/run/${vars.__runId}-record-transition.status
|
|
555
|
+
softFail: false
|
|
556
|
+
timeoutMs: 120000
|
|
557
|
+
# (d) feature-sync-bounded.ts owns the sync; shell adds the orphan note and fallbacks
|
|
558
|
+
# (0411 retry-suppression; 0328 / ADR-0322). Best-effort `exit 0` — feature status sync is a
|
|
559
|
+
# follow-up, not a completion gate; `record → done` runs `spur task check`.
|
|
751
560
|
- kind: shell
|
|
752
561
|
options:
|
|
753
562
|
command: >-
|
|
754
|
-
|
|
755
|
-
RETRY_OUTPUT=$("$@" 2>&1); RETRY_RC=$?;
|
|
756
|
-
printf '%s\n' "$RETRY_OUTPUT";
|
|
757
|
-
if [ "$RETRY_RC" -eq 0 ]; then return 0; fi;
|
|
758
|
-
if ! printf '%s' "$RETRY_OUTPUT" | grep -Eq 'ENOENT|EBUSY|ENOTEMPTY|database is locked|SQLite database .*is busy|SQLITE_BUSY'; then return "$RETRY_RC"; fi;
|
|
759
|
-
sleep 2;
|
|
760
|
-
RETRY_SECOND_OUTPUT=$("$@" 2>&1); RETRY_RC=$?;
|
|
761
|
-
printf '%s\n' "$RETRY_SECOND_OUTPUT";
|
|
762
|
-
if [ "$RETRY_RC" -ne 0 ] && printf '%s\n%s' "$RETRY_OUTPUT" "$RETRY_SECOND_OUTPUT" | grep -Eq 'ENOENT|EBUSY|ENOTEMPTY'; then
|
|
763
|
-
echo "node_modules link/dependency broken — run bun install and retry";
|
|
764
|
-
fi;
|
|
765
|
-
return "$RETRY_RC";
|
|
766
|
-
};
|
|
767
|
-
retry_transient $spurBin task record "$wbs" --solution-from-diff --transition testing
|
|
768
|
-
# Best-effort, and portable across layouts. `feature-sync-bounded.ts` is a
|
|
769
|
-
# Spur-monorepo path — `spur init` never scaffolds `plugins/sp/`, so a seeded
|
|
770
|
-
# project must fall back to the plain `spur feature sync` verb rather than
|
|
771
|
-
# abort a run that already produced a PASS verdict. Trailing `exit 0` keeps the
|
|
772
|
-
# whole step advisory for the same reason (feature status sync is a follow-up,
|
|
773
|
-
# not a completion gate — `record → done` runs `spur task check`).
|
|
774
|
-
- kind: shell
|
|
775
|
-
options:
|
|
776
|
-
command: >-
|
|
777
|
-
FID=$($spurBin task show $wbs --json 2>/dev/null |
|
|
778
|
-
jq -r ".feature_id // .frontmatter.feature_id // empty" 2>/dev/null);
|
|
779
|
-
if [ -n "$FID" ]; then
|
|
780
|
-
if [ -f plugins/sp/scripts/feature-sync-bounded.ts ]; then
|
|
781
|
-
bun plugins/sp/scripts/feature-sync-bounded.ts "$FID" --spur-bin "$spurBin" --json;
|
|
782
|
-
elif SYNC_MJS="$(superskill script path sp feature-sync-bounded.mjs 2>/dev/null)" && [ -f "$SYNC_MJS" ]; then
|
|
783
|
-
node "$SYNC_MJS" "$FID" --spur-bin "$spurBin" --json;
|
|
784
|
-
else
|
|
785
|
-
$spurBin feature sync "$FID" --json;
|
|
786
|
-
fi;
|
|
787
|
-
else
|
|
788
|
-
echo "Orphan task $wbs — no feature_id linked; proposal: consider linking to a parent feature." >> ".spur/run/$wbs-report.txt";
|
|
789
|
-
fi;
|
|
790
|
-
exit 0
|
|
563
|
+
FID=$($spurBin task show $wbs --json 2>/dev/null | jq -r '.feature_id // .frontmatter.feature_id // empty' 2>/dev/null); if [ -z "$FID" ]; then echo "Orphan task $wbs — no feature_id linked — proposal: consider linking to a parent feature." >> ".spur/run/$wbs-report.txt"; elif [ -f plugins/sp/scripts/feature-sync-bounded.ts ]; then bun plugins/sp/scripts/feature-sync-bounded.ts "$FID" --spur-bin "$spurBin" --json; elif M="$(superskill script path sp feature-sync-bounded.mjs 2>/dev/null)" && [ -f "$M" ]; then node "$M" "$FID" --spur-bin "$spurBin" --json; else $spurBin feature sync "$FID" --json; fi; exit 0
|
|
791
564
|
|
|
792
565
|
- id: done
|
|
793
566
|
description: >
|
|
@@ -795,38 +568,27 @@ states:
|
|
|
795
568
|
guard runs `spur task check` before certifying; a genuinely non-compliant
|
|
796
569
|
task routes to `failed` instead of a silent bad `done`.
|
|
797
570
|
onEnter:
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
fi;
|
|
812
|
-
return "$RETRY_RC";
|
|
813
|
-
};
|
|
814
|
-
retry_transient $spurBin task update "$wbs" done --no-lifecycle
|
|
571
|
+
# (c) command.gate owns the done transition with classified transient retry.
|
|
572
|
+
- kind: command.gate
|
|
573
|
+
options:
|
|
574
|
+
id: task-done-transition
|
|
575
|
+
executable: "${vars.spurBin}"
|
|
576
|
+
args: ["task", "update", "${vars.wbs}", "done", "--no-lifecycle"]
|
|
577
|
+
retry:
|
|
578
|
+
maxAttempts: 2
|
|
579
|
+
delayMs: 2000
|
|
580
|
+
on: ["sqlite-busy", "ENOENT", "EBUSY", "ENOTEMPTY"]
|
|
581
|
+
resultFile: .spur/run/${vars.__runId}-done-transition.status
|
|
582
|
+
softFail: false
|
|
583
|
+
timeoutMs: 120000
|
|
815
584
|
- kind: note
|
|
816
585
|
options:
|
|
817
586
|
message: "Pipeline complete for task ${vars.wbs} (done gate cleared)."
|
|
818
|
-
#
|
|
819
|
-
# run-id/artifact binding corrected by 0784 R4) — canonical frontmatter
|
|
820
|
-
# contract; mirrors the Session Checkpoint Convention in
|
|
821
|
-
# plugins/sp/skills/spur-dev/references/cross-cutting.md.
|
|
822
|
-
# Advisory only: the task file and the persisted run row stay authoritative.
|
|
587
|
+
# (e) advisory terminal checkpoint, one printf (0711 R1–R3; binding corrected 0784 R4).
|
|
823
588
|
- kind: shell
|
|
824
589
|
options:
|
|
825
|
-
# Single logical line: the composition-baseline argument-split lint flags any
|
|
826
|
-
# command whose continuation lines look like argument lists (heredocs with
|
|
827
|
-
# `- item` entries trip it), so the checkpoint body is one printf with \n escapes.
|
|
828
590
|
command: >-
|
|
829
|
-
mkdir -p .spur/memory/sessions; CP_TS="$(date -u +%Y-%m-%dT%H:%M:%SZ)"; CP_COMMIT="$(git rev-parse HEAD 2>/dev/null || echo unknown)";
|
|
591
|
+
mkdir -p .spur/memory/sessions; CP_TS="$(date -u +%Y-%m-%dT%H:%M:%SZ)"; CP_COMMIT="$(git rev-parse HEAD 2>/dev/null || echo unknown)"; printf '%s\n' '---' 'schema_version: 1' "session_id: $(date -u +%Y-%m-%d)-$wbs" 'workflow: task-pipeline' "run_id: $__runId" "task_wbs: $wbs" 'feature_id: ""' 'phase: done' 'status: done' 'last_gate: record' "source_commit: $CP_COMMIT" "digest: $proofDigest" "generated_at: $CP_TS" "updated_at: $CP_TS" "next_action: none - task $wbs complete (terminal; advisory only)" 'artifacts:' " - .spur/run/$wbs-verdict.json" " - .spur/run/$wbs-test-gate.log" '---' '' '## Session Notes' '' "Terminal checkpoint for task $wbs (task-pipeline done)." 'Advisory only; the task file is authoritative.' > .spur/memory/sessions/$wbs-checkpoint.md; exit 0
|
|
830
592
|
|
|
831
593
|
- id: failed
|
|
832
594
|
description: >
|
|
@@ -981,18 +743,12 @@ transitions:
|
|
|
981
743
|
to: record
|
|
982
744
|
description: Verification verdict is PASS and its proof block names the captured digest on every stage — record results and proceed to done.
|
|
983
745
|
guard:
|
|
746
|
+
# (one jq -e predicate) PASS verdict; proof, quality-gate, review and verification digests
|
|
747
|
+
# equal to proofDigest; review completed; matching run id and definition digest (0703 R5, 0785 R4).
|
|
984
748
|
kind: shell
|
|
985
749
|
options:
|
|
986
750
|
command: >-
|
|
987
|
-
|
|
988
|
-
test "$(jq -r .verdict "$V" 2>/dev/null)" = PASS &&
|
|
989
|
-
test "$(jq -r '.proof.digest // ""' "$V" 2>/dev/null)" = "$proofDigest" &&
|
|
990
|
-
test "$(jq -r '.proof.stages.qualityGate.digest // ""' "$V" 2>/dev/null)" = "$proofDigest" &&
|
|
991
|
-
test "$(jq -r '.proof.stages.review.digest // ""' "$V" 2>/dev/null)" = "$proofDigest" &&
|
|
992
|
-
test "$(jq -r '.proof.stages.review.status // ""' "$V" 2>/dev/null)" = "completed" &&
|
|
993
|
-
test "$(jq -r '.proof.stages.verification.digest // ""' "$V" 2>/dev/null)" = "$proofDigest" &&
|
|
994
|
-
test "$(jq -r '.proof.runId // ""' "$V" 2>/dev/null)" = "$__runId" &&
|
|
995
|
-
test "$(jq -r '.proof.definitionDigest // ""' "$V" 2>/dev/null)" = "$__definitionDigest"
|
|
751
|
+
jq -e --arg d "$proofDigest" --arg r "$__runId" --arg dd "$__definitionDigest" '.verdict == "PASS" and (.proof.digest // "") == $d and (.proof.stages.qualityGate.digest // "") == $d and (.proof.stages.review.digest // "") == $d and (.proof.stages.review.status // "") == "completed" and (.proof.stages.verification.digest // "") == $d and (.proof.runId // "") == $r and (.proof.definitionDigest // "") == $dd' ".spur/run/$wbs-verdict.json" >/dev/null 2>&1
|
|
996
752
|
- from: verify
|
|
997
753
|
to: test-fix
|
|
998
754
|
description: >-
|
|
@@ -1000,6 +756,7 @@ transitions:
|
|
|
1000
756
|
bounded remediation hop (task 0703 R4); the chain re-enters quality → review → verify on a
|
|
1001
757
|
freshly captured digest. Never reached on PASS: remediation cannot follow certification.
|
|
1002
758
|
guard:
|
|
759
|
+
# (e) remediation route: verdict not PASS and fix attempts below qualityGateMaxFixAttempts (0703 R4).
|
|
1003
760
|
kind: shell
|
|
1004
761
|
options:
|
|
1005
762
|
command: >-
|