@gobing-ai/spur 0.3.80 → 0.3.81
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 +29 -18
- package/config/config.global.yaml +10 -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/transition-shims.json +7 -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 +10 -1
- 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/quality-gate.mjs +165 -0
- package/plugins/sp/scripts/quality-gate.ts +217 -0
- 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/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/references/agent.md +56 -14
- package/plugins/sp/skills/spur-cli/references/message.md +30 -3
- package/plugins/sp/skills/spur-cli/references/projects.md +45 -1
- package/plugins/sp/skills/spur-cli/references/self.md +5 -4
- package/plugins/sp/skills/spur-cli/references/serve.md +5 -4
- package/plugins/sp/skills/spur-cli/references/tasks.md +1 -1
- package/plugins/sp/skills/spur-cli/references/team.md +21 -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/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 +49 -0
- package/spur.js +46754 -44121
- package/web/_astro/{BoardApp.CHQ1lycZ.js → BoardApp.B1U26g3I.js} +97 -95
- package/web/_astro/BoardApp.Csgyg-lS.js +1 -0
- package/web/_astro/{TaskDetail.GKfQJ60c.js → TaskDetail.DwPqpq7v.js} +1 -1
- package/web/_astro/{arc.DWEtA3Tx.js → arc.CweZEjN2.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.DB42oWmP.js → architectureDiagram-3BPJPVTR.D89pbDuv.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.rhv-zNQV.js → blockDiagram-GPEHLZMM.BOuTeEpX.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.Ci4-4VvY.js → c4Diagram-AAUBKEIU.CASbkWZF.js} +1 -1
- package/web/_astro/channel.Cx6sXxhq.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.Cc9veUgf.js → chunk-2J33WTMH.BKQYtOvY.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.Bec9c4eI.js → chunk-4BX2VUAB.9sHLdMtG.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.DoV8S1iB.js → chunk-55IACEB6.wOLXWlPs.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.DwR-Qlyj.js → chunk-727SXJPM.DovFbwg3.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.ND_a81WY.js → chunk-AQP2D5EJ.B1Weod1X.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.Wv_jwG48.js → chunk-FMBD7UC4.TEMS04st.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.CXKXCMmp.js → chunk-ND2GUHAM.Cp8VT1wQ.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.nkaoNYQq.js → chunk-QZHKN3VN.BzATdEcP.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.cMQcVlQu.js → classDiagram-4FO5ZUOK.C9BOCfAO.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.cMQcVlQu.js → classDiagram-v2-Q7XG4LA2.C9BOCfAO.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.OaDJ7Mr2.js → cose-bilkent-S5V4N54A.DUnr4UAw.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.Chi8IphF.js → cynefin-OW5HDTMX.rYq5uM3D.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.CWeNKe3I.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.DRvxlVS7.js → diagram-2AECGRRQ.DCkfls10.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.CnYvNdwA.js → diagram-5GNKFQAL.D5U4JCka.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.CpLpMw5R.js → diagram-KO2AKTUF.BZJgqaqG.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.JTb78qUA.js → diagram-LMA3HP47.DoMeHvPR.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.Bk-1jDIb.js → diagram-OG6HWLK6.B50qwwWX.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.D8hN9GZq.js → erDiagram-TEJ5UH35.DdGPG6LK.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.-6zQr6m5.js → flowDiagram-I6XJVG4X.QP2MJ12u.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.DboLQ9ca.js → ganttDiagram-6RSMTGT7.BI6LgKSy.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.4tYvJKGR.js → gitGraphDiagram-PVQCEYII.npPZiC2G.js} +1 -1
- package/web/_astro/index.DayyIngm.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.Bd9rXpsB.js → infoDiagram-5YYISTIA.DCJCBVbp.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.CvMoaf67.js → ishikawaDiagram-YF4QCWOH.BMLV-3I1.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.Ccy1CA7y.js → journeyDiagram-JHISSGLW.LE58crde.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.0MaMqHNS.js → kanban-definition-UN3LZRKU.BPbz8rH9.js} +1 -1
- package/web/_astro/{linear.CHXgcIbN.js → linear.DhZaBtYh.js} +1 -1
- package/web/_astro/{mermaid.core.Ca-kcelG.js → mermaid.core.BD5-jXum.js} +6 -6
- package/web/_astro/{mindmap-definition-RKZ34NQL.BUIDlHa0.js → mindmap-definition-RKZ34NQL.MTJyrQ65.js} +1 -1
- package/web/_astro/ordinal.BYWQX77i.js +1 -0
- package/web/_astro/{pieDiagram-4H26LBE5.2dX3CU1s.js → pieDiagram-4H26LBE5.BrDhDvIS.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.B3LBlRiv.js → quadrantDiagram-W4KKPZXB.71d73_5N.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.X12I2uNx.js → requirementDiagram-4Y6WPE33.Bga6UF-z.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.BXohIHqx.js → sankeyDiagram-5OEKKPKP.BnHs4K82.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.C37ZIUzg.js → sequenceDiagram-3UESZ5HK.DsfY2gnj.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.BRgz317z.js → stateDiagram-AJRCARHV.DvsTSc9a.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.7VYSXN9-.js → stateDiagram-v2-BHNVJYJU.DxzzmHUR.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.BVNz_HiN.js → timeline-definition-PNZ67QCA.4ZuQmOTt.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.CHVDkPX4.js → vennDiagram-CIIHVFJN.Ck5Q86SG.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.EQQ_qT9v.js → wardleyDiagram-YWT4CUSO.BK7k2hXr.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.DrAT9WoP.js → xychartDiagram-2RQKCTM6.DfCrgauK.js} +1 -1
- package/web/index.html +2 -2
- 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
|
@@ -33,22 +33,15 @@ include:
|
|
|
33
33
|
- "packages/**/tests/**/*.test.ts"
|
|
34
34
|
- "packages/**/tests/**/*.test.tsx"
|
|
35
35
|
exclude:
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
|
|
41
|
-
# These files use beforeEach restoreMock() atop the shared baseline — the mock.module
|
|
42
|
-
# call is in a runtime function, not at module scope, and always registers the full surface.
|
|
36
|
+
# These three files mock.module() first-party modules and keep the mock inside a
|
|
37
|
+
# runtime function guarded by beforeEach restoreMock() atop the shared full-surface
|
|
38
|
+
# baseline (apps/web/tests/test-helpers/rpc-client-mock.ts, itself unflagged: it
|
|
39
|
+
# only documents the surface in comments). Remaining mock.module() hits in the
|
|
40
|
+
# test suite were verified stale or third-party-only and have been de-listed.
|
|
43
41
|
- "apps/web/tests/modules/task-kanban/board.test.tsx"
|
|
44
|
-
- "apps/web/tests/modules/task-kanban/components.test.tsx"
|
|
45
42
|
- "apps/web/tests/modules/task-kanban/new-task-panel.test.tsx"
|
|
46
|
-
- "apps/web/tests/modules/features/components.test.tsx"
|
|
47
43
|
- "apps/web/tests/modules/task-kanban/task-detail.test.tsx"
|
|
48
|
-
- "apps/web/tests/modules/
|
|
49
|
-
# index.test.tsx no longer mocks useTasks/useTaskParams/TaskDetail; useTasks.test.ts
|
|
50
|
-
# uses custom listFn (isolated store), not mock.module for first-party modules.
|
|
51
|
-
- "apps/web/tests/modules/task-kanban/useTasks.test.ts"
|
|
44
|
+
- "apps/web/tests/modules/projects/WorkView.test.tsx"
|
|
52
45
|
rules:
|
|
53
46
|
- id: no-leaky-mock-module
|
|
54
47
|
description: >
|
|
@@ -34,9 +34,6 @@ include:
|
|
|
34
34
|
- "apps/**/tests/**/*.test.tsx"
|
|
35
35
|
- "packages/**/tests/**/*.test.ts"
|
|
36
36
|
- "packages/**/tests/**/*.test.tsx"
|
|
37
|
-
exclude:
|
|
38
|
-
- "**/node_modules/**"
|
|
39
|
-
- "**/dist/**"
|
|
40
37
|
# All known side-effect modules are resolved. When a new one is discovered,
|
|
41
38
|
# exclude files that already mock the dependency before importing it.
|
|
42
39
|
|
|
@@ -13,9 +13,6 @@ $schema: "@gobing-ai/spur/schemas/rule-file.schema.json"
|
|
|
13
13
|
include:
|
|
14
14
|
- "apps/web/tests/**/*.test.ts"
|
|
15
15
|
- "apps/web/tests/**/*.test.tsx"
|
|
16
|
-
exclude:
|
|
17
|
-
- "**/node_modules/**"
|
|
18
|
-
- "**/dist/**"
|
|
19
16
|
|
|
20
17
|
rules:
|
|
21
18
|
- id: prefer-accessible-role-for-button-queries
|
|
@@ -27,8 +27,6 @@ rules:
|
|
|
27
27
|
- "apps/web/src/**/*.tsx"
|
|
28
28
|
exclude:
|
|
29
29
|
- "apps/web/src/ui.ts"
|
|
30
|
-
- "**/tests/**"
|
|
31
|
-
- "**/node_modules/**"
|
|
32
30
|
|
|
33
31
|
- id: no-daisyui-class-leak
|
|
34
32
|
description: >
|
|
@@ -48,7 +46,5 @@ rules:
|
|
|
48
46
|
include:
|
|
49
47
|
- "apps/web/src/**/*.tsx"
|
|
50
48
|
exclude:
|
|
49
|
+
# Sanctioned class-authoring site (Rule B seam).
|
|
51
50
|
- "apps/web/src/components/ui/**"
|
|
52
|
-
- "apps/web/src/ui.ts"
|
|
53
|
-
- "**/tests/**"
|
|
54
|
-
- "**/node_modules/**"
|
|
@@ -15,19 +15,19 @@
|
|
|
15
15
|
"keepsWorking": "a pre-existing agent spec carrying only type (no executor field) still drains: --agent <specId> falls back to the spec's coding-agent type instead of resolving a missing executor",
|
|
16
16
|
"removalCondition": "every spec under .spur/agents/ records an executor field (scan .spur/agents/*.yaml for the key; re-materialize the demo team first)"
|
|
17
17
|
},
|
|
18
|
-
{
|
|
19
|
-
"id": "agent-flag-spec-id",
|
|
20
|
-
"wbs": "0542",
|
|
21
|
-
"file": "apps/cli/src/commands/agent.ts",
|
|
22
|
-
"keepsWorking": "a team spec id passed to --agent <spec-id> still addresses the spec and drives the drain path, warned once by warnAgentSpecIdOnce; --spec <id> is the canonical carrier",
|
|
23
|
-
"removalCondition": "no --agent <spec-id> usage remains in config/workflows/, plugins/sp/, or docs/ (scan for --agent values that match a .spur/agents/ spec id)"
|
|
24
|
-
},
|
|
25
18
|
{
|
|
26
19
|
"id": "agent-default-executor",
|
|
27
20
|
"wbs": "0542",
|
|
28
21
|
"file": "packages/app/src/services/agent-service.ts",
|
|
29
22
|
"keepsWorking": "a configured executor name in agent.default still resolves during the transition, warned once by warnAgentDefaultExecutorOnce; the value domain moved to roles (recommended default: coder)",
|
|
30
23
|
"removalCondition": "no agent.default value names an agent.executors entry (scan .spur/config.yaml and config/config.example.yaml against the agent.executors names)"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "team-noun-retired",
|
|
27
|
+
"wbs": "0848",
|
|
28
|
+
"file": "apps/cli/src/commands/team.ts",
|
|
29
|
+
"keepsWorking": "the `spur team` noun keeps working after its six verbs moved to owning nouns (0848): assign → `spur task update --assignee`, status → `spur agent list --specs`, up → fleet materialization at serve start (`spur projects list --fleet` for --check), down/start/stop → `spur agent stop|start|stop <spec-id>`; a one-time stderr warning (warnTeamNounRetiredOnce) names the replacement for the invoked verb and every verb keeps its previous exit code and output",
|
|
30
|
+
"removalCondition": "no `spur team` invocation remains in config/workflows/, plugins/sp/, scripts/, or docs/, and the cutover window is recorded in docs/features/G64_retire-workspace-inbox-teams-and-spur-team.md"
|
|
31
31
|
}
|
|
32
32
|
]
|
|
33
33
|
}
|
|
@@ -56,6 +56,8 @@ states:
|
|
|
56
56
|
onEnter:
|
|
57
57
|
- kind: shell
|
|
58
58
|
options:
|
|
59
|
+
# (e) 0825: 10 lines — soft probe must run the trusted compound gate via `sh -c`
|
|
60
|
+
# and write the status file the guards read, always exit 0 (0771 R1 contract).
|
|
59
61
|
command: >-
|
|
60
62
|
mkdir -p .spur/run &&
|
|
61
63
|
STATUS_FILE=".spur/run/$__runId-basic-gate.status" &&
|
|
@@ -83,6 +85,8 @@ states:
|
|
|
83
85
|
options:
|
|
84
86
|
agent: ${vars.agent}
|
|
85
87
|
input: /sp:dev-fixall "${vars.qualityGateCmd}"
|
|
88
|
+
answerFile: .spur/run/${vars.__runId}-basic-fix-answer.txt
|
|
89
|
+
expectFile: .spur/run/${vars.__runId}-basic-fix-answer.txt
|
|
86
90
|
# Declared Layer-1 role (0538 R2): routing reason beside the agent: pin.
|
|
87
91
|
role: coder
|
|
88
92
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
@@ -138,12 +138,8 @@ states:
|
|
|
138
138
|
# excluded from the digest's git-tree half, so the spec is folded in via `taskFile`.
|
|
139
139
|
- kind: shell
|
|
140
140
|
options:
|
|
141
|
-
#
|
|
142
|
-
#
|
|
143
|
-
# wbs exits non-zero with a named message. The resolved path folds
|
|
144
|
-
# into the proof digest via `taskFile:` below; a silent miss would
|
|
145
|
-
# degrade the docs-pipeline proof to tree-only (same hazard 0751 R2
|
|
146
|
-
# removed from task-pipeline). (0769: run-scoped capture path.)
|
|
141
|
+
# (e) 0825: 6 lines — fail-closed lookup feeds the verdict/digest file names
|
|
142
|
+
# (0760 R1); a silent miss would degrade the proof to tree-only.
|
|
147
143
|
command: >-
|
|
148
144
|
set -e;
|
|
149
145
|
task_path="$($spurBin task path $wbs --json | jq -r '.path // .filePath // empty')";
|
|
@@ -159,6 +155,8 @@ states:
|
|
|
159
155
|
# folding a stale spec into the proof.
|
|
160
156
|
- kind: shell
|
|
161
157
|
options:
|
|
158
|
+
# (e) 0825: 9 lines — per-check rc capture (task show/feature show/jq) keeps an
|
|
159
|
+
# orphan task and a broken linked feature distinguishable (0785 R2 contract).
|
|
162
160
|
command: >-
|
|
163
161
|
set -e;
|
|
164
162
|
FID="$($spurBin task show $wbs --json 2>/dev/null | jq -r '.feature_id // .frontmatter.feature_id // empty' 2>/dev/null)";
|
|
@@ -191,6 +189,7 @@ states:
|
|
|
191
189
|
role: reviewer
|
|
192
190
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
193
191
|
answerFile: .spur/run/${vars.__runId}-docs-verify-answer.txt
|
|
192
|
+
expectFile: .spur/run/${vars.__runId}-docs-verify-answer.txt
|
|
194
193
|
- kind: shell
|
|
195
194
|
options:
|
|
196
195
|
command: "$spurBin task verdict $wbs --from-answer .spur/run/$__runId-docs-verify-answer.txt"
|
|
@@ -199,6 +198,8 @@ states:
|
|
|
199
198
|
# guard below, not this step.
|
|
200
199
|
- kind: shell
|
|
201
200
|
options:
|
|
201
|
+
# (e) 0825: 6 lines — stamp shell freezes the docs proof digest for the closing
|
|
202
|
+
# guard; the record→done guard compares against this stamp.
|
|
202
203
|
command: >-
|
|
203
204
|
V=".spur/run/$wbs-verdict.json";
|
|
204
205
|
if [ -f "$V" ] && [ -n "$proofDigest" ]; then
|
|
@@ -263,7 +264,7 @@ transitions:
|
|
|
263
264
|
guard:
|
|
264
265
|
kind: shell
|
|
265
266
|
options:
|
|
266
|
-
command: 'test "$(cat .spur/run/$
|
|
267
|
+
command: 'test "$(cat .spur/run/$__runId-docs-precheck.status 2>/dev/null)" = PASS'
|
|
267
268
|
- from: precheck
|
|
268
269
|
to: failed
|
|
269
270
|
description: Precheck FAIL — stop before draft.
|
|
@@ -313,17 +314,13 @@ transitions:
|
|
|
313
314
|
guard:
|
|
314
315
|
kind: shell
|
|
315
316
|
options:
|
|
316
|
-
#
|
|
317
|
-
#
|
|
317
|
+
# (e) 0825: 8-line operand ladder condensed to one jq select — same file, same
|
|
318
|
+
# digest-locked PASS semantics (0769 R4/R5); empty/malformed still fail closed.
|
|
318
319
|
command: >-
|
|
319
320
|
test -n "$proofDigest" &&
|
|
320
321
|
test -n "$proofDigestNow" &&
|
|
321
322
|
test "$proofDigestNow" = "$proofDigest" &&
|
|
322
|
-
test -
|
|
323
|
-
test -n "$(jq -r '.verdict // empty' .spur/run/$wbs-verdict.json 2>/dev/null)" &&
|
|
324
|
-
test "$(jq -r '.verdict // empty' .spur/run/$wbs-verdict.json 2>/dev/null)" = PASS &&
|
|
325
|
-
test -n "$(jq -r '.proof.digest // empty' .spur/run/$wbs-verdict.json 2>/dev/null)" &&
|
|
326
|
-
test "$(jq -r '.proof.digest // empty' .spur/run/$wbs-verdict.json 2>/dev/null)" = "$proofDigest"
|
|
323
|
+
test "$(jq -r --arg d "$proofDigest" 'select(.proof.digest == $d) | .verdict // empty' ".spur/run/$wbs-verdict.json" 2>/dev/null)" = PASS
|
|
327
324
|
- from: verify
|
|
328
325
|
to: failed
|
|
329
326
|
description: Non-PASS verdict, missing/malformed answer, or digest mismatch (R5) — the run
|
|
@@ -338,6 +335,8 @@ transitions:
|
|
|
338
335
|
guard:
|
|
339
336
|
kind: shell
|
|
340
337
|
options:
|
|
338
|
+
# (e) 0825: 5-line guard — captured record PASS + digest-locked persisted verdict
|
|
339
|
+
# must both hold before done; a failed `task record` never converts to success.
|
|
341
340
|
command: >-
|
|
342
341
|
test "$(cat .spur/run/$__runId-docs-record.status 2>/dev/null)" = PASS &&
|
|
343
342
|
test -f ".spur/run/$wbs-verdict.json" &&
|
|
@@ -91,35 +91,11 @@ states:
|
|
|
91
91
|
onEnter:
|
|
92
92
|
- kind: shell
|
|
93
93
|
options:
|
|
94
|
+
# (d) 0825: the 12-command identity/roster program moved into
|
|
95
|
+
# plugins/sp/scripts/feature-dev-precheck.ts (§1.1 owner); same messages,
|
|
96
|
+
# artifacts and soft-fail exit-0 contract, wrapper fails closed.
|
|
94
97
|
command: >-
|
|
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";
|
|
118
|
-
else
|
|
119
|
-
printf '%s' "$(jq -r '[.[] | select(.status == "todo") | .wbs] | sort | join(",")' "$ROSTER_JSON")" > "$TASKS_TXT";
|
|
120
|
-
printf 'PASS\n' > "$PRECHECK_STATUS";
|
|
121
|
-
fi;
|
|
122
|
-
exit 0
|
|
98
|
+
mkdir -p .spur/run; if [ -f plugins/sp/scripts/feature-dev-precheck.ts ]; then bun plugins/sp/scripts/feature-dev-precheck.ts; elif W="$(superskill script path sp feature-dev-precheck.mjs 2>/dev/null)" && [ -f "$W" ]; then node "$W"; else echo "feature-dev precheck failed closed — feature-dev-precheck script not found — run 'superskill install sp'" >&2; printf 'FAIL\n' > ".spur/run/$__runId-feature-dev-precheck.status"; fi; exit 0
|
|
123
99
|
- kind: note
|
|
124
100
|
options:
|
|
125
101
|
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."
|
|
@@ -143,6 +119,8 @@ states:
|
|
|
143
119
|
options:
|
|
144
120
|
agent: ${vars.agent}
|
|
145
121
|
input: /sp:dev-runall --tasks ${vars.featureTaskIds} --auto
|
|
122
|
+
answerFile: .spur/run/${vars.__runId}-feature-dev-runall-answer.txt
|
|
123
|
+
expectFile: .spur/run/${vars.__runId}-feature-dev-runall-answer.txt
|
|
146
124
|
# Declared Layer-1 role (0538 R2): routing reason beside the agent: pin.
|
|
147
125
|
role: planner
|
|
148
126
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
@@ -162,6 +140,8 @@ states:
|
|
|
162
140
|
options:
|
|
163
141
|
agent: ${vars.agent}
|
|
164
142
|
input: /sp:dev-runall --tasks ${vars.featureTaskIds}
|
|
143
|
+
answerFile: .spur/run/${vars.__runId}-feature-dev-runall-answer.txt
|
|
144
|
+
expectFile: .spur/run/${vars.__runId}-feature-dev-runall-answer.txt
|
|
165
145
|
# Declared Layer-1 role (0538 R2): routing reason beside the agent: pin.
|
|
166
146
|
role: planner
|
|
167
147
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
@@ -177,17 +157,10 @@ states:
|
|
|
177
157
|
onEnter:
|
|
178
158
|
- kind: shell
|
|
179
159
|
options:
|
|
160
|
+
# (e) 0825: 9 lines — `feature check --as done` runs EXACTLY ONCE per run (0782 R3)
|
|
161
|
+
# and owns the status write the guards read; split would re-run the check.
|
|
180
162
|
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
|
|
163
|
+
mkdir -p .spur/run; rm -f ".spur/run/$__runId-feature-dev-verify.status"; $spurBin feature check "$featureId" --as done --json > ".spur/run/$__runId-feature-dev-verify.json" 2>&1 && jq -e 'type == "array" and length > 0 and all(.[]; .pass == true)' ".spur/run/$__runId-feature-dev-verify.json" > /dev/null 2>&1 && R=PASS || R=FAIL; printf '%s\n' "$R" > ".spur/run/$__runId-feature-dev-verify.status.tmp" && mv -f ".spur/run/$__runId-feature-dev-verify.status.tmp" ".spur/run/$__runId-feature-dev-verify.status"
|
|
191
164
|
- kind: note
|
|
192
165
|
options:
|
|
193
166
|
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)."
|
|
@@ -211,36 +184,18 @@ states:
|
|
|
211
184
|
`requireCleanReview=true` turns a non-clean COLLECTED verdict (including
|
|
212
185
|
collect FAIL) into the blocking edge declared first below.
|
|
213
186
|
onEnter:
|
|
187
|
+
# (e) 0825: one request action + one collect action (same verdicts, same files,
|
|
188
|
+
# collect gated on the captured head SHA as before) to hold the §1.2 budgets.
|
|
214
189
|
- kind: shell
|
|
215
190
|
options:
|
|
216
191
|
command: >-
|
|
217
|
-
mkdir -p .spur/run &&
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
bun "$(superskill script path sp pr-reviewing.ts)" request --base "$baseBranch" --json
|
|
225
|
-
--status-file "$PR_REQUEST_STATUS" > "$REQUEST_JSON" 2>&1;
|
|
226
|
-
request_rc=$?; set -e &&
|
|
227
|
-
if [ "$request_rc" -eq 0 ]; then
|
|
228
|
-
printf 'PASS\n' > "$STATUS_FILE";
|
|
229
|
-
else
|
|
230
|
-
printf 'FAIL\n' > "$STATUS_FILE";
|
|
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;
|
|
243
|
-
exit 0
|
|
192
|
+
mkdir -p .spur/run; bun "$(superskill script path sp pr-reviewing.ts)" request --base "$baseBranch" --json --status-file ".spur/run/$__runId-integration-review-pr-request.status" > ".spur/run/$__runId-integration-review.json" 2>&1 && printf 'PASS\n' > ".spur/run/$__runId-integration-review.status" || printf 'FAIL\n' > ".spur/run/$__runId-integration-review.status"; exit 0
|
|
193
|
+
- kind: shell
|
|
194
|
+
options:
|
|
195
|
+
# (e) 0825: 8 lines — collect stays head-locked to the request record and records
|
|
196
|
+
# its verdict status file; the rc/status echo is the audible soft-fail contract.
|
|
197
|
+
command: >-
|
|
198
|
+
if H=$(jq -er '.head // empty | select(. != "")' ".spur/run/$__runId-integration-review.json" 2>/dev/null); then bun "$(superskill script path sp pr-reviewing.ts)" collect --head "$H" --json --status-file ".spur/run/$__runId-integration-review-collect.status" > ".spur/run/$__runId-integration-review-collect.json" 2>&1; rc=$?; echo "integration-review: collect rc=$rc status=$(cat ".spur/run/$__runId-integration-review-collect.status" 2>/dev/null || echo missing) — verdict captured to .spur/run/$__runId-integration-review-collect.json" >&2; else echo "integration-review: request result carried no head SHA — collect not run, recorded FAIL" >&2; printf 'FAIL\n' > ".spur/run/$__runId-integration-review-collect.status"; fi; exit 0
|
|
244
199
|
- kind: note
|
|
245
200
|
options:
|
|
246
201
|
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."
|
|
@@ -72,7 +72,10 @@ vars:
|
|
|
72
72
|
agent: "claude"
|
|
73
73
|
spurBin: "spur"
|
|
74
74
|
__runId: ""
|
|
75
|
-
|
|
75
|
+
# 2026-09-12: raised 30m -> 60m. Observed runs on zai/glm-5.3-flash butt against the
|
|
76
|
+
# 30m ceiling (correct exited at exactly 30m00s; a prior run passed at 29m52s) — the
|
|
77
|
+
# enrich/validate/correct stages legitimately need >30m for the 60KB candidate report.
|
|
78
|
+
stepTimeoutMs: "3600000"
|
|
76
79
|
workflowFile: "config/workflows/history-anatomy.yaml"
|
|
77
80
|
contractVersion: "1"
|
|
78
81
|
reportDir: "docs/report"
|
|
@@ -225,6 +228,24 @@ states:
|
|
|
225
228
|
role: reviewer
|
|
226
229
|
expectFile: .spur/run/${vars.__runId}-validation.txt
|
|
227
230
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
231
|
+
- kind: shell
|
|
232
|
+
options:
|
|
233
|
+
# 2026-09-13 verdict-placement normalization: the 0771 guard reads ONLY the
|
|
234
|
+
# final line, but models sometimes lead with `Verdict: PASS` and close with
|
|
235
|
+
# prose — a substantive PASS must not die as a format phantom (observed
|
|
236
|
+
# 2026-09-13 run 94c4d6dc). Moves the verdict line to the end ONLY when
|
|
237
|
+
# exactly one exact-line verdict exists and it is PASS. Any `Verdict: FAIL`
|
|
238
|
+
# line anywhere (or multiple/ambiguous verdict lines) suppresses
|
|
239
|
+
# normalization, so the guard still fails and a real FAIL can never be
|
|
240
|
+
# laundered into a PASS.
|
|
241
|
+
command: >-
|
|
242
|
+
f=.spur/run/$__runId-validation.txt;
|
|
243
|
+
if [ -f "$f" ] && ! tail -n 1 "$f" | grep -qx 'Verdict: PASS'; then
|
|
244
|
+
p=$(grep -cx 'Verdict: PASS' "$f"); q=$(grep -cx 'Verdict: FAIL' "$f");
|
|
245
|
+
if [ "$p" = 1 ] && [ "$q" = 0 ]; then
|
|
246
|
+
grep -vx 'Verdict: PASS' "$f" > "$f.tmp" && printf 'Verdict: PASS\n' >> "$f.tmp" && mv "$f.tmp" "$f";
|
|
247
|
+
fi;
|
|
248
|
+
fi
|
|
228
249
|
- kind: shell
|
|
229
250
|
options:
|
|
230
251
|
command: >-
|