@gobing-ai/spur 0.3.98 → 0.4.0
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.global.yaml +17 -0
- package/config/decisions/task-pipeline.yaml +55 -0
- package/config/plugin-scripts.json +15 -0
- package/config/rules/boundary/sp-runtime-path.yaml +2 -0
- package/config/rules/quality/coverage-gate.yaml +5 -3
- package/config/rules/strict/runtime-boundaries.yaml +11 -0
- package/config/rules/structure/protected-files.yaml +3 -0
- package/config/templates/task/brainstorm.md +1 -1
- package/config/templates/task/feature-impl.md +1 -1
- package/config/templates/task/issue.md +1 -1
- package/config/templates/task/meta.md +1 -1
- package/config/templates/task/review.md +1 -1
- package/config/templates/task/standard.md +1 -1
- package/config/workflows/task-pipeline.yaml +178 -18
- package/package.json +9 -9
- package/plugins/sp/README.md +14 -1
- package/plugins/sp/agents/super-reviewer.md +25 -11
- package/plugins/sp/commands/dev-changelog.md +5 -4
- package/plugins/sp/commands/dev-review-session.md +12 -7
- package/plugins/sp/commands/dev-run.md +5 -2
- package/plugins/sp/commands/dev-runall.md +5 -2
- package/plugins/sp/hooks/agent-lifecycle.ts +155 -0
- package/plugins/sp/hooks/fleet-guest-stop.ts +180 -0
- package/plugins/sp/hooks/hooks.json +45 -1
- package/plugins/sp/hooks/pi/guard-extension.ts +21 -0
- package/plugins/sp/lib/history-anatomy.generated.mjs +5 -0
- package/plugins/sp/lib/idea-handoff.generated.mjs +68 -10
- package/plugins/sp/lib/inline-run.generated.d.mts +1 -0
- package/plugins/sp/lib/inline-run.generated.mjs +83 -20
- package/plugins/sp/lib/residual-scan.generated.d.mts +5 -0
- package/plugins/sp/lib/residual-scan.generated.mjs +45 -5
- package/plugins/sp/lib/transcript.ts +160 -0
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/history-anatomy-cache.mjs +5 -0
- package/plugins/sp/scripts/inline-run-setup.mjs +21 -20
- package/plugins/sp/scripts/inline-run-setup.ts +25 -31
- package/plugins/sp/scripts/persist-out-check.mjs +164 -0
- package/plugins/sp/scripts/persist-out-check.ts +185 -0
- package/plugins/sp/scripts/residual-scan.mjs +52 -15
- package/plugins/sp/scripts/residual-scan.ts +11 -11
- package/plugins/sp/scripts/run-summary.mjs +286 -0
- package/plugins/sp/scripts/run-summary.ts +241 -0
- package/plugins/sp/scripts/session-timeline.mjs +234 -0
- package/plugins/sp/scripts/session-timeline.ts +152 -0
- package/plugins/sp/scripts/task-diffstat.mjs +17 -5
- package/plugins/sp/scripts/task-diffstat.ts +25 -4
- package/plugins/sp/skills/code-verification/SKILL.md +8 -0
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +17 -1
- package/plugins/sp/skills/fleet-join/SKILL.md +63 -0
- package/plugins/sp/skills/session-review/SKILL.md +46 -24
- package/plugins/sp/skills/spur-cli/SKILL.md +1 -0
- package/plugins/sp/skills/spur-cli/references/agent.md +108 -6
- package/plugins/sp/skills/spur-cli/references/decision.md +97 -0
- package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +22 -0
- package/plugins/sp/skills/spur-composer/SKILL.md +27 -1
- package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +40 -7
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +28 -16
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +278 -42
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +17 -1
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +16 -4
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +139 -10
- package/plugins/sp/skills/spur-dev/references/planning-workflow.md +6 -0
- package/schemas/decision-catalog.schema.json +226 -0
- package/schemas/spur-config.schema.json +25 -0
- package/spur.js +8856 -2564
- package/web/_astro/BoardApp.Bzr4OXrW.js +324 -0
- package/web/_astro/{BoardApp.Dflq5a8x.js → BoardApp.VeoxxLBy.js} +1 -1
- package/web/_astro/{TaskDetail.CK7oXddP.js → TaskDetail.LAyR_Pvn.js} +1 -1
- package/web/_astro/{arc.BIqkDATc.js → arc.f4e0OYLg.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.D0B3SvWv.js → architectureDiagram-3BPJPVTR.SSIvxi-Z.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.C2YyKQmL.js → blockDiagram-GPEHLZMM.DSXIHOFf.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.DmtTYrt_.js → c4Diagram-AAUBKEIU.DG9Ubu85.js} +1 -1
- package/web/_astro/channel.CUFdtWhm.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.HsRXbh7l.js → chunk-2J33WTMH.C7QU88Qh.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.obE_P-R3.js → chunk-4BX2VUAB.26Z94xBj.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.CjWTF5mz.js → chunk-55IACEB6.DYRiBkqw.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.DMo799Ax.js → chunk-727SXJPM.D5Vfv5pe.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.-cE6JOKZ.js → chunk-AQP2D5EJ.DXHV47bN.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.D6Rw6HIk.js → chunk-FMBD7UC4.D1suAfOs.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.d5rBD73Z.js → chunk-ND2GUHAM.D6BmdHEY.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.r3ox40Q3.js → chunk-QZHKN3VN.rnPU-Ak-.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.CGGbvjjJ.js → classDiagram-4FO5ZUOK.CvEfXIo1.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.CGGbvjjJ.js → classDiagram-v2-Q7XG4LA2.CvEfXIo1.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.CmKtPd4l.js → cose-bilkent-S5V4N54A.DkMisLSm.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.QNg5GXs8.js → cynefin-OW5HDTMX.C77Q-pF6.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.B1SabsjC.js → dagre-BM42HDAG.YaV8ce27.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.RtDbNj89.js → diagram-2AECGRRQ.CUyGEKge.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.IzZpuM-Y.js → diagram-5GNKFQAL.CRgd8dgj.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.DPTF_fgv.js → diagram-KO2AKTUF.Bmzys9hq.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.CCyza39P.js → diagram-LMA3HP47.BvmJY4Lw.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.BXAi8zcZ.js → diagram-OG6HWLK6.0hEhD30d.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.DUDX7_MR.js → erDiagram-TEJ5UH35.Bjt8lrnm.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.C63QTh3N.js → flowDiagram-I6XJVG4X.CJZTjRDV.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.CnlZJSwG.js → ganttDiagram-6RSMTGT7._VqoVafc.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.CTqIrOPC.js → gitGraphDiagram-PVQCEYII.CEDb7tVo.js} +1 -1
- package/web/_astro/index.Dg_3bOaJ.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.BYMiDFh9.js → infoDiagram-5YYISTIA.CgwifPpK.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.CRiQmI-z.js → ishikawaDiagram-YF4QCWOH.B99wx2J1.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.YQgWBsEU.js → journeyDiagram-JHISSGLW.2hRwJbEo.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.1ECnrrK1.js → kanban-definition-UN3LZRKU.BahJkKNC.js} +1 -1
- package/web/_astro/{linear.DzVA71yT.js → linear.DguUUe6V.js} +1 -1
- package/web/_astro/{mermaid.core.BK7P2Sr_.js → mermaid.core.CvgfmqVr.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.BvXyWZK7.js → mindmap-definition-RKZ34NQL.B3qEA3x9.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.BiXk1TW3.js → pieDiagram-4H26LBE5.FlWA-_ru.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.krvBH8lh.js → quadrantDiagram-W4KKPZXB.9bVn35X7.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.ih1gL25c.js → requirementDiagram-4Y6WPE33.ChykIhct.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.CpUxkv4u.js → sankeyDiagram-5OEKKPKP.Z2Y3hkYb.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.clAfeN7y.js → sequenceDiagram-3UESZ5HK.J7JsHWDt.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.DpXWb8-m.js → stateDiagram-AJRCARHV.DAPbiCSS.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.ClZb9GIH.js → stateDiagram-v2-BHNVJYJU.D2jvSi0q.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.BLcX5dh7.js → timeline-definition-PNZ67QCA.psW2ci29.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.oGgpKMpx.js → vennDiagram-CIIHVFJN.DlILYVmg.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.BPHtMtt-.js → wardleyDiagram-YWT4CUSO.xNWwLJnn.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.BNZOPYSk.js → xychartDiagram-2RQKCTM6.EU_BuH7V.js} +1 -1
- package/web/board-runtime.json +1 -1
- package/web/index.html +2 -2
- package/web/_astro/BoardApp.CYbe-OTb.js +0 -322
- package/web/_astro/channel.JuKVyuhZ.js +0 -1
- package/web/_astro/index.CbNz17Sx.css +0 -1
|
@@ -149,3 +149,20 @@ agent:
|
|
|
149
149
|
# extra workflow folders by concatenating them here (loader merge):
|
|
150
150
|
workflows:
|
|
151
151
|
paths: []
|
|
152
|
+
|
|
153
|
+
# Decision catalogs (feature P / ADR-134): the project layer is always
|
|
154
|
+
# `<cwd>/.spur/decisions` and the shared layer is the installed package's own
|
|
155
|
+
# `config/decisions`, so no config entry is needed to discover the shipped
|
|
156
|
+
# catalogs. `paths` registers extra catalog folders (absolute or
|
|
157
|
+
# project-relative); the first layer wins per file basename (ADR-113 style).
|
|
158
|
+
# `maker` selects the default DecisionMaker for every decision point, and
|
|
159
|
+
# `makers` overrides it per decision id. Built-in makers: typesafe, fm-local,
|
|
160
|
+
# laya-local (laya-local needs Apple Silicon). An unregistered name is a config
|
|
161
|
+
# error, never a silent fallback. Inspect the effective choice with
|
|
162
|
+
# `spur decision status`. `init` never overwrites this file — until you add the
|
|
163
|
+
# keys, the shipped catalog's maker applies (see `status`).
|
|
164
|
+
decisions:
|
|
165
|
+
paths: []
|
|
166
|
+
maker: typesafe
|
|
167
|
+
# makers:
|
|
168
|
+
# task-triage: laya-local
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
$schema: "@gobing-ai/spur/schemas/decision-catalog.schema.json"
|
|
2
|
+
# Decision catalogs for the task pipeline (feature P / task 1092, ADR-134).
|
|
3
|
+
#
|
|
4
|
+
# These entries mirror the inline decide actions that ship in
|
|
5
|
+
# config/workflows/task-pipeline.yaml today — same questions, choices and
|
|
6
|
+
# fallbacks — so their reliability can be measured with `spur decision run`
|
|
7
|
+
# before feature P1 migrates the workflow actions onto them (design §3.5).
|
|
8
|
+
# No workflow references this catalog yet.
|
|
9
|
+
#
|
|
10
|
+
# Resolution: this file ships in the shared layer (the installed package's
|
|
11
|
+
# config/decisions/). A project can override it per basename through
|
|
12
|
+
# .spur/decisions/ or decisions.paths (first layer wins, ADR-113 style).
|
|
13
|
+
version: 1
|
|
14
|
+
|
|
15
|
+
defaults:
|
|
16
|
+
# ADR-125 value, kept over the upstream 0.7 default.
|
|
17
|
+
minConfidence: 0.8
|
|
18
|
+
|
|
19
|
+
decisions:
|
|
20
|
+
task-triage:
|
|
21
|
+
type: choice
|
|
22
|
+
description: Which verification lane does a green-gate diff deserve?
|
|
23
|
+
parameters:
|
|
24
|
+
wbs:
|
|
25
|
+
type: string
|
|
26
|
+
description: Task WBS the diff belongs to.
|
|
27
|
+
criteria:
|
|
28
|
+
low: Small, contained diff — the fast lane is enough.
|
|
29
|
+
standard: Ordinary diff — the standard verification lane applies.
|
|
30
|
+
high: Broad or risky diff — verify with high caution.
|
|
31
|
+
fallback: standard
|
|
32
|
+
|
|
33
|
+
failure-class:
|
|
34
|
+
type: choice
|
|
35
|
+
description: Is a failed quality gate fixable by the repair agent, or should the run stop?
|
|
36
|
+
parameters:
|
|
37
|
+
wbs:
|
|
38
|
+
type: string
|
|
39
|
+
description: Task WBS whose gate failed.
|
|
40
|
+
criteria:
|
|
41
|
+
fix: The failure looks fixable — send it to the repair agent.
|
|
42
|
+
stop: The failure is not mechanically fixable — stop the run for the operator.
|
|
43
|
+
fallback: fix
|
|
44
|
+
|
|
45
|
+
review-failure-class:
|
|
46
|
+
type: choice
|
|
47
|
+
description: Is a non-PASS review fixable by the repair agent, or should the run stop?
|
|
48
|
+
parameters:
|
|
49
|
+
wbs:
|
|
50
|
+
type: string
|
|
51
|
+
description: Task WBS whose review did not certify PASS.
|
|
52
|
+
criteria:
|
|
53
|
+
fix: The findings look fixable — send them to the repair agent.
|
|
54
|
+
stop: The findings are not mechanically fixable — stop the run for the operator.
|
|
55
|
+
fallback: fix
|
|
@@ -66,16 +66,31 @@
|
|
|
66
66
|
"contract": "standard",
|
|
67
67
|
"twin": "quality-gate.mjs"
|
|
68
68
|
},
|
|
69
|
+
{
|
|
70
|
+
"rel": "persist-out-check.ts",
|
|
71
|
+
"contract": "standard",
|
|
72
|
+
"twin": "persist-out-check.mjs"
|
|
73
|
+
},
|
|
69
74
|
{
|
|
70
75
|
"rel": "residual-scan.ts",
|
|
71
76
|
"contract": "standard",
|
|
72
77
|
"twin": "residual-scan.mjs"
|
|
73
78
|
},
|
|
79
|
+
{
|
|
80
|
+
"rel": "run-summary.ts",
|
|
81
|
+
"contract": "standard",
|
|
82
|
+
"twin": "run-summary.mjs"
|
|
83
|
+
},
|
|
74
84
|
{
|
|
75
85
|
"rel": "script-root.ts",
|
|
76
86
|
"contract": "standard",
|
|
77
87
|
"twin": "script-root.mjs"
|
|
78
88
|
},
|
|
89
|
+
{
|
|
90
|
+
"rel": "session-timeline.ts",
|
|
91
|
+
"contract": "standard",
|
|
92
|
+
"twin": "session-timeline.mjs"
|
|
93
|
+
},
|
|
79
94
|
{
|
|
80
95
|
"rel": "task-diffstat.ts",
|
|
81
96
|
"contract": "standard",
|
|
@@ -24,6 +24,8 @@ exclude:
|
|
|
24
24
|
# legitimately contains `config/...` documentation / self-references — not
|
|
25
25
|
# runtime paths.
|
|
26
26
|
- "apps/cli/config/**"
|
|
27
|
+
# Desktop stage copies the same generated build-time asset payload beside each binary.
|
|
28
|
+
- "apps/desktop/resources/**"
|
|
27
29
|
rules:
|
|
28
30
|
- id: sp-runtime-path
|
|
29
31
|
description: >
|
|
@@ -2,9 +2,11 @@ $schema: "@gobing-ai/spur/schemas/rule-file.schema.json"
|
|
|
2
2
|
# Coverage gate — per-file line coverage meets threshold from Bun's lcov output.
|
|
3
3
|
# Absorbed from ts-libs/.spur/rules/quality/coverage-gate.yaml, re-scoped to
|
|
4
4
|
# Spur's monorepo layout:
|
|
5
|
-
# - lcovPath kept at .coverage/lcov.info (Spur's `bun run test:coverage`
|
|
6
|
-
#
|
|
7
|
-
#
|
|
5
|
+
# - lcovPath kept at .coverage/lcov.info (Spur's `bun run test:coverage`
|
|
6
|
+
# writes it via --coverage-dir=.coverage, and `bunfig.toml
|
|
7
|
+
# [test] coverage = true` means a plain `bun run test` writes the same
|
|
8
|
+
# artifact — so a TARGETED `bun test <file>` overwrites it with a partial
|
|
9
|
+
# tracefile, and this rule then judges that partial data)
|
|
8
10
|
# - include expanded to apps/** + packages/** (Spur tests cover both;
|
|
9
11
|
# ts-libs only covers packages/**)
|
|
10
12
|
# - threshold kept at 90 matching bunfig.toml coverageThreshold
|
|
@@ -32,6 +32,10 @@ rules:
|
|
|
32
32
|
- "**/tests/**"
|
|
33
33
|
- "**/*.test.ts"
|
|
34
34
|
|
|
35
|
+
# Electron Node adapter: ts-runtime's streaming handle uses Bun.spawn.
|
|
36
|
+
# Owns the detached process-group handle needed for startup cancellation and quit.
|
|
37
|
+
- "apps/desktop/src/server-process.ts"
|
|
38
|
+
|
|
35
39
|
# Low-level system primitives:
|
|
36
40
|
- "packages/app/src/services/quality-gate.ts" # gate runs `sh -c` with stdout+stderr merged onto ONE fd; ProcessExecutor captures streams separately and cannot reproduce the stream-order contract (task 1006 R1; mirrors the history-anatomy bundling exemptions)
|
|
37
41
|
|
|
@@ -60,16 +64,23 @@ rules:
|
|
|
60
64
|
- "packages/app/src/services/token-ledger-service.ts" # FD byte-window log tailing
|
|
61
65
|
- "packages/app/src/services/token-ledger-watcher.ts" # node:fs watch() live watcher
|
|
62
66
|
- "packages/app/src/services/project-registry.ts" # atomic projects.json persistence
|
|
67
|
+
- "packages/app/src/services/project-server-owner.ts" # atomic mkdir lifetime claim + unique-marker stale recovery; FileSystem has no exclusive mkdir primitive (1082)
|
|
63
68
|
- "packages/app/src/services/slash-commands-service.ts" # synchronous ~/.config/spur/slash_commands.json persistence (mirrors project-registry.ts)
|
|
64
69
|
- "packages/app/src/services/agent-usage-producer.ts" # atomic agent-usage snapshot write (tmp + rename, mirrors project-registry.ts; task 0892 R1)
|
|
65
70
|
- "packages/app/src/services/history-service.ts" # versioned analyze artifact + bounded-errors sidecar + latest.json symlink pointer (task 0474); ts-runtime FileSystem seam has no symlink, so the pointer uses node:fs directly (mirrors project-registry.ts persistence exemption)
|
|
66
71
|
- "packages/app/src/services/history-anatomy.ts" # deterministic history-anatomy cache core (task 1005 R2): fsync'd tmp+rename atomic publish, readdir digest scans and sync report reads must stay byte-stable; bundled standalone for the bare-node plugin twin (mirrors agent-usage-producer.ts / history-service.ts exemptions)
|
|
67
72
|
- "packages/app/src/observability/workflow-run-log-sink.ts" # sync FD append for mid-run tail-able all-in-one run log (task 0426 / feature D2); append() is sync from the observability bus
|
|
73
|
+
- "packages/app/src/observability/agent-run-log.ts" # same primitive as the sink above for the per-run agent stream (task 1076 R1, ADR-132); append() is sync from the agent service's own onOutput hook, and the byte cap needs the FD position it tracks
|
|
68
74
|
- "apps/cli/src/commands/workflow.ts" # FD byte-window tail of the mid-run run log for `workflow trace --follow` streaming (task 0428 / feature D2); readSync at offset over the observability sink's FDs
|
|
69
75
|
- "apps/web/src/modules/discover.ts" # Vite/Astro module scanner fallback under bun test
|
|
70
76
|
- "packages/app/src/services/quality-gate.ts" # gate artifacts (log/findings/receipt) written directly; merged-fd gate log and fsync-free atomic writes must stay byte-stable; bundled standalone for the bare-node plugin twin (task 1006 R1; mirrors history-anatomy.ts)
|
|
71
77
|
- "packages/app/src/services/run-storage.ts" # durable-evidence migration core (task 1025 R4): atomic tmp+rename byte copies with digest reread and atomic manifest publish must stay byte-stable (mirrors agent-usage-producer.ts / project-registry.ts exemptions)
|
|
72
78
|
|
|
79
|
+
# Electron Node adapters cannot load Bun runtime implementations; keep the
|
|
80
|
+
# synchronous existence/directory probes injected for unit testing (desktop contract).
|
|
81
|
+
- "apps/desktop/src/layout.ts"
|
|
82
|
+
- "apps/desktop/src/server-process.ts"
|
|
83
|
+
|
|
73
84
|
# Synchronous bootstrap & path resolution:
|
|
74
85
|
- "packages/config/src/bundled-config.ts" # sync bundledConfigRoot() directory walk
|
|
75
86
|
- "packages/config/src/loader.ts" # sync resolveConfigFile() / schema specifier lookup
|
|
@@ -37,6 +37,9 @@ rules:
|
|
|
37
37
|
# this rule's include scope — scanning generated copies only adds noise
|
|
38
38
|
# (e.g. `id: task-implement-transition` trips the sk- token shape).
|
|
39
39
|
- "apps/cli/config/"
|
|
40
|
+
# Desktop ships the same generated config payload beside each compiled binary.
|
|
41
|
+
# Its source remains outside this source-code rule, just like the CLI bundle.
|
|
42
|
+
- "apps/desktop/resources/"
|
|
40
43
|
# `apps/cli/plugins/` is the same kind of gitignored build:bundle output — a staged
|
|
41
44
|
# npm copy of `plugins/`, itself outside the include scope. Minified generated libs
|
|
42
45
|
# there trip the api-key shape (0976: HEAD's inline-run.generated.mjs fires too).
|
|
@@ -27,7 +27,7 @@ updated_at: "{{ CREATED_AT }}"
|
|
|
27
27
|
|
|
28
28
|
### Acceptance Criteria
|
|
29
29
|
|
|
30
|
-
<!-- Number items AC1, AC2, … (never R<n> — that is the Requirements namespace):
|
|
30
|
+
<!-- Number items AC1, AC2, … (never R<n> — that is the Requirements namespace). Preferred: `Scenario: AC1 — <concrete outcome> (req: R1)` blocks with Given/When/Then, declaring both `ac_altitude: task-local` and `ac_numbering: task-local` for task-local regression criteria (altitude skips only the feature-subset check; numbering makes `(req: R<n>)` count toward requirement coverage). Parsed checkbox rows `- [ ] AC1 — <title>` are supported but never bind requirements — only `Scenario:` titles read `(req: R<n>)`. Bare `- AC1` bullets are legacy unparsed records, not a traceability bypass. Requirements use `- [ ] R1. <text>`, checked at close. Keep empty if not applicable. -->
|
|
31
31
|
|
|
32
32
|
### Q&A
|
|
33
33
|
|
|
@@ -27,7 +27,7 @@ updated_at: "{{ CREATED_AT }}"
|
|
|
27
27
|
|
|
28
28
|
### Acceptance Criteria
|
|
29
29
|
|
|
30
|
-
<!-- Number items AC1, AC2, … (never R<n> — that is the Requirements namespace):
|
|
30
|
+
<!-- Number items AC1, AC2, … (never R<n> — that is the Requirements namespace). Preferred: `Scenario: AC1 — <concrete outcome> (req: R1)` blocks with Given/When/Then, declaring both `ac_altitude: task-local` and `ac_numbering: task-local` for task-local regression criteria (altitude skips only the feature-subset check; numbering makes `(req: R<n>)` count toward requirement coverage). Parsed checkbox rows `- [ ] AC1 — <title>` are supported but never bind requirements — only `Scenario:` titles read `(req: R<n>)`. Bare `- AC1` bullets are legacy unparsed records, not a traceability bypass. Requirements use `- [ ] R1. <text>`, checked at close. Do not leave placeholder AC here. -->
|
|
31
31
|
|
|
32
32
|
### Q&A
|
|
33
33
|
|
|
@@ -27,7 +27,7 @@ updated_at: "{{ CREATED_AT }}"
|
|
|
27
27
|
|
|
28
28
|
### Acceptance Criteria
|
|
29
29
|
|
|
30
|
-
<!-- Number items AC1, AC2, … (never R<n> — that is the Requirements namespace):
|
|
30
|
+
<!-- Number items AC1, AC2, … (never R<n> — that is the Requirements namespace). Preferred: `Scenario: AC1 — <concrete outcome> (req: R1)` blocks with Given/When/Then, declaring both `ac_altitude: task-local` and `ac_numbering: task-local` for task-local regression criteria (altitude skips only the feature-subset check; numbering makes `(req: R<n>)` count toward requirement coverage). Parsed checkbox rows `- [ ] AC1 — <title>` are supported but never bind requirements — only `Scenario:` titles read `(req: R<n>)`. Bare `- AC1` bullets are legacy unparsed records, not a traceability bypass. Requirements use `- [ ] R1. <text>`, checked at close. Use a regression scenario proving the bug is fixed. -->
|
|
31
31
|
|
|
32
32
|
### Q&A
|
|
33
33
|
|
|
@@ -27,7 +27,7 @@ updated_at: "{{ CREATED_AT }}"
|
|
|
27
27
|
|
|
28
28
|
### Acceptance Criteria
|
|
29
29
|
|
|
30
|
-
<!-- Number items AC1, AC2, … (never R<n> — that is the Requirements namespace):
|
|
30
|
+
<!-- Number items AC1, AC2, … (never R<n> — that is the Requirements namespace). Preferred: `Scenario: AC1 — <concrete outcome> (req: R1)` blocks with Given/When/Then, declaring both `ac_altitude: task-local` and `ac_numbering: task-local` for task-local regression criteria (altitude skips only the feature-subset check; numbering makes `(req: R<n>)` count toward requirement coverage). Parsed checkbox rows `- [ ] AC1 — <title>` are supported but never bind requirements — only `Scenario:` titles read `(req: R<n>)`. Bare `- AC1` bullets are legacy unparsed records, not a traceability bypass. Requirements use `- [ ] R1. <text>`, checked at close. Keep empty if not applicable. -->
|
|
31
31
|
|
|
32
32
|
### Q&A
|
|
33
33
|
|
|
@@ -37,7 +37,7 @@ in the reviewed PR/commit/diff). Fix in priority order (P1 → P2 → …); re-r
|
|
|
37
37
|
|
|
38
38
|
### Acceptance Criteria
|
|
39
39
|
|
|
40
|
-
<!-- Number items AC1, AC2, … (never R<n> — that is the Requirements namespace):
|
|
40
|
+
<!-- Number items AC1, AC2, … (never R<n> — that is the Requirements namespace). Preferred: `Scenario: AC1 — <concrete outcome> (req: R1)` blocks with Given/When/Then, declaring both `ac_altitude: task-local` and `ac_numbering: task-local` for task-local regression criteria (altitude skips only the feature-subset check; numbering makes `(req: R<n>)` count toward requirement coverage). Parsed checkbox rows `- [ ] AC1 — <title>` are supported but never bind requirements — only `Scenario:` titles read `(req: R<n>)`. Bare `- AC1` bullets are legacy unparsed records, not a traceability bypass. Requirements use `- [ ] R1. <text>`, checked at close. Keep empty until the review task becomes executable work. -->
|
|
41
41
|
|
|
42
42
|
### Q&A
|
|
43
43
|
|
|
@@ -27,7 +27,7 @@ updated_at: "{{ CREATED_AT }}"
|
|
|
27
27
|
|
|
28
28
|
### Acceptance Criteria
|
|
29
29
|
|
|
30
|
-
<!-- Number items AC1, AC2, … (never R<n> — that is the Requirements namespace):
|
|
30
|
+
<!-- Number items AC1, AC2, … (never R<n> — that is the Requirements namespace). Preferred: `Scenario: AC1 — <concrete outcome> (req: R1)` blocks with Given/When/Then, declaring both `ac_altitude: task-local` and `ac_numbering: task-local` for task-local regression criteria (altitude skips only the feature-subset check; numbering makes `(req: R<n>)` count toward requirement coverage). Parsed checkbox rows `- [ ] AC1 — <title>` are supported but never bind requirements — only `Scenario:` titles read `(req: R<n>)`. Bare `- AC1` bullets are legacy unparsed records, not a traceability bypass. Requirements use `- [ ] R1. <text>`, checked at close. Keep empty if this task has no objective AC yet. -->
|
|
31
31
|
|
|
32
32
|
### Q&A
|
|
33
33
|
|
|
@@ -19,6 +19,21 @@
|
|
|
19
19
|
# done guard closed, and re-records Testing before the failed edge); done settles
|
|
20
20
|
# deferrals; failed renders the recovery report. No new state, edge, or model query.
|
|
21
21
|
#
|
|
22
|
+
# Confidence gate (session finding after 1088, task 1068 follow-up): the verify answer has
|
|
23
|
+
# carried a `Confidence: HIGH|MEDIUM|LOW` line since 1068, and nothing read it — a PASS at
|
|
24
|
+
# LOW confidence certified exactly like HIGH. The verify stamp now records a `confidence`
|
|
25
|
+
# check row, and BOTH completion guards (verify → record, record → done) refuse a LOW (or
|
|
26
|
+
# absent) level unless the run was launched with `--vars '{"ackLowConfidence":"true"}'`.
|
|
27
|
+
# The acknowledgement is an operator decision, not a model one: it is the only way to
|
|
28
|
+
# certify a verdict the verifier itself would not stand behind.
|
|
29
|
+
#
|
|
30
|
+
# Review-failure routing (session finding after 1088): `review` had no outgoing edge for a
|
|
31
|
+
# FAIL verdict, so a failing review could only be remediated by driving the graph by hand
|
|
32
|
+
# (re-entering `test` and hand-emitting rows). The review stage now derives
|
|
33
|
+
# `.spur/run/<wbs>-review.status` from its own answer file (fail-closed to FAIL), and a FAIL
|
|
34
|
+
# routes through `review-fail-triage` — the review-side twin of `test-fail-triage` (0943) —
|
|
35
|
+
# into the SAME bounded fix budget, so remediation stays on-graph and bounded.
|
|
36
|
+
#
|
|
22
37
|
# Vars (passed as a JSON object via `--vars`):
|
|
23
38
|
# wbs — task WBS (required)
|
|
24
39
|
# profile — "auto" skips HITL approve (R4)
|
|
@@ -36,7 +51,19 @@ kind: state-machine
|
|
|
36
51
|
name: task-pipeline
|
|
37
52
|
# 0772 R2: explicit identity tag. Set only after the proof failure-path tests
|
|
38
53
|
# passed; the engine treats unversioned external definitions as supported.
|
|
39
|
-
|
|
54
|
+
# v4 (session finding after 1088): the completion gate reads the verifier's confidence level and
|
|
55
|
+
# the graph routes a non-PASS review into bounded remediation — both are contract changes, so the
|
|
56
|
+
# identity moves with them.
|
|
57
|
+
# v5 (task 1077's inline run, 2026-10-06): the verify verdict action is explicitly `onError:
|
|
58
|
+
# continue`. `task verdict` exits non-zero on any non-PASS verdict BY DESIGN (1003 R2) — that is
|
|
59
|
+
# data for the verify guards, not a state failure. Under the engine's default 'fail' policy
|
|
60
|
+
# (`resolveOnErrorPolicy: action ?? workflowDefault ?? runOption ?? 'fail'`) the state sequence
|
|
61
|
+
# halted on that exit code and `lifecycle.fail('verify')` finalized the run before any transition
|
|
62
|
+
# guard was evaluated, which made the declared `verify → test-fix` "repairable non-PASS" lane
|
|
63
|
+
# UNREACHABLE: a non-PASS verify could only end the run failed. `test-fix` also gained the
|
|
64
|
+
# review-lane evidence projection (see its onEnter) so the repair hop is not fed an empty findings
|
|
65
|
+
# list when the failing hop was `review-fail-triage` rather than the quality gate.
|
|
66
|
+
version: "5"
|
|
40
67
|
description: "Standard task pipeline: precheck → implement → quality-gate (test/fix/recheck) → review → approve → verify → record → done"
|
|
41
68
|
# Task entity lifecycle (owned by task-lifecycle.yaml):
|
|
42
69
|
# normal: backlog → todo → wip → testing → done
|
|
@@ -149,6 +176,14 @@ vars:
|
|
|
149
176
|
# Escalation counter: .spur/run/<wbs>-escalation-count. Default 2 = two answered
|
|
150
177
|
# questions; a third pause routes to failed with a report note.
|
|
151
178
|
maxEscalations: "2"
|
|
179
|
+
# Operator acknowledgement of a LOW-confidence certification (session finding after 1088).
|
|
180
|
+
# The verify answer's `Confidence:` line is captured into the verdict artifact; a LOW (or
|
|
181
|
+
# absent) level fails the completion guards unless this is "true", because a PASS the
|
|
182
|
+
# verifier would not stand behind is an operator risk decision, not a mechanical one.
|
|
183
|
+
# Set per run: `--vars '{"ackLowConfidence":"true"}'` (and export it on the inline path).
|
|
184
|
+
# Deliberately NOT settable from the task file or the verifier's own answer: the point is
|
|
185
|
+
# that a second party, not the producer, accepts the residual uncertainty.
|
|
186
|
+
ackLowConfidence: ""
|
|
152
187
|
# Runtime-written, 0933: the escalate hop's file.read.into-var fills this from
|
|
153
188
|
# .spur/run/<wbs>-question.md; the hitl.input prompt below references it. Declared
|
|
154
189
|
# here (empty) so the static var-reference validator sees the template source.
|
|
@@ -438,6 +473,17 @@ states:
|
|
|
438
473
|
options:
|
|
439
474
|
command: >-
|
|
440
475
|
printf '%s\n' "$(( $(cat ".spur/run/$wbs-test-fix-attempt" 2>/dev/null || echo 0) + 1 ))" > ".spur/run/$wbs-test-fix-attempt"; [ -f ".spur/run/$wbs-verdict.json" ] && echo '--- verify verdict (remediation input, task 0703 R4) ---' >> ".spur/run/$wbs-test-gate.log" && cat ".spur/run/$wbs-verdict.json" >> ".spur/run/$wbs-test-gate.log"; [ -f ".spur/run/$wbs-residuals.json" ] && echo '--- residual artifact (F96 fix targets; deferrals go to residual-deferrals.json, P3/markers only) ---' >> ".spur/run/$wbs-test-gate.log" && cat ".spur/run/$wbs-residuals.json" >> ".spur/run/$wbs-test-gate.log"; exit 0
|
|
476
|
+
# Review-lane input (v5, task 1077 run): `review-fail-triage` routes a non-PASS review
|
|
477
|
+
# into this same bounded repair hop, but it stages no review evidence — the fixer then
|
|
478
|
+
# received a GREEN gate log and an empty findings list, so the review finding it exists
|
|
479
|
+
# to repair was invisible to it. Project the reviewer's answer into the same log the
|
|
480
|
+
# other lanes use, and only on the review lane (the decision file exists only after
|
|
481
|
+
# `review-fail-triage`). Own action, not an append to the one above: ADR-115's 800-char
|
|
482
|
+
# shell cap is already 602 chars there.
|
|
483
|
+
- kind: shell
|
|
484
|
+
options:
|
|
485
|
+
command: >-
|
|
486
|
+
[ -f ".spur/run/$wbs-review-failure-class.decision" ] && [ -f ".spur/run/$__runId-review-answer.txt" ] && echo '--- review verdict (remediation input, review lane) ---' >> ".spur/run/$wbs-test-gate.log" && cat ".spur/run/$__runId-review-answer.txt" >> ".spur/run/$wbs-test-gate.log"; exit 0
|
|
441
487
|
# R3 (0482): project the extracted gate anchors into a var so the dispatch input
|
|
442
488
|
# can NAME the failing file:line, not merely point at a log. A vars template cannot
|
|
443
489
|
# run a shell, so the read is an action, not an inline `$(cat ...)` substitution.
|
|
@@ -611,10 +657,43 @@ states:
|
|
|
611
657
|
# verify stamp marks review `completed` only when this marker equals the current digest;
|
|
612
658
|
# otherwise `skipped` (an honest downgrade, never a fake completed), and the bound
|
|
613
659
|
# `record` registration refuses a skipped review.
|
|
660
|
+
# Session finding after 1088: the review VERDICT is read straight out of the reviewer's
|
|
661
|
+
# own answer file by the review → * guards below, so this stage needs no extra artifact
|
|
662
|
+
# and no extra action (ADR-115 legibility cap).
|
|
614
663
|
- kind: shell
|
|
615
664
|
options:
|
|
616
665
|
command: 'mkdir -p .spur/run; printf "%s" "$proofDigest" > ".spur/run/$__runId-review-proof.digest"; exit 0'
|
|
617
666
|
|
|
667
|
+
# ── review-fail-triage (session finding after 1088) ────────────────────────
|
|
668
|
+
# A review that reports FAIL used to have no outgoing edge: the graph could not see the
|
|
669
|
+
# verdict and nothing consumed it, so remediating a failing review meant driving the
|
|
670
|
+
# states by hand. This state is the review-side twin of `test-fail-triage` (0943 R3):
|
|
671
|
+
# the deterministic classifier routes the failure into the SAME bounded fix budget
|
|
672
|
+
# (`.spur/run/<wbs>-test-fix-attempt` against `qualityGateMaxFixAttempts`), and the fix
|
|
673
|
+
# re-enters quality → review → verify on a freshly captured digest. The `decide` action
|
|
674
|
+
# itself always runs (the engine has no onEnter conditionals); with
|
|
675
|
+
# `workflow.decideDecisionMaker` off it degrades to its declared default.
|
|
676
|
+
- id: review-fail-triage
|
|
677
|
+
description: >-
|
|
678
|
+
Deterministic router for a review that did NOT certify PASS (session finding after 1088).
|
|
679
|
+
`decide review-failure-class` reads the reviewer's answer and the task spec and routes:
|
|
680
|
+
fix → the shared test-fix hop, stop → failed with terminalReason failed-check. A
|
|
681
|
+
missing/corrupt decision fails closed to failed-check.
|
|
682
|
+
onEnter:
|
|
683
|
+
- kind: decide
|
|
684
|
+
options:
|
|
685
|
+
id: review-failure-class
|
|
686
|
+
method: choice
|
|
687
|
+
question: >-
|
|
688
|
+
The review did not certify PASS (FAIL or PARTIAL). Is the finding fixable by the
|
|
689
|
+
repair agent, or should the run stop here for the operator?
|
|
690
|
+
choices: [fix, stop]
|
|
691
|
+
default: fix
|
|
692
|
+
evidence:
|
|
693
|
+
- .spur/run/${vars.__runId}-review-answer.txt
|
|
694
|
+
- ${vars.taskSpecPath}
|
|
695
|
+
resultFile: .spur/run/${vars.wbs}-review-failure-class.decision
|
|
696
|
+
|
|
618
697
|
- id: approve
|
|
619
698
|
description: >
|
|
620
699
|
Human-in-the-loop approval gate. Under `profile=auto` this state is never entered
|
|
@@ -675,8 +754,26 @@ states:
|
|
|
675
754
|
# (d) `spur task verdict` owns the answer lint (task 1003 R2) — the shell below
|
|
676
755
|
# fails closed when the verifier's answer is malformed, before any verdict artifact.
|
|
677
756
|
- kind: shell
|
|
757
|
+
# `task verdict` exits 1 on a non-PASS verdict by design (1003 R2). The `continue`
|
|
758
|
+
# policy keeps that exit code as recorded evidence (the action row is still `failed`)
|
|
759
|
+
# while letting the state finish its remaining actions and reach the guards below;
|
|
760
|
+
# without it the default 'fail' policy halts the run here and `verify → test-fix`
|
|
761
|
+
# (the declared bounded remediation lane) can never fire (v5, task 1077 run).
|
|
762
|
+
onError: continue
|
|
678
763
|
options:
|
|
679
764
|
command: "$spurBin task verdict $wbs --from-answer .spur/run/$wbs-verify-answer.txt"
|
|
765
|
+
# Session finding after 1088: the verifier's confidence level becomes a `confidence` check
|
|
766
|
+
# row (warn unless HIGH) and a gate-log line, so the level the completion guards enforce is
|
|
767
|
+
# visible in the artifact and the log — not only in the answer file. This is its own action
|
|
768
|
+
# because the proof-binding stamp below must stay the state's LAST action AND under the
|
|
769
|
+
# ADR-115 800-char shell cap.
|
|
770
|
+
- kind: shell
|
|
771
|
+
options:
|
|
772
|
+
command: >-
|
|
773
|
+
V=".spur/run/$wbs-verdict.json"; [ -f "$V" ] || exit 0;
|
|
774
|
+
jq --arg ack "$ackLowConfidence" '. + {checks: ((.checks // []) | map(select(.name != "confidence"))) + [{name: "confidence", status: (if (.confidence // "LOW") == "HIGH" then "pass" else "warn" end), evidence: ((.confidence // "LOW (absent from artifact)") + (if $ack == "true" then " (operator-acknowledged)" else "" end))}]}' "$V" > "$V.tmp" && mv "$V.tmp" "$V";
|
|
775
|
+
printf 'confidence: %s (ack=%s)\n' "$(jq -r '.confidence // "LOW (absent)"' "$V")" "$ackLowConfidence" >> ".spur/run/$wbs-test-gate.log";
|
|
776
|
+
exit 0
|
|
680
777
|
# (e) one jq mutation binds the verdict to the proof digest (0703 R3; runId 0730 §B.2 /
|
|
681
778
|
# 0757 R4; definitionDigest 0759 R5; honest review stamp 0785 R4; soft action + hard guard).
|
|
682
779
|
- kind: shell
|
|
@@ -1018,21 +1115,73 @@ transitions:
|
|
|
1018
1115
|
description: Failure-class decision missing/corrupt — stop at failed.
|
|
1019
1116
|
guard:
|
|
1020
1117
|
kind: always
|
|
1021
|
-
# ── review
|
|
1022
|
-
#
|
|
1023
|
-
#
|
|
1024
|
-
#
|
|
1025
|
-
|
|
1118
|
+
# ── review-failure routing (session finding after 1088): the review-side twin of the
|
|
1119
|
+
# failure-class router above — stop first, then the shared attempt cap, then the fix lane,
|
|
1120
|
+
# then the fail-closed defense. The counter is the SAME one the quality-gate loop uses, so a
|
|
1121
|
+
# review FAIL cannot buy extra attempts and the iteration bound stays finite.
|
|
1122
|
+
- from: review-fail-triage
|
|
1123
|
+
to: failed
|
|
1124
|
+
terminalReason: failed-check
|
|
1125
|
+
description: Review failure class stop — the run terminates at failed, never bypassed.
|
|
1126
|
+
guard:
|
|
1127
|
+
kind: shell
|
|
1128
|
+
options:
|
|
1129
|
+
command: >-
|
|
1130
|
+
jq -e '.value == "stop"' ".spur/run/$wbs-review-failure-class.decision" >/dev/null 2>&1
|
|
1131
|
+
- from: review-fail-triage
|
|
1132
|
+
to: failed
|
|
1133
|
+
terminalReason: retry-exhausted
|
|
1134
|
+
description: Attempt cap reached — qualityGateMaxFixAttempts bounds the review repair lane.
|
|
1135
|
+
guard:
|
|
1136
|
+
kind: shell
|
|
1137
|
+
options:
|
|
1138
|
+
command: >-
|
|
1139
|
+
fix_attempts="$(cat .spur/run/$wbs-test-fix-attempt 2>/dev/null || echo 0)";
|
|
1140
|
+
test "$fix_attempts" -ge "$qualityGateMaxFixAttempts"
|
|
1141
|
+
- from: review-fail-triage
|
|
1142
|
+
to: test-fix
|
|
1143
|
+
description: Review failure class fix and under the cap — the shared bounded repair hop.
|
|
1144
|
+
guard:
|
|
1145
|
+
kind: shell
|
|
1146
|
+
options:
|
|
1147
|
+
command: >-
|
|
1148
|
+
jq -e '.value == "fix"' ".spur/run/$wbs-review-failure-class.decision" >/dev/null 2>&1
|
|
1149
|
+
# Defense: missing/corrupt review-failure decision — fail closed instead of silently repairing.
|
|
1150
|
+
- from: review-fail-triage
|
|
1151
|
+
to: failed
|
|
1152
|
+
terminalReason: failed-check
|
|
1153
|
+
description: Review-failure decision missing/corrupt — stop at failed.
|
|
1154
|
+
guard:
|
|
1155
|
+
kind: always
|
|
1156
|
+
# ── review routing: FAIL → bounded remediation; PASS → approve or the auto-skip (R4) ──
|
|
1157
|
+
# Declaration order matters and is FAIL-CLOSED: the two PASS edges each demand the reviewer's
|
|
1158
|
+
# own `Verdict: PASS` line, and the catch-all sends EVERYTHING ELSE (FAIL, a missing answer, an
|
|
1159
|
+
# unparseable one) into `review-fail-triage`. Before this, a FAIL review had no outgoing edge at
|
|
1160
|
+
# all — the graph could not see the verdict, so remediation meant driving states by hand
|
|
1161
|
+
# (session finding after 1088). The reviewer writes `Verdict: PASS|FAIL` in its answer; both
|
|
1162
|
+
# plain and bold forms are accepted. Under `profile=auto` the approve gate is skipped, so the
|
|
1163
|
+
# PASS+auto edge is tried first and the interactive PASS edge second.
|
|
1026
1164
|
- from: review
|
|
1027
1165
|
to: verify
|
|
1028
|
-
description: profile=auto — skip the human approval gate
|
|
1166
|
+
description: profile=auto and the review certified PASS — skip the human approval gate.
|
|
1029
1167
|
guard:
|
|
1030
1168
|
kind: shell
|
|
1031
1169
|
options:
|
|
1032
|
-
command:
|
|
1170
|
+
command: >-
|
|
1171
|
+
test "$profile" = auto &&
|
|
1172
|
+
grep -qiE '^[[:space:]]*\**Verdict:\** *PASS' ".spur/run/$__runId-review-answer.txt"
|
|
1033
1173
|
- from: review
|
|
1034
1174
|
to: approve
|
|
1035
|
-
description: Review
|
|
1175
|
+
description: Review certified PASS — gate on human approval before verification.
|
|
1176
|
+
guard:
|
|
1177
|
+
kind: shell
|
|
1178
|
+
options:
|
|
1179
|
+
command: grep -qiE '^[[:space:]]*\**Verdict:\** *PASS' ".spur/run/$__runId-review-answer.txt"
|
|
1180
|
+
- from: review
|
|
1181
|
+
to: review-fail-triage
|
|
1182
|
+
description: >-
|
|
1183
|
+
No PASS in the reviewer's answer (FAIL, PARTIAL, missing, or unparseable — fail closed) —
|
|
1184
|
+
classify the failure and route it into the bounded repair hop instead of dead-ending the run.
|
|
1036
1185
|
guard:
|
|
1037
1186
|
kind: always
|
|
1038
1187
|
|
|
@@ -1069,19 +1218,23 @@ transitions:
|
|
|
1069
1218
|
# The verify step emits .spur/run/<wbs>-verdict.json with a required proof block (task 0703 R3/R5).
|
|
1070
1219
|
# Only `verdict: PASS` PLUS a proof block whose digest — top level and every named stage — equals
|
|
1071
1220
|
# the captured `proofDigest` clears the gate to `record`; any non-PASS, a missing file, malformed
|
|
1072
|
-
# JSON, or missing/mismatched proof evidence does not.
|
|
1221
|
+
# JSON, or missing/mismatched proof evidence does not. The verifier's confidence level is part of
|
|
1222
|
+
# that certification (session finding after 1088): a PASS the verifier itself marked LOW — or left
|
|
1223
|
+
# unstated — clears the gate only when the operator accepted it with
|
|
1224
|
+
# `--vars '{"ackLowConfidence":"true"}'`. Declaration order: PASS+proof guard FIRST,
|
|
1073
1225
|
# then the bounded remediation route (R4), then the always catch-all so a PASS verdict with a
|
|
1074
1226
|
# missing/malformed proof block still terminates at `failed` instead of hanging the state.
|
|
1075
1227
|
- from: verify
|
|
1076
1228
|
to: record
|
|
1077
|
-
description: Verification verdict is PASS
|
|
1229
|
+
description: Verification verdict is PASS, its proof block names the captured digest on every stage, and any LOW confidence was operator-acknowledged — record results and proceed to done.
|
|
1078
1230
|
guard:
|
|
1079
1231
|
# (one jq -e predicate) PASS verdict; proof, quality-gate, review and verification digests
|
|
1080
|
-
# equal to proofDigest; review completed; matching run id and definition digest (0703 R5, 0785 R4)
|
|
1232
|
+
# equal to proofDigest; review completed; matching run id and definition digest (0703 R5, 0785 R4);
|
|
1233
|
+
# confidence HIGH/MEDIUM, or LOW/absent with the operator's ack (session finding after 1088).
|
|
1081
1234
|
kind: shell
|
|
1082
1235
|
options:
|
|
1083
1236
|
command: >-
|
|
1084
|
-
jq -e --arg d "$proofDigest" --arg r "$__runId" --arg dd "$__definitionDigest" '
|
|
1237
|
+
jq -e --arg d "$proofDigest" --arg r "$__runId" --arg dd "$__definitionDigest" --arg ack "$ackLowConfidence" '
|
|
1085
1238
|
.verdict == "PASS"
|
|
1086
1239
|
and (.proof.digest // "") == $d
|
|
1087
1240
|
and (.proof.stages.qualityGate.digest // "") == $d
|
|
@@ -1090,6 +1243,7 @@ transitions:
|
|
|
1090
1243
|
and (.proof.stages.verification.digest // "") == $d
|
|
1091
1244
|
and (.proof.runId // "") == $r
|
|
1092
1245
|
and (.proof.definitionDigest // "") == $dd
|
|
1246
|
+
and ((.confidence // "LOW") != "LOW" or $ack == "true")
|
|
1093
1247
|
' ".spur/run/$wbs-verdict.json" >/dev/null 2>&1
|
|
1094
1248
|
- from: verify
|
|
1095
1249
|
to: test-fix
|
|
@@ -1117,11 +1271,12 @@ transitions:
|
|
|
1117
1271
|
# ── done gate: record → done/failed gated on `spur task check` (ADR-026 amendment 2026-06-23)
|
|
1118
1272
|
# PLUS the proof-block re-assertion (task 0703 R5): the verdict artifact must still be PASS and
|
|
1119
1273
|
# still name the captured digest — a forged or mutated completion artifact fails closed here.
|
|
1274
|
+
# PLUS the confidence re-assertion (session finding after 1088): the same ack rule as verify →
|
|
1275
|
+
# record, re-read from the artifact so a mutation between the two boundaries cannot slip through.
|
|
1120
1276
|
# Declaration order: pass guard first.
|
|
1121
1277
|
- from: record
|
|
1122
1278
|
to: done
|
|
1123
|
-
description: Task check passed
|
|
1124
|
-
# (warn) 5 commands: named verdict + proof digest reads after the task check (legibility, 0874).
|
|
1279
|
+
description: Task check passed, the verdict proof block still names the captured digest, and any LOW confidence was acknowledged — certify done.
|
|
1125
1280
|
guard:
|
|
1126
1281
|
kind: shell
|
|
1127
1282
|
options:
|
|
@@ -1130,11 +1285,16 @@ transitions:
|
|
|
1130
1285
|
# the CLI's own target-aware `--no-lifecycle` backstop (F92 R3). No `#`
|
|
1131
1286
|
# comments inside the folded scalar: `>-` joins same-indent lines into
|
|
1132
1287
|
# one shell line.
|
|
1288
|
+
# Two logical commands, deliberately (ADR-115): the `task check` projection plus
|
|
1289
|
+
# ONE jq predicate re-asserting the whole completion contract — PASS verdict, the
|
|
1290
|
+
# captured proof digest, and the confidence rule (session finding after 1088).
|
|
1133
1291
|
command: >-
|
|
1134
1292
|
$spurBin task check $wbs --as done &&
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1293
|
+
jq -e --arg p "$proofDigest" --arg ack "$ackLowConfidence" '
|
|
1294
|
+
.verdict == "PASS"
|
|
1295
|
+
and ((.proof.digest // "") == $p)
|
|
1296
|
+
and ((.confidence // "LOW") != "LOW" or $ack == "true")
|
|
1297
|
+
' ".spur/run/$wbs-verdict.json" >/dev/null 2>&1
|
|
1138
1298
|
- from: record
|
|
1139
1299
|
to: failed
|
|
1140
1300
|
terminalReason: failed-check
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/spur",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Spur CLI — local-first harness for mainstream coding agents: constraint checking, workflow orchestration, agent health, and history analytics. Bun-native; exposes the `spur` command.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"spur",
|
|
@@ -57,14 +57,14 @@
|
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@commander-js/extra-typings": "^14.0.0",
|
|
60
|
-
"@gobing-ai/ts-db": "^0.5.
|
|
61
|
-
"@gobing-ai/ts-ai-runner": "^0.5.
|
|
62
|
-
"@gobing-ai/ts-dual-workflow-engine": "^0.5.
|
|
63
|
-
"@gobing-ai/ts-infra": "^0.5.
|
|
64
|
-
"@gobing-ai/ts-llm-jsonl-importer": "^0.5.
|
|
65
|
-
"@gobing-ai/ts-rule-engine": "^0.5.
|
|
66
|
-
"@gobing-ai/ts-runtime": "^0.5.
|
|
67
|
-
"@gobing-ai/ts-utils": "^0.5.
|
|
60
|
+
"@gobing-ai/ts-db": "^0.5.16",
|
|
61
|
+
"@gobing-ai/ts-ai-runner": "^0.5.16",
|
|
62
|
+
"@gobing-ai/ts-dual-workflow-engine": "^0.5.16",
|
|
63
|
+
"@gobing-ai/ts-infra": "^0.5.16",
|
|
64
|
+
"@gobing-ai/ts-llm-jsonl-importer": "^0.5.16",
|
|
65
|
+
"@gobing-ai/ts-rule-engine": "^0.5.16",
|
|
66
|
+
"@gobing-ai/ts-runtime": "^0.5.16",
|
|
67
|
+
"@gobing-ai/ts-utils": "^0.5.16",
|
|
68
68
|
"@types/bun": "1.3.14",
|
|
69
69
|
"@types/figlet": "^1.7.0",
|
|
70
70
|
"@types/node-notifier": "8.0.5",
|