@gobing-ai/spur 0.3.85 → 0.3.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/config/pipeline-budgets.json +0 -25
- package/config/plugin-scripts.json +0 -5
- package/config/rules/boundary/env-var-hygiene.yaml +41 -0
- package/config/transition-shims.json +1 -1
- package/config/workflow-candidates.json +41 -0
- package/config/workflows/feature-lifecycle.yaml +4 -2
- package/config/workflows/feature-verification.yaml +70 -0
- package/config/workflows/idea-pipeline.yaml +24 -12
- package/config/workflows/task-pipeline.yaml +43 -12
- package/config/workflows/wrapup-pipeline.yaml +70 -7
- package/package.json +9 -9
- package/plugins/sp/README.md +0 -3
- package/plugins/sp/hooks/agent-hint.ts +5 -4
- package/plugins/sp/hooks/careful-guard.ts +3 -1
- package/plugins/sp/hooks/context-post-tool.ts +2 -1
- package/plugins/sp/hooks/context-session-start.ts +4 -3
- package/plugins/sp/hooks/context-session-stop.ts +2 -1
- package/plugins/sp/hooks/pi/guard-extension.ts +4 -3
- package/plugins/sp/hooks/task-write-guard.ts +4 -3
- package/plugins/sp/lib/idea-handoff.generated.mjs +260 -260
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/daily-summary/daily-summary.mjs +13 -4
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +6 -4
- package/plugins/sp/scripts/feature-sync-bounded.mjs +10 -2
- package/plugins/sp/scripts/feature-sync-bounded.ts +2 -1
- package/plugins/sp/scripts/idea-handoff.mjs +6 -1
- package/plugins/sp/scripts/idea-handoff.ts +4 -1
- package/plugins/sp/scripts/inline-pipeline-parity-check.ts +1 -1
- package/plugins/sp/scripts/inline-run-setup.ts +185 -2
- package/plugins/sp/scripts/pr-reviewing.mjs +8 -1
- package/plugins/sp/scripts/pr-reviewing.ts +2 -1
- package/plugins/sp/scripts/quality-gate.mjs +8 -1
- package/plugins/sp/scripts/quality-gate.ts +2 -1
- package/plugins/sp/scripts/surface-drift-inventory.ts +1 -4
- package/plugins/sp/scripts/task-evidence-precheck.ts +2 -1
- package/plugins/sp/scripts/task-size-precheck.ts +6 -4
- package/plugins/sp/scripts/verify-answer-lint.ts +2 -1
- package/plugins/sp/scripts/workflow-step-profile.mjs +10 -2
- package/plugins/sp/scripts/workflow-step-profile.ts +2 -1
- package/plugins/sp/scripts/wrapup-steps.mjs +8 -1
- package/plugins/sp/scripts/wrapup-steps.ts +2 -1
- package/plugins/sp/skills/spur-cli/references/workflows.md +8 -7
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +4 -5
- package/plugins/sp/skills/spur-dev/references/done-housekeeping.md +1 -1
- package/plugins/sp/skills/spur-dev/references/gate-checklists.md +2 -2
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +59 -1
- package/schemas/state-machine-workflow.schema.json +5 -0
- package/schemas/transition-flow-workflow.schema.json +5 -0
- package/spur.js +20988 -20652
- package/web/_astro/BoardApp.CJiqp5pS.js +1 -0
- package/web/_astro/{BoardApp.yW425dRZ.js → BoardApp.yBBcFXWP.js} +4 -4
- package/web/_astro/{TaskDetail.CGwJAinW.js → TaskDetail.DqFJbRFc.js} +1 -1
- package/web/_astro/{arc.B-qNXzSO.js → arc.DL-BpHoi.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.Bdg-xiji.js → architectureDiagram-3BPJPVTR.9IdQYyDq.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.DFArg3Kp.js → blockDiagram-GPEHLZMM.BKsFCqTl.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.BkOLcjyx.js → c4Diagram-AAUBKEIU.DhwI0dh1.js} +1 -1
- package/web/_astro/channel.CX5453qQ.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.DN3c20V4.js → chunk-2J33WTMH.B3QVmQ9S.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.B64K0Ttd.js → chunk-4BX2VUAB.DBSuqs9F.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.C-AJvNwa.js → chunk-55IACEB6.BSYTWAYD.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.DMkr46uS.js → chunk-727SXJPM.cWVuxXfS.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.4_WGbI64.js → chunk-AQP2D5EJ.DpU_Ob3d.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.cbFW_lVE.js → chunk-FMBD7UC4.BykFkyji.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.CDW_-2O1.js → chunk-ND2GUHAM.DwgHlMdY.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.BoYOt_yg.js → chunk-QZHKN3VN.CusXUGWM.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.BNT6XpHQ.js → classDiagram-4FO5ZUOK.fx0ObzkN.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.BNT6XpHQ.js → classDiagram-v2-Q7XG4LA2.fx0ObzkN.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.CI1DQxR3.js → cose-bilkent-S5V4N54A.Z4HgOlsd.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.A8ARH61p.js → cynefin-OW5HDTMX.B5dIZHJu.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.u28ZmqwR.js → dagre-BM42HDAG.DT70Q_Yw.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.Dx8JcrGc.js → diagram-2AECGRRQ.DqHA3XBF.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.BXJUG9GY.js → diagram-5GNKFQAL.BmCem957.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.BtlMuWqW.js → diagram-KO2AKTUF.sn0-hrE0.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.C4T7qaAy.js → diagram-LMA3HP47.BSHe9tVc.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.D5HECE10.js → diagram-OG6HWLK6.DHIc-86k.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.B_hMR3yw.js → erDiagram-TEJ5UH35.Bxayrs7v.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.BAmpcYl3.js → flowDiagram-I6XJVG4X.BkzoE_5I.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.DV1bSWK-.js → ganttDiagram-6RSMTGT7.okT6CvTo.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.DxYKkKrJ.js → gitGraphDiagram-PVQCEYII.CJuYbhC7.js} +1 -1
- package/web/_astro/{infoDiagram-5YYISTIA.BAWY2xMb.js → infoDiagram-5YYISTIA.RqLy7nBo.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.CZssRn6V.js → ishikawaDiagram-YF4QCWOH.BwIcoagw.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.C--muARd.js → journeyDiagram-JHISSGLW.UB1VbWtH.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.D_QCK4et.js → kanban-definition-UN3LZRKU.AaxMKpTk.js} +1 -1
- package/web/_astro/{linear.DzrmTtZ0.js → linear.Nv_xOUjP.js} +1 -1
- package/web/_astro/{mermaid.core.Da03W3iu.js → mermaid.core.Bc4LqQgX.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.6cy-8hR_.js → mindmap-definition-RKZ34NQL.oKUvU_qi.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.C5rS1pdU.js → pieDiagram-4H26LBE5.DQk0oo03.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.w56GZZ6Q.js → quadrantDiagram-W4KKPZXB.BdDjESDa.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.CipX3Pwu.js → requirementDiagram-4Y6WPE33.C2u9hUeH.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.C0VVzgJm.js → sankeyDiagram-5OEKKPKP.CDEoiJST.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.BX2dUUbF.js → sequenceDiagram-3UESZ5HK.D_hT_GAT.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.ypCdgODQ.js → stateDiagram-AJRCARHV.DI8RYG0b.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.In0baEtg.js → stateDiagram-v2-BHNVJYJU.Bkxz4DnP.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.CJN4Vkvl.js → timeline-definition-PNZ67QCA.DSY-kH3-.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.Cf8KkIPY.js → vennDiagram-CIIHVFJN.CpaDtuGr.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.DCZBo9xw.js → wardleyDiagram-YWT4CUSO.DujQWvo8.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.Cm4v_MiJ.js → xychartDiagram-2RQKCTM6.DcM5Y4b9.js} +1 -1
- package/web/index.html +1 -1
- package/config/workflows/basic.yaml +0 -146
- package/config/workflows/docs-pipeline.yaml +0 -350
- package/config/workflows/feature-dev.yaml +0 -288
- package/plugins/sp/scripts/feature-dev-precheck.mjs +0 -146
- package/plugins/sp/scripts/feature-dev-precheck.ts +0 -238
- package/web/_astro/BoardApp.CHenHFia.js +0 -1
- package/web/_astro/channel.CbDHK5UQ.js +0 -1
|
@@ -28,17 +28,6 @@
|
|
|
28
28
|
"source": "Post-merge count (0607 R2: doc-sync + learning-capture consolidated, 2 -> 1). Real-run reading (R5): median 253s, max-sane 2319s (n=44). Budget = 60 min ceiling.",
|
|
29
29
|
"decision": null
|
|
30
30
|
},
|
|
31
|
-
"docs-pipeline": {
|
|
32
|
-
"modelQueries": 2,
|
|
33
|
-
"wallClockMs": null,
|
|
34
|
-
"tokenCostUsd": null,
|
|
35
|
-
"source": "No fixture (0607 option a) and only dry/short runs recorded in history (max 1s, n=4) — wall-clock budget unenforced until a real docs-pipeline run exists. Query budget 2 matches the live workflow definition's query list ['draft','verify'] (added by task 0704; 0607 R3 recorded 1, which is stale against the definition). 0754 R6: FIX, not raise — the value was wrong against the definition, the workflow's declared query count did not change.",
|
|
36
|
-
"decision": {
|
|
37
|
-
"date": "2026-09-03",
|
|
38
|
-
"wbs": "0754",
|
|
39
|
-
"note": "align docs-pipeline.modelQueries to the live workflow definition's query list ['draft','verify']; not a measured-cost raise — the workflow's declared query count was already 2 (added by 0704); the budget was the stale side, not the workflow. Per D8 Decision 11."
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
31
|
"pr-review": {
|
|
43
32
|
"modelQueries": 0,
|
|
44
33
|
"wallClockMs": null,
|
|
@@ -53,26 +42,12 @@
|
|
|
53
42
|
"source": "Coverage entry (0826 R2): the live definition's model-query count (extractResolvedWorkflowFacts, 2026-09-11). No real-run measurement yet: wall-clock and cost are unenforced until measured, never 0.",
|
|
54
43
|
"decision": null
|
|
55
44
|
},
|
|
56
|
-
"feature-dev": {
|
|
57
|
-
"modelQueries": 2,
|
|
58
|
-
"wallClockMs": null,
|
|
59
|
-
"tokenCostUsd": null,
|
|
60
|
-
"source": "Coverage entry (0826 R2): the live definition's model-query count (extractResolvedWorkflowFacts, 2026-09-11). No real-run measurement yet: wall-clock and cost are unenforced until measured, never 0.",
|
|
61
|
-
"decision": null
|
|
62
|
-
},
|
|
63
45
|
"wayfinder-resolution": {
|
|
64
46
|
"modelQueries": 2,
|
|
65
47
|
"wallClockMs": null,
|
|
66
48
|
"tokenCostUsd": null,
|
|
67
49
|
"source": "Coverage entry (0826 R2): the live definition's model-query count (extractResolvedWorkflowFacts, 2026-09-11). No real-run measurement yet: wall-clock and cost are unenforced until measured, never 0.",
|
|
68
50
|
"decision": null
|
|
69
|
-
},
|
|
70
|
-
"basic": {
|
|
71
|
-
"modelQueries": 1,
|
|
72
|
-
"wallClockMs": null,
|
|
73
|
-
"tokenCostUsd": null,
|
|
74
|
-
"source": "Coverage entry (0826 R2): the live definition's model-query count (extractResolvedWorkflowFacts, 2026-09-11). No real-run measurement yet: wall-clock and cost are unenforced until measured, never 0.",
|
|
75
|
-
"decision": null
|
|
76
51
|
}
|
|
77
52
|
}
|
|
78
53
|
}
|
|
@@ -26,11 +26,6 @@
|
|
|
26
26
|
"contract": "standard",
|
|
27
27
|
"twin": "dogfood-testing/validate-report.mjs"
|
|
28
28
|
},
|
|
29
|
-
{
|
|
30
|
-
"rel": "feature-dev-precheck.ts",
|
|
31
|
-
"contract": "standard",
|
|
32
|
-
"twin": "feature-dev-precheck.mjs"
|
|
33
|
-
},
|
|
34
29
|
{
|
|
35
30
|
"rel": "feature-sync-bounded.ts",
|
|
36
31
|
"contract": "standard",
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
$schema: "@gobing-ai/spur/schemas/rule-file.schema.json"
|
|
2
|
+
# Environment-variable hygiene — env access funnels through the config gateway (task 0902).
|
|
3
|
+
#
|
|
4
|
+
# Contract:
|
|
5
|
+
# - `@gobing-ai/ts-utils` (`packages/utils/src/env.ts` in ts-libs) is the single
|
|
6
|
+
# implementation source; `packages/config/src/index.ts` re-exports `getEnvVar` /
|
|
7
|
+
# `getEnvVars` / `setEnvVar` / `removeEnvVar` / `getAppOptions` as the app-side funnel,
|
|
8
|
+
# and NO file in this repo touches `process.env` directly — packages/config included.
|
|
9
|
+
# Env flows into the app as typed config or injected `env` records (ADR-027). Runtime
|
|
10
|
+
# options belong in `.spur/config.yaml` `bootstrap.options` via `getAppOptions`;
|
|
11
|
+
# dev-only knobs belong in consts.
|
|
12
|
+
# - Plugin scripts/hooks import the same functions from `@gobing-ai/ts-utils`; the
|
|
13
|
+
# `superskill script convert` `.mjs` twins bundle them in (self-contained on any
|
|
14
|
+
# install target).
|
|
15
|
+
# - `apps/web` uses `import.meta.env` (Vite browser plane) — not process env, never matches.
|
|
16
|
+
# - Which variables exist and who consumes them is documented in `.env.example`.
|
|
17
|
+
# (The former per-var lease table moved there; the rule now enforces the funnel,
|
|
18
|
+
# not a file-by-var inventory.)
|
|
19
|
+
#
|
|
20
|
+
# Scope: apps/**, packages/**, scripts/**, plugins/sp/** — src AND tests. No exclusions:
|
|
21
|
+
# even the gateway package itself is free of direct access.
|
|
22
|
+
include:
|
|
23
|
+
- "apps/**/src/**/*.{ts,tsx}"
|
|
24
|
+
- "apps/**/tests/**/*.{ts,tsx}"
|
|
25
|
+
- "packages/**/src/**/*.{ts,tsx}"
|
|
26
|
+
- "packages/**/tests/**/*.{ts,tsx}"
|
|
27
|
+
- "scripts/**/*.ts"
|
|
28
|
+
- "plugins/sp/**/*.ts"
|
|
29
|
+
rules:
|
|
30
|
+
- id: env-var-hygiene
|
|
31
|
+
description: >
|
|
32
|
+
No direct `process.env` or `Bun.env` access outside the packages/config gateway.
|
|
33
|
+
Read via `getEnvVar` (single var, undefined-only fallback), seed/compose via
|
|
34
|
+
`getEnvVars()` (live record — also the sanctioned form for setting/deleting
|
|
35
|
+
parent→child contract markers), or take an injected `env` record parameter.
|
|
36
|
+
Aliasing (`const env = process.env`) is caught too — it must touch `process.env`.
|
|
37
|
+
severity: error
|
|
38
|
+
evaluator:
|
|
39
|
+
type: rg
|
|
40
|
+
config:
|
|
41
|
+
pattern: "process\\.env|\\bBun\\.env\\b"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"note": "Transition-shim manifest (task 0541, feature B2). Each entry records one compatibility path marked @transition-shim(<id>) in source. Every field is required. The gate (bun run transition-shim-check,
|
|
2
|
+
"note": "Transition-shim manifest (task 0541, feature B2). Each entry records one compatibility path marked @transition-shim(<id>) in source. Every field is required. The gate (bun run transition-shim-check, in the feature-scoped `spur-check-feature` pass since task 0872) is two-sided: an unregistered marker fails, and a listed entry whose marker is gone fails. Emptying this file is the definition of the transition being complete (docs/04_DESIGN.md §2.5). Removal conditions must be objectively checkable against the repository, not 'when convenient'. Shims are registered by the tasks that create them (0536/0537/0538/0542).",
|
|
3
3
|
"entries": [
|
|
4
4
|
{
|
|
5
5
|
"id": "agent-bare-binary-name",
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"note": "Workflow graph-change promotion candidates (task 0873, feature D62; ADR-076 amendment). A candidate is shadow-run against recorded real-run inputs and is promoted into the canonical definition or deleted by its named deadline — never left as a standing parallel <name>2.yaml. `verdict` is null while pending and is filled by `bun scripts/spur-dev.ts promotion evaluate <id>`; `promotion check` (spur-check-feature) fails any candidate still present past its deadline and any unreferenced parallel definition in config/workflows/.",
|
|
4
|
+
"candidates": [
|
|
5
|
+
{
|
|
6
|
+
"id": "wrapup-contract-violation-pilot-routing",
|
|
7
|
+
"canonical": "wrapup-pipeline",
|
|
8
|
+
"deadline": "2026-10-17",
|
|
9
|
+
"createdAt": "2026-09-17",
|
|
10
|
+
"rationale": "ADR-118 pilot graph change shipped by task 0871 (doc-sync contract-violation routing to the shell-only repair state). Registered by task 0876 to take the promotion decision on its first real-run measurement (R4/R5): wrapup-pipeline run fadca099-25a7-4884-a4ae-923cd0239775 (done, state-machine, definition sha256:cad7ad8b..., v4, created 2026-09-17T15:14:46Z, 14h28m after 0871 landed at 00:46:46Z - the only post-landing terminal non-dry wrapup run) routed doc-sync->learnings-append on the default edge: the doc-sync agent.run exited clean (exitCode 0) and produced its declared expectFile capture (.spur/run/fadca099-...-wrapup-learnings.md), so data.outcome was success, ContractViolationGuardRunner passed:false (packages/app/src/workflow/guards/contract-violation.ts:26), zero workflow.agent.contract-violation system_events, zero failed actions (executor-failure path not taken), no repair status file. Measured absence on real traffic - never fabricated. The change projects delta.agentRunCount=1 (unchanged): the repair edge adds no model hop (repair is a shell that records the miss; ADR-118 forbids re-dispatch), so the ADR-076 cost bar cannot reward it; its justification is ADR-118 routing correctness, evidenced here by the measured absence on the first real run. Disposition (resolve delete, or hold as the pattern-spread precedent for 0873's gate) belongs to the D62 owner before the deadline.",
|
|
11
|
+
"measurement": {
|
|
12
|
+
"workflow": "wrapup-pipeline",
|
|
13
|
+
"runIds": ["fadca099-25a7-4884-a4ae-923cd0239775"]
|
|
14
|
+
},
|
|
15
|
+
"delta": {
|
|
16
|
+
"agentRunCount": 1
|
|
17
|
+
},
|
|
18
|
+
"verdict": {
|
|
19
|
+
"decision": "delete",
|
|
20
|
+
"evaluatedAt": "2026-09-17T15:33:27.274Z",
|
|
21
|
+
"agentRunCount": {
|
|
22
|
+
"runs": 1,
|
|
23
|
+
"mean": 1,
|
|
24
|
+
"median": 1,
|
|
25
|
+
"min": 1,
|
|
26
|
+
"max": 1
|
|
27
|
+
},
|
|
28
|
+
"agentRunDurationMs": {
|
|
29
|
+
"runs": 1,
|
|
30
|
+
"mean": 505937,
|
|
31
|
+
"median": 505937,
|
|
32
|
+
"min": 505937,
|
|
33
|
+
"max": 505937
|
|
34
|
+
},
|
|
35
|
+
"candidateAgentRunCount": 1,
|
|
36
|
+
"canonicalAgentRunCount": 1,
|
|
37
|
+
"reason": "candidate projects 1 agent.run action(s), not fewer than the canonical wrapup-pipeline count of 1 (1 real run(s), median 1 agent.run action(s)/run, median 505937 ms/run) — ADR-076 rejected a graph adding a model hop"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
@@ -62,11 +62,13 @@ transitions:
|
|
|
62
62
|
- from: verifying
|
|
63
63
|
to: done
|
|
64
64
|
description: >
|
|
65
|
-
Feature verified — complete.
|
|
65
|
+
Feature verified — complete. Requires the feature-scoped verification pass
|
|
66
|
+
(ADR-119) to have recorded PASS, then strict check (AC validated,
|
|
67
|
+
traceability clean).
|
|
66
68
|
guard:
|
|
67
69
|
kind: shell
|
|
68
70
|
options:
|
|
69
|
-
command: '$spurBin feature check $featureId --strict --as done'
|
|
71
|
+
command: 'test "$(cat .spur/run/$featureId-feature-verification.status 2>/dev/null)" = PASS && $spurBin feature check $featureId --strict --as done'
|
|
70
72
|
|
|
71
73
|
# Rework: verifying → active (mandatory History entry)
|
|
72
74
|
- from: verifying
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Feature-scoped verification pass (ADR-119, feature D62, task 0872).
|
|
2
|
+
#
|
|
3
|
+
# Owns a lifecycle boundary no per-task graph owns: the repo-wide checks whose
|
|
4
|
+
# invariant spans the repository rather than a single task's diff — corpus
|
|
5
|
+
# consistency, contract baselines, dependency/schema drift, the frozen History
|
|
6
|
+
# surface, and the repo-wide test set. They run once per feature against a
|
|
7
|
+
# settled tree, deliberately the slow pass, and `feature-lifecycle`'s
|
|
8
|
+
# verifying→done guard refuses to complete the feature until this pass records
|
|
9
|
+
# PASS (ADR-119 consequence: a feature is not done until it passes).
|
|
10
|
+
#
|
|
11
|
+
# Run: spur workflow run feature-verification.yaml --vars '{"featureId":"D62"}'
|
|
12
|
+
#
|
|
13
|
+
# Shape: verify -> done | failed. The shell action writes the verdict to
|
|
14
|
+
# `.spur/run/<featureId>-feature-verification.status` and always exits 0; the
|
|
15
|
+
# transition guard reads the status so a missing, corrupt or FAIL verdict routes
|
|
16
|
+
# to `failed` (fail-closed), never a silent PASS.
|
|
17
|
+
#
|
|
18
|
+
# Reliability (aligned with task-pipeline / ADR-043): no agent.run nodes; the
|
|
19
|
+
# verification command is a trusted config var executed via `sh -c` (same
|
|
20
|
+
# surface as task-pipeline's qualityGateCmd — never interpolate untrusted input).
|
|
21
|
+
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
22
|
+
kind: state-machine
|
|
23
|
+
name: feature-verification
|
|
24
|
+
version: "1"
|
|
25
|
+
description: "Feature-scoped verification pass (ADR-119): runs the repo-wide check set once per feature against a settled tree and records PASS/FAIL."
|
|
26
|
+
iterationBound: 2
|
|
27
|
+
initialState: verify
|
|
28
|
+
terminalStates:
|
|
29
|
+
- done
|
|
30
|
+
- failed
|
|
31
|
+
failureStates:
|
|
32
|
+
- failed
|
|
33
|
+
vars:
|
|
34
|
+
featureId: "X"
|
|
35
|
+
spurBin: "spur"
|
|
36
|
+
verificationCmd: "bun run spur-check-feature"
|
|
37
|
+
|
|
38
|
+
states:
|
|
39
|
+
- id: verify
|
|
40
|
+
description: >
|
|
41
|
+
Run the repo-wide check set, record the verdict, always exit 0.
|
|
42
|
+
onEnter:
|
|
43
|
+
- kind: shell
|
|
44
|
+
options:
|
|
45
|
+
command: >-
|
|
46
|
+
mkdir -p .spur/run;
|
|
47
|
+
sh -c "$verificationCmd" > ".spur/run/$featureId-feature-verification.log" 2>&1
|
|
48
|
+
&& printf 'PASS\n' > ".spur/run/$featureId-feature-verification.status"
|
|
49
|
+
|| printf 'FAIL\n' > ".spur/run/$featureId-feature-verification.status";
|
|
50
|
+
exit 0
|
|
51
|
+
|
|
52
|
+
- id: done
|
|
53
|
+
description: Terminal — repo-wide checks passed.
|
|
54
|
+
|
|
55
|
+
- id: failed
|
|
56
|
+
description: Terminal — repo-wide checks failed or the verdict is missing/corrupt.
|
|
57
|
+
|
|
58
|
+
transitions:
|
|
59
|
+
- from: verify
|
|
60
|
+
to: done
|
|
61
|
+
description: Repo-wide checks passed — the feature-scoped verification pass records PASS.
|
|
62
|
+
guard:
|
|
63
|
+
kind: shell
|
|
64
|
+
options:
|
|
65
|
+
command: 'test "$(cat .spur/run/$featureId-feature-verification.status 2>/dev/null)" = PASS'
|
|
66
|
+
- from: verify
|
|
67
|
+
to: failed
|
|
68
|
+
description: Repo-wide checks failed (or the verdict is missing/corrupt) — stop at failed.
|
|
69
|
+
guard:
|
|
70
|
+
kind: always
|
|
@@ -568,12 +568,14 @@ transitions:
|
|
|
568
568
|
- from: ac-generate
|
|
569
569
|
to: system-design
|
|
570
570
|
description: "profile=auto, check passed, design route — run system design."
|
|
571
|
-
# (warn) 4
|
|
571
|
+
# (warn) 5 commands (named status capture + 4 conditions): profile gate + captured status + route signal jointly own the
|
|
572
572
|
# route; guards never re-run the CLI (0769), so there is nothing smaller to extract.
|
|
573
573
|
guard:
|
|
574
574
|
kind: shell
|
|
575
575
|
options:
|
|
576
|
-
command:
|
|
576
|
+
command: >-
|
|
577
|
+
ac_status="$(cat .spur/run/$__runId-idea-ac-check.status 2>/dev/null)";
|
|
578
|
+
test "$profile" = auto && test "$ac_status" = PASS && test "$design" = auto && test "$(jq -r .needs_design .spur/run/$__runId-idea-needs-design.json 2>/dev/null)" != false
|
|
577
579
|
# Auto-skip 2: pass + skip-design route → decompose
|
|
578
580
|
- from: ac-generate
|
|
579
581
|
to: decompose
|
|
@@ -588,22 +590,26 @@ transitions:
|
|
|
588
590
|
- from: ac-generate
|
|
589
591
|
to: ac-generate
|
|
590
592
|
description: "profile=auto, check failed, retry cap not reached — re-run ac-generate."
|
|
591
|
-
# (warn) 4
|
|
593
|
+
# (warn) 5 commands (named status capture + 4 conditions): profile gate + captured status + captured retry count; the
|
|
592
594
|
# retry loop is the smallest honest formulation of cap<3 routing (0769).
|
|
593
595
|
guard:
|
|
594
596
|
kind: shell
|
|
595
597
|
options:
|
|
596
|
-
command:
|
|
598
|
+
command: >-
|
|
599
|
+
ac_status="$(cat .spur/run/$__runId-idea-ac-check.status 2>/dev/null)";
|
|
600
|
+
test "$profile" = auto && test "$ac_status" != PASS && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -lt 3
|
|
597
601
|
# Auto-skip 4: check failed, retry cap reached → escalate to failed
|
|
598
602
|
- from: ac-generate
|
|
599
603
|
to: failed
|
|
600
604
|
description: "profile=auto, check failed after 3 retries — escalate to failed."
|
|
601
|
-
# (warn) 4
|
|
605
|
+
# (warn) 5 commands (named status capture + 4 conditions): mirror of the retry guard with cap>=3; keeping escalation and
|
|
602
606
|
# retry as one test-chain pair makes the cap boundary auditable in the diff (0769).
|
|
603
607
|
guard:
|
|
604
608
|
kind: shell
|
|
605
609
|
options:
|
|
606
|
-
command:
|
|
610
|
+
command: >-
|
|
611
|
+
ac_status="$(cat .spur/run/$__runId-idea-ac-check.status 2>/dev/null)";
|
|
612
|
+
test "$profile" = auto && test "$ac_status" != PASS && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -ge 3
|
|
607
613
|
# Interactive fallback: enter feature-check HITL gate
|
|
608
614
|
- from: ac-generate
|
|
609
615
|
to: feature-check
|
|
@@ -616,12 +622,14 @@ transitions:
|
|
|
616
622
|
- from: feature-check
|
|
617
623
|
to: system-design
|
|
618
624
|
description: Feature check passed, design route — run system design.
|
|
619
|
-
# (warn) 4
|
|
625
|
+
# (warn) 5 commands (named status capture + 4 conditions): HITL answer + captured status + design route; same shape as the
|
|
620
626
|
# ac-generate auto-skips, reading the same captured signal files (0769).
|
|
621
627
|
guard:
|
|
622
628
|
kind: shell
|
|
623
629
|
options:
|
|
624
|
-
command:
|
|
630
|
+
command: >-
|
|
631
|
+
ac_status="$(cat .spur/run/$__runId-idea-ac-check.status 2>/dev/null)";
|
|
632
|
+
test "$__hitlAnswer" = yes && test "$ac_status" = PASS && test "$design" = auto && test "$(jq -r .needs_design .spur/run/$__runId-idea-needs-design.json 2>/dev/null)" != false
|
|
625
633
|
- from: feature-check
|
|
626
634
|
to: decompose
|
|
627
635
|
description: Feature check passed, skip-design route — go directly to decompose.
|
|
@@ -634,20 +642,24 @@ transitions:
|
|
|
634
642
|
- from: feature-check
|
|
635
643
|
to: ac-generate
|
|
636
644
|
description: "Feature check failed — revise AC (retry cap: 3)."
|
|
637
|
-
# (warn) 4
|
|
645
|
+
# (warn) 5 commands (named status capture + 4 conditions): HITL reject/failed-status OR-pair + captured retry count; the
|
|
638
646
|
# interactive retry mirror of the ac-generate cap<3 guard (0769).
|
|
639
647
|
guard:
|
|
640
648
|
kind: shell
|
|
641
649
|
options:
|
|
642
|
-
command:
|
|
650
|
+
command: >-
|
|
651
|
+
ac_status="$(cat .spur/run/$__runId-idea-ac-check.status 2>/dev/null)";
|
|
652
|
+
(test "$__hitlAnswer" = no || test "$ac_status" != PASS) && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -lt 3
|
|
643
653
|
- from: feature-check
|
|
644
654
|
to: failed
|
|
645
655
|
description: Feature check failed after 3 retries — escalate to failed.
|
|
646
|
-
# (warn) 4
|
|
656
|
+
# (warn) 5 commands (named status capture + 4 conditions): mirror of the revise guard with cap>=3 (0769).
|
|
647
657
|
guard:
|
|
648
658
|
kind: shell
|
|
649
659
|
options:
|
|
650
|
-
command:
|
|
660
|
+
command: >-
|
|
661
|
+
ac_status="$(cat .spur/run/$__runId-idea-ac-check.status 2>/dev/null)";
|
|
662
|
+
(test "$__hitlAnswer" = no || test "$ac_status" != PASS) && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -ge 3
|
|
651
663
|
- from: feature-check
|
|
652
664
|
to: cancelled
|
|
653
665
|
description: Operator cancelled the feature-check gate.
|
|
@@ -603,10 +603,14 @@ transitions:
|
|
|
603
603
|
- from: precheck
|
|
604
604
|
to: implement
|
|
605
605
|
description: Deterministic size, evidence, and task checks are green — begin implementation.
|
|
606
|
+
# (warn) 5 commands: named size/evidence status reads + task check (legibility, 0874).
|
|
606
607
|
guard:
|
|
607
608
|
kind: shell
|
|
608
609
|
options:
|
|
609
|
-
command:
|
|
610
|
+
command: >-
|
|
611
|
+
size_status="$(cat .spur/run/$wbs-precheck-size.status 2>/dev/null)";
|
|
612
|
+
evidence_status="$(cat .spur/run/$wbs-precheck-evidence.status 2>/dev/null)";
|
|
613
|
+
test "$size_status" = PASS && test "$evidence_status" = PASS && $spurBin task check $wbs
|
|
610
614
|
- from: precheck
|
|
611
615
|
to: failed
|
|
612
616
|
description: Size and/or task check failed — stop before implement.
|
|
@@ -626,14 +630,18 @@ transitions:
|
|
|
626
630
|
guard:
|
|
627
631
|
kind: shell
|
|
628
632
|
options:
|
|
629
|
-
command:
|
|
633
|
+
command: >-
|
|
634
|
+
gate_status="$(cat .spur/run/$wbs-test-gate.status 2>/dev/null)";
|
|
635
|
+
test "$gate_status" = PASS && test "$mode" = fast
|
|
630
636
|
- from: test
|
|
631
637
|
to: review
|
|
632
638
|
description: Quality gate already green and safety mode — proceed to review.
|
|
633
639
|
guard:
|
|
634
640
|
kind: shell
|
|
635
641
|
options:
|
|
636
|
-
command:
|
|
642
|
+
command: >-
|
|
643
|
+
gate_status="$(cat .spur/run/$wbs-test-gate.status 2>/dev/null)";
|
|
644
|
+
test "$gate_status" = PASS && test "$mode" != fast
|
|
637
645
|
- from: test
|
|
638
646
|
to: test-fix
|
|
639
647
|
description: Quality gate red — start bounded fixall loop.
|
|
@@ -659,28 +667,40 @@ transitions:
|
|
|
659
667
|
guard:
|
|
660
668
|
kind: shell
|
|
661
669
|
options:
|
|
662
|
-
command:
|
|
670
|
+
command: >-
|
|
671
|
+
gate_status="$(cat .spur/run/$wbs-test-gate.status 2>/dev/null)";
|
|
672
|
+
test "$gate_status" = PASS && test "$mode" = fast
|
|
663
673
|
- from: test-recheck
|
|
664
674
|
to: review
|
|
665
675
|
description: Quality gate green after fixall and safety mode — proceed to review.
|
|
666
676
|
guard:
|
|
667
677
|
kind: shell
|
|
668
678
|
options:
|
|
669
|
-
command:
|
|
679
|
+
command: >-
|
|
680
|
+
gate_status="$(cat .spur/run/$wbs-test-gate.status 2>/dev/null)";
|
|
681
|
+
test "$gate_status" = PASS && test "$mode" != fast
|
|
670
682
|
- from: test-recheck
|
|
671
683
|
to: test-fix
|
|
672
684
|
description: Still red and under qualityGateMaxFixAttempts — another fixall hop.
|
|
685
|
+
# (warn) 5 commands: named gate status + fix attempts (legibility, 0874).
|
|
673
686
|
guard:
|
|
674
687
|
kind: shell
|
|
675
688
|
options:
|
|
676
|
-
command:
|
|
689
|
+
command: >-
|
|
690
|
+
gate_status="$(cat .spur/run/$wbs-test-gate.status 2>/dev/null)";
|
|
691
|
+
fix_attempts="$(cat .spur/run/$wbs-test-fix-attempt 2>/dev/null || echo 0)";
|
|
692
|
+
test "$gate_status" = FAIL && test "$fix_attempts" -lt "$qualityGateMaxFixAttempts"
|
|
677
693
|
- from: test-recheck
|
|
678
694
|
to: failed
|
|
679
695
|
description: Still red after max fixall attempts — stop at failed (not silent abort).
|
|
696
|
+
# (warn) 5 commands: named gate status + fix attempts (legibility, 0874).
|
|
680
697
|
guard:
|
|
681
698
|
kind: shell
|
|
682
699
|
options:
|
|
683
|
-
command:
|
|
700
|
+
command: >-
|
|
701
|
+
gate_status="$(cat .spur/run/$wbs-test-gate.status 2>/dev/null)";
|
|
702
|
+
fix_attempts="$(cat .spur/run/$wbs-test-fix-attempt 2>/dev/null || echo 0)";
|
|
703
|
+
test "$gate_status" = FAIL && test "$fix_attempts" -ge "$qualityGateMaxFixAttempts"
|
|
684
704
|
# Defense: corrupt recheck status — failed, not review.
|
|
685
705
|
- from: test-recheck
|
|
686
706
|
to: failed
|
|
@@ -748,7 +768,16 @@ transitions:
|
|
|
748
768
|
kind: shell
|
|
749
769
|
options:
|
|
750
770
|
command: >-
|
|
751
|
-
jq -e --arg d "$proofDigest" --arg r "$__runId" --arg dd "$__definitionDigest" '
|
|
771
|
+
jq -e --arg d "$proofDigest" --arg r "$__runId" --arg dd "$__definitionDigest" '
|
|
772
|
+
.verdict == "PASS"
|
|
773
|
+
and (.proof.digest // "") == $d
|
|
774
|
+
and (.proof.stages.qualityGate.digest // "") == $d
|
|
775
|
+
and (.proof.stages.review.digest // "") == $d
|
|
776
|
+
and (.proof.stages.review.status // "") == "completed"
|
|
777
|
+
and (.proof.stages.verification.digest // "") == $d
|
|
778
|
+
and (.proof.runId // "") == $r
|
|
779
|
+
and (.proof.definitionDigest // "") == $dd
|
|
780
|
+
' ".spur/run/$wbs-verdict.json" >/dev/null 2>&1
|
|
752
781
|
- from: verify
|
|
753
782
|
to: test-fix
|
|
754
783
|
description: >-
|
|
@@ -760,8 +789,8 @@ transitions:
|
|
|
760
789
|
kind: shell
|
|
761
790
|
options:
|
|
762
791
|
command: >-
|
|
763
|
-
|
|
764
|
-
test -n "$
|
|
792
|
+
verdict="$(jq -r .verdict .spur/run/$wbs-verdict.json 2>/dev/null)";
|
|
793
|
+
test -n "$verdict" && test "$verdict" != PASS &&
|
|
765
794
|
test "$(cat .spur/run/$wbs-test-fix-attempt 2>/dev/null || echo 0)" -lt "$qualityGateMaxFixAttempts"
|
|
766
795
|
- from: verify
|
|
767
796
|
to: failed
|
|
@@ -778,6 +807,7 @@ transitions:
|
|
|
778
807
|
- from: record
|
|
779
808
|
to: done
|
|
780
809
|
description: Task check passed and the verdict proof block still names the captured digest — certify done.
|
|
810
|
+
# (warn) 5 commands: named verdict + proof digest reads after the task check (legibility, 0874).
|
|
781
811
|
guard:
|
|
782
812
|
kind: shell
|
|
783
813
|
options:
|
|
@@ -788,8 +818,9 @@ transitions:
|
|
|
788
818
|
# one shell line.
|
|
789
819
|
command: >-
|
|
790
820
|
$spurBin task check $wbs --as done &&
|
|
791
|
-
|
|
792
|
-
|
|
821
|
+
verdict="$(jq -r .verdict .spur/run/$wbs-verdict.json 2>/dev/null)" &&
|
|
822
|
+
proof_digest="$(jq -r '.proof.digest // ""' .spur/run/$wbs-verdict.json 2>/dev/null)" &&
|
|
823
|
+
test "$verdict" = PASS && test "$proof_digest" = "$proofDigest"
|
|
793
824
|
- from: record
|
|
794
825
|
to: failed
|
|
795
826
|
description: Task check failed or proof evidence missing/malformed/mismatched — block before done.
|
|
@@ -13,10 +13,12 @@
|
|
|
13
13
|
# pauses,
|
|
14
14
|
# even under --auto (Iron Law #6: irreversible action -> surface to human).
|
|
15
15
|
#
|
|
16
|
-
# Shape: start -> task-resolve -> doc-sync -> metrics-record
|
|
16
|
+
# Shape: start -> task-resolve -> doc-sync -> learnings-append -> metrics-record
|
|
17
17
|
# -> feature-transition (conditional: if vars.feature set)
|
|
18
18
|
# -> branch-cleanup (conditional: if vars.merge=true)
|
|
19
19
|
# -> done
|
|
20
|
+
# doc-sync routes a contract violation to `repair` (cheap, no
|
|
21
|
+
# re-dispatch — ADR-118 pilot) and an executor failure to `failed`;
|
|
20
22
|
# task-resolve with a VALIDATED empty list short-circuits to `skipped`;
|
|
21
23
|
# invalid wrap input, an unresolved/non-completed task, a failed metrics
|
|
22
24
|
# capture, a failed required sync, or a failed feature gate routes to
|
|
@@ -72,7 +74,7 @@
|
|
|
72
74
|
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
73
75
|
kind: state-machine
|
|
74
76
|
name: wrapup-pipeline
|
|
75
|
-
version: "
|
|
77
|
+
version: "4"
|
|
76
78
|
description: "Post-execution wrap-up: doc-sync (doc drift + learning capture), metrics, feature-transition, branch-cleanup"
|
|
77
79
|
iterationBound: 10
|
|
78
80
|
initialState: start
|
|
@@ -168,13 +170,16 @@ states:
|
|
|
168
170
|
learnings (task 0607 R2: two adjacent agent.run hops merged, count 2 -> 1;
|
|
169
171
|
no operator-decision pause or verdict boundary sits between them). No dedicated
|
|
170
172
|
slash command yet — free-form skill pointer until a /sp:doc-evolve command
|
|
171
|
-
exists (ADR-043 residual).
|
|
172
|
-
a
|
|
173
|
-
|
|
173
|
+
exists (ADR-043 residual). This is the expensive stage; a contract violation
|
|
174
|
+
(a clean exit that missed its declared answerFile/expectFile post-condition)
|
|
175
|
+
routes to the cheap `repair` edge instead of a full re-dispatch (ADR-118
|
|
176
|
+
pilot, 0871) — `onError: continue` lets the transition guards read the
|
|
177
|
+
agent's result instead of halting the run.
|
|
174
178
|
onEnter:
|
|
175
179
|
# (warn) free-form prompt: no /sp:doc-evolve command surface yet (ADR-043
|
|
176
180
|
# residual, 0607 R2 one-query merge); capture is bounded by expectFile.
|
|
177
181
|
- kind: agent.run
|
|
182
|
+
onError: continue
|
|
178
183
|
options:
|
|
179
184
|
agent: ${vars.agent}
|
|
180
185
|
input: >-
|
|
@@ -192,6 +197,13 @@ states:
|
|
|
192
197
|
answerFile: .spur/run/${vars.__runId}-wrapup-learnings.md
|
|
193
198
|
expectFile: .spur/run/${vars.__runId}-wrapup-learnings.md
|
|
194
199
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
200
|
+
|
|
201
|
+
- id: learnings-append
|
|
202
|
+
description: >
|
|
203
|
+
Best-effort soft append of the captured learnings to .spur/memory/learnings.md
|
|
204
|
+
(0607 R2): an empty capture skips without aborting wrap-up. Runs only after
|
|
205
|
+
doc-sync's contract held; a contract violation never reaches this state.
|
|
206
|
+
onEnter:
|
|
195
207
|
# (warn) learnings append is intentionally soft (empty capture skips
|
|
196
208
|
# without aborting wrap-up, 0607 R2).
|
|
197
209
|
- kind: shell
|
|
@@ -207,6 +219,21 @@ states:
|
|
|
207
219
|
fi &&
|
|
208
220
|
exit 0
|
|
209
221
|
|
|
222
|
+
- id: repair
|
|
223
|
+
description: >
|
|
224
|
+
Contract-violation repair (ADR-118 pilot, 0871): doc-sync's agent.run missed
|
|
225
|
+
its declared post-condition after a clean exit, so wrap-up does NOT re-dispatch
|
|
226
|
+
the expensive agent on this attempt. The miss is recorded to a run-scoped
|
|
227
|
+
repair status and wrap-up proceeds to metrics-record — the run log and action
|
|
228
|
+
trace already name the violated contract and the observed value.
|
|
229
|
+
onEnter:
|
|
230
|
+
- kind: shell
|
|
231
|
+
options:
|
|
232
|
+
command: >-
|
|
233
|
+
mkdir -p .spur/run &&
|
|
234
|
+
printf 'contract-violation: doc-sync learnings capture missed its declared contract — repair skipped re-dispatch (ADR-118 pilot)\n' > ".spur/run/$__runId-wrapup-repair.status" &&
|
|
235
|
+
exit 0
|
|
236
|
+
|
|
210
237
|
- id: metrics-record
|
|
211
238
|
description: >
|
|
212
239
|
Append one JSONL row per task to .spur/memory/wrapup-metrics.jsonl deterministically.
|
|
@@ -354,10 +381,46 @@ transitions:
|
|
|
354
381
|
guard:
|
|
355
382
|
kind: always
|
|
356
383
|
|
|
357
|
-
# ──
|
|
384
|
+
# ── doc-sync: contract-first routing (ADR-118 pilot, 0871) ──
|
|
385
|
+
# Declaration order matters: the contract-violation edge is tried first, then
|
|
386
|
+
# success (action-ok), then the executor-failure defense (always) — a failed
|
|
387
|
+
# agent.run reaches these guards because doc-sync's agent.run declares
|
|
388
|
+
# onError: continue.
|
|
358
389
|
- from: doc-sync
|
|
390
|
+
to: repair
|
|
391
|
+
description: >
|
|
392
|
+
Contract violation (ADR-118): the agent exited 0 but missed its declared
|
|
393
|
+
post-condition. Route to the cheap repair edge — never re-dispatch the
|
|
394
|
+
full agent.run on this attempt.
|
|
395
|
+
trigger: contract-violation
|
|
396
|
+
guard:
|
|
397
|
+
kind: contract-violation
|
|
398
|
+
- from: doc-sync
|
|
399
|
+
to: learnings-append
|
|
400
|
+
description: Doc drift repair + learning capture succeeded — append learnings.
|
|
401
|
+
guard:
|
|
402
|
+
kind: action-ok
|
|
403
|
+
- from: doc-sync
|
|
404
|
+
to: failed
|
|
405
|
+
description: >
|
|
406
|
+
Executor failure (non-zero exit, signal, or dispatch error) — keep the
|
|
407
|
+
existing fail semantics; a contract violation never falls through here
|
|
408
|
+
because the contract-violation edge is declared first.
|
|
409
|
+
trigger: executor-failure
|
|
410
|
+
guard:
|
|
411
|
+
kind: always
|
|
412
|
+
|
|
413
|
+
# ── linear body: learnings-append -> metrics-record ──
|
|
414
|
+
- from: learnings-append
|
|
415
|
+
to: metrics-record
|
|
416
|
+
description: Learning capture appended (or soft-skipped) — record metrics.
|
|
417
|
+
guard:
|
|
418
|
+
kind: always
|
|
419
|
+
|
|
420
|
+
# ── repair: cheap path, no re-dispatch -> metrics-record ──
|
|
421
|
+
- from: repair
|
|
359
422
|
to: metrics-record
|
|
360
|
-
description:
|
|
423
|
+
description: Contract-violation repair recorded — proceed to metrics without re-dispatch.
|
|
361
424
|
guard:
|
|
362
425
|
kind: always
|
|
363
426
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/spur",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.86",
|
|
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",
|
|
@@ -53,14 +53,14 @@
|
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@commander-js/extra-typings": "^14.0.0",
|
|
56
|
-
"@gobing-ai/ts-db": "^0.4.
|
|
57
|
-
"@gobing-ai/ts-ai-runner": "^0.4.
|
|
58
|
-
"@gobing-ai/ts-dual-workflow-engine": "^0.4.
|
|
59
|
-
"@gobing-ai/ts-infra": "^0.4.
|
|
60
|
-
"@gobing-ai/ts-llm-jsonl-importer": "^0.4.
|
|
61
|
-
"@gobing-ai/ts-rule-engine": "^0.4.
|
|
62
|
-
"@gobing-ai/ts-runtime": "^0.4.
|
|
63
|
-
"@gobing-ai/ts-utils": "^0.4.
|
|
56
|
+
"@gobing-ai/ts-db": "^0.4.67",
|
|
57
|
+
"@gobing-ai/ts-ai-runner": "^0.4.67",
|
|
58
|
+
"@gobing-ai/ts-dual-workflow-engine": "^0.4.67",
|
|
59
|
+
"@gobing-ai/ts-infra": "^0.4.67",
|
|
60
|
+
"@gobing-ai/ts-llm-jsonl-importer": "^0.4.67",
|
|
61
|
+
"@gobing-ai/ts-rule-engine": "^0.4.67",
|
|
62
|
+
"@gobing-ai/ts-runtime": "^0.4.67",
|
|
63
|
+
"@gobing-ai/ts-utils": "^0.4.67",
|
|
64
64
|
"@types/bun": "1.3.14",
|
|
65
65
|
"@types/figlet": "^1.7.0",
|
|
66
66
|
"@types/node-notifier": "8.0.5",
|