@gobing-ai/spur 0.3.72 → 0.3.74
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 +48 -27
- package/config/pipeline-budgets.json +9 -5
- package/config/plugin-scripts.json +4 -0
- package/config/proportional-route-table.ts +155 -0
- package/config/rules/strict/runtime-boundaries.yaml +1 -1
- package/config/rules/structure/protected-files.yaml +3 -0
- package/config/task-pipeline-proportional-migration-plan.md +79 -0
- package/config/templates/AGENTS.md +5 -0
- package/config/templates/docs/99_PROJECT_CONSTITUTION.md +2 -0
- package/config/workflows/basic.yaml +5 -2
- package/config/workflows/docs-pipeline.yaml +101 -54
- package/config/workflows/feature-dev.yaml +57 -27
- package/config/workflows/feature-lifecycle.yaml +1 -0
- package/config/workflows/history-anatomy.yaml +8 -4
- package/config/workflows/idea-pipeline.yaml +66 -33
- package/config/workflows/pr-review.yaml +31 -8
- package/config/workflows/task-lifecycle.yaml +28 -15
- package/config/workflows/task-pipeline.yaml +929 -844
- package/config/workflows/wayfinder-resolution.yaml +124 -90
- package/config/workflows/wrapup-pipeline.yaml +290 -107
- package/package.json +9 -9
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/daily-summary/daily-summary.mjs +2 -4
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +2 -4
- package/plugins/sp/scripts/history-anatomy-cache.mjs +7 -1
- package/plugins/sp/scripts/history-anatomy-cache.ts +11 -2
- package/plugins/sp/scripts/inline-pipeline-parity-check.ts +185 -0
- package/plugins/sp/scripts/pr-reviewing.mjs +5 -1
- package/plugins/sp/scripts/pr-reviewing.ts +7 -1
- package/plugins/sp/scripts/task-evidence-precheck.ts +1 -1
- package/plugins/sp/scripts/transition-shim-check.ts +6 -6
- package/plugins/sp/scripts/verify-answer-lint.ts +4 -0
- package/plugins/sp/skills/pr-reviewing/SKILL.md +4 -1
- package/plugins/sp/skills/spur-cli/references/self.md +17 -3
- package/plugins/sp/skills/spur-cli/references/tasks.md +1 -1
- package/plugins/sp/skills/spur-cli/references/workflows/authoring-workflows.md +23 -0
- package/plugins/sp/skills/spur-cli/references/workflows/workflow-fit-and-tuning.md +5 -5
- package/plugins/sp/skills/spur-cli/references/workflows.md +4 -4
- package/plugins/sp/skills/spur-dev/SKILL.md +17 -0
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +3 -3
- package/plugins/sp/skills/spur-dev/references/gate-checklists.md +1 -2
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +20 -0
- package/plugins/sp/skills/wayfinder/SKILL.md +1 -1
- package/schemas/state-machine-workflow.schema.json +3 -1
- package/schemas/transition-flow-workflow.schema.json +3 -1
- package/spur.js +12639 -10106
- package/web/_astro/{BoardApp.BYCNkMOn.js → BoardApp.C60RJZRj.js} +78 -77
- package/web/_astro/BoardApp.ymfj1EHA.js +1 -0
- package/web/_astro/{TaskDetail.CgUreSP2.js → TaskDetail.Guk8VuNu.js} +1 -1
- package/web/_astro/{arc.BySSh34M.js → arc.CAZtlWJM.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.DM46TS_h.js → architectureDiagram-3BPJPVTR.9-XbF_Tz.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.tZhvNUHA.js → blockDiagram-GPEHLZMM.DC4WLx3L.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.PT4Or4Nf.js → c4Diagram-AAUBKEIU.JgjUQMgG.js} +1 -1
- package/web/_astro/channel.Bxxe2Byc.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.J9r0_Bbe.js → chunk-2J33WTMH.D_JFiXa-.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.hzyeIvhR.js → chunk-4BX2VUAB.Bk_RxoeT.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.B0rO7qVh.js → chunk-55IACEB6.a_Rj2VxL.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.wE_Uk5D4.js → chunk-727SXJPM.C2NoS1U6.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.DqEEjQw7.js → chunk-AQP2D5EJ.D1Xn6CNa.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.CDoD9sBX.js → chunk-FMBD7UC4.BhJrSBsX.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.CtX5nF9P.js → chunk-ND2GUHAM.ByVGduYQ.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.CK_EwfaT.js → chunk-QZHKN3VN.B47Paunq.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.DLt5a8Lh.js → classDiagram-4FO5ZUOK.BERMSD5C.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.DLt5a8Lh.js → classDiagram-v2-Q7XG4LA2.BERMSD5C.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.CMCWP49h.js → cose-bilkent-S5V4N54A.O_rHGlhU.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.HyXw_vdS.js → cynefin-OW5HDTMX.qCW2GdNp.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.BTuAzh01.js → dagre-BM42HDAG.C5Y4lH_u.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.D9dr9wfT.js → diagram-2AECGRRQ.DxZmRXxF.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.C4Rot0hj.js → diagram-5GNKFQAL.qSxZeYSK.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.B_TK5uWC.js → diagram-KO2AKTUF.-7vl3bXs.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.JkXKK7CO.js → diagram-LMA3HP47.BHVV-UH3.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.BzMN8Bd6.js → diagram-OG6HWLK6.DHAuW9sK.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.DVZaWGUd.js → erDiagram-TEJ5UH35.DMRLQwPN.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.rjEiWUfR.js → flowDiagram-I6XJVG4X.B3EpPp_8.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.C_EgAarK.js → ganttDiagram-6RSMTGT7.BgUrExXM.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.B-QQSDsK.js → gitGraphDiagram-PVQCEYII.CU-9yVN4.js} +1 -1
- package/web/_astro/{index.B5MTfe7k.css → index.BhvM3djt.css} +1 -1
- package/web/_astro/{infoDiagram-5YYISTIA.DlWesz7T.js → infoDiagram-5YYISTIA.CvVTCRLe.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.BUMZOawi.js → ishikawaDiagram-YF4QCWOH.BUzOfKPf.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.CWfkxfjY.js → journeyDiagram-JHISSGLW.BBTb7ziz.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.B-YpMwXf.js → kanban-definition-UN3LZRKU.CTQQr70i.js} +1 -1
- package/web/_astro/{linear.D7uqzENp.js → linear.CLKlJPiS.js} +1 -1
- package/web/_astro/{mermaid.core.CxrNppBD.js → mermaid.core.BA4wFhLP.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.B4Qe7cM2.js → mindmap-definition-RKZ34NQL.t1vG2l5e.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.Ds-5j2ro.js → pieDiagram-4H26LBE5.C8zJhyTu.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.tBd38uNC.js → quadrantDiagram-W4KKPZXB.DqYYvb7T.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.sFENkWl3.js → requirementDiagram-4Y6WPE33.lzfDZ9nI.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.BeB-Hk7C.js → sankeyDiagram-5OEKKPKP.lm6N5ORM.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.DnTeaSpx.js → sequenceDiagram-3UESZ5HK.E6_IV4Dj.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.B-8Jt5EJ.js → stateDiagram-AJRCARHV.Bnv6Ok2p.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.Br7xoqMW.js → stateDiagram-v2-BHNVJYJU.mJHH_Ng_.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.C-3WdOyi.js → timeline-definition-PNZ67QCA.BhRgqSLa.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.DCIs7Lc6.js → vennDiagram-CIIHVFJN.pjb5DMC9.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.rGAL-bbz.js → wardleyDiagram-YWT4CUSO.CW20KUng.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.hkfQKiRl.js → xychartDiagram-2RQKCTM6.vX9_yuMl.js} +1 -1
- package/web/index.html +2 -2
- package/config/corpus-baseline.json +0 -1637
- package/config/workflow-composition-baseline.json +0 -576
- package/web/_astro/BoardApp.E12MFjOS.js +0 -1
- package/web/_astro/channel.5cYKr5cs.js +0 -1
|
@@ -1,48 +1,74 @@
|
|
|
1
|
-
# Wrap-up pipeline — post-execution wrap-up for one task or a batch (design
|
|
1
|
+
# Wrap-up pipeline — post-execution wrap-up for one task or a batch (design
|
|
2
|
+
# §wrapup-pipeline).
|
|
2
3
|
#
|
|
3
|
-
# Orchestration is configuration (ADR-022 / §3.2): this is YAML over the
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
4
|
+
# Orchestration is configuration (ADR-022 / §3.2): this is YAML over the
|
|
5
|
+
# existing
|
|
6
|
+
# dual-workflow engine — zero new engine code. The pipeline NEVER mutates task
|
|
7
|
+
# status —
|
|
8
|
+
# wrap-up consumes completed tasks and produces learning/metrics/doc artifacts
|
|
9
|
+
# only.
|
|
10
|
+
# Feature transitions go through `spur feature update` so the feature-lifecycle
|
|
11
|
+
# guards
|
|
12
|
+
# apply identically. Branch cleanup is an irreversible HITL gate — it always
|
|
13
|
+
# pauses,
|
|
8
14
|
# even under --auto (Iron Law #6: irreversible action -> surface to human).
|
|
9
15
|
#
|
|
10
16
|
# Shape: start -> task-resolve -> doc-sync -> metrics-record
|
|
11
17
|
# -> feature-transition (conditional: if vars.feature set)
|
|
12
18
|
# -> branch-cleanup (conditional: if vars.merge=true)
|
|
13
19
|
# -> done
|
|
14
|
-
#
|
|
20
|
+
# task-resolve with a VALIDATED empty list short-circuits to `skipped`;
|
|
21
|
+
# invalid wrap input, an unresolved/non-completed task, a failed metrics
|
|
22
|
+
# capture, a failed required sync, or a failed feature gate routes to
|
|
23
|
+
# `failed` (0770) instead of being absorbed by an always-defensive skip.
|
|
15
24
|
#
|
|
16
25
|
# Vars (passed as a JSON object via `--vars`):
|
|
17
|
-
#
|
|
18
|
-
#
|
|
26
|
+
# tasks — JSON array of WBS strings, passed as a JSON-encoded STRING value
|
|
27
|
+
# (the CLI
|
|
28
|
+
# rejects non-string --vars values), e.g. --vars '{"tasks":"[\"0167\"]"}'
|
|
19
29
|
# feature — feature id to advance through legal lifecycle edges (optional)
|
|
20
30
|
# profile — set --vars '{"profile":"auto"}' to skip objective confirmations
|
|
21
|
-
#
|
|
31
|
+
# merge — set --vars '{"merge":"true"}' to run branch cleanup (irreversible
|
|
32
|
+
# HITL)
|
|
22
33
|
# spurBin — PATH-independent spur invocation (overridden by CLI at run start)
|
|
23
34
|
# agent — agent for agent.run steps (default: omp)
|
|
24
35
|
#
|
|
25
36
|
# Reliability (aligned with task-pipeline / ADR-043):
|
|
26
|
-
#
|
|
37
|
+
# - Prefer pure slash commands when a command exists; free-form inputs remain
|
|
38
|
+
# only
|
|
27
39
|
# where capture artifacts (answerFile) have no dedicated slash surface yet.
|
|
28
40
|
# - doc-sync / metrics-record use expectFile + soft append (empty capture
|
|
29
|
-
#
|
|
30
|
-
#
|
|
41
|
+
# does not abort wrap-up after prior steps already landed). The doc-sync hop is
|
|
42
|
+
# a
|
|
43
|
+
# single model query that both repairs doc drift (sp:doc-evolve) and captures
|
|
31
44
|
# working learnings (task 0607 R2 — one query answers both; count 2 -> 1).
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
45
|
+
# - feature-transition is a soft shell (always exit 0); a failed required sync
|
|
46
|
+
# or feature gate records FAIL and routes to `failed` instead of claiming
|
|
47
|
+
# success (0770).
|
|
48
|
+
# - 0770 truthful outcomes: vars.tasks is parsed and validated ONCE at
|
|
49
|
+
# task-resolve (JSON array of non-empty WBS strings, every member resolved
|
|
50
|
+
# via `task show` to a completed status); siblings consume the normalized
|
|
51
|
+
# run-scoped artifact and never re-parse raw input (malformed input can no
|
|
52
|
+
# longer be silently re-interpreted as an empty list). All captures are
|
|
53
|
+
# run-scoped under .spur/run/<runId>-wrapup-*; a lookup failure is
|
|
54
|
+
# recorded, never silently omitted as success.
|
|
55
|
+
# - branch-cleanup HITL is exhaustive (yes/no/cancel → done; missing answer
|
|
56
|
+
# defense) and consent-only: it records the decision, performs no git op.
|
|
35
57
|
# - Wrap-up never mutates task status (consumes completed work only).
|
|
36
58
|
|
|
37
59
|
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
38
60
|
kind: state-machine
|
|
39
61
|
name: wrapup-pipeline
|
|
62
|
+
version: "1"
|
|
40
63
|
description: "Post-execution wrap-up: doc-sync (doc drift + learning capture), metrics, feature-transition, branch-cleanup"
|
|
41
64
|
iterationBound: 10
|
|
42
65
|
initialState: start
|
|
43
66
|
terminalStates:
|
|
44
67
|
- done
|
|
45
68
|
- skipped
|
|
69
|
+
- failed
|
|
70
|
+
failureStates:
|
|
71
|
+
- failed
|
|
46
72
|
vars:
|
|
47
73
|
tasks: "[]"
|
|
48
74
|
feature: ""
|
|
@@ -50,20 +76,14 @@ vars:
|
|
|
50
76
|
merge: "false"
|
|
51
77
|
spurBin: "spur"
|
|
52
78
|
agent: "auto"
|
|
79
|
+
__runId: ""
|
|
80
|
+
mode: ""
|
|
53
81
|
stepTimeoutMs: "1800000"
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
# sweep that observes feature-level findings — so a transition that arms one
|
|
57
|
-
# would go green. This gate is therefore the CORPUS SWEEP ALONE (~29 s), not
|
|
58
|
-
# `spur-check-new`: every task in the feature already paid a full `spur-check`
|
|
59
|
-
# in its own pipeline, and the states that run before this hop (`doc-sync`,
|
|
60
|
-
# `metrics-record`) write only markdown and task sections, which Biome skips
|
|
61
|
-
# (`ignoreUnknown: true`). Re-running the ~105 s per-task gate here re-verifies
|
|
62
|
-
# unchanged code and measures nothing new. Soft by design: the gate reports
|
|
63
|
-
# PASS/FAIL and lets the operator decide; it never hard-fails the wrap-up shell.
|
|
82
|
+
# Recheck the affected feature after sync (D61 / ADR-108), not the corpus
|
|
83
|
+
# or full code suite. Explicit caller overrides remain supported.
|
|
64
84
|
# TRUSTED CONFIG ONLY — this string is executed via `sh -c` (same surface as
|
|
65
85
|
# task-pipeline's qualityGateCmd). Never interpolate untrusted input into it.
|
|
66
|
-
featureGateCmd:
|
|
86
|
+
featureGateCmd: '$spurBin feature check "$feature"'
|
|
67
87
|
__hitlAnswer: ""
|
|
68
88
|
|
|
69
89
|
states:
|
|
@@ -78,13 +98,92 @@ states:
|
|
|
78
98
|
|
|
79
99
|
- id: task-resolve
|
|
80
100
|
description: >
|
|
81
|
-
Validate the task list
|
|
82
|
-
|
|
83
|
-
|
|
101
|
+
Validate the task list ONCE and evaluate closed proportional routing (0758
|
|
102
|
+
R1-R4). Validation (0770): vars.tasks must be a JSON array of non-empty
|
|
103
|
+
WBS strings, deduplicated in first-seen order, and every member must
|
|
104
|
+
resolve via `spur task show` to a completed status (done/cancelled).
|
|
105
|
+
Invalid input or an unresolved/non-completed task records FAIL (reason
|
|
106
|
+
into the run-scoped reason file) and routes to `failed`; siblings consume
|
|
107
|
+
the normalized run-scoped artifact .spur/run/<runId>-wrapup-tasks.json
|
|
108
|
+
and never re-parse raw input. A validated empty list -> skipped;
|
|
109
|
+
tasks > 0 && mode == fast -> fast-path (metrics-record, bypassing
|
|
110
|
+
doc-sync); missing/unknown/conflict -> doc-sync (safety-path). Writes
|
|
111
|
+
bounded machine-readable reason to reasonFile.
|
|
84
112
|
onEnter:
|
|
85
113
|
- kind: note
|
|
86
114
|
options:
|
|
87
|
-
message: "Resolving task list: ${vars.tasks}"
|
|
115
|
+
message: "Resolving task list: ${vars.tasks} (proportional mode: ${vars.mode})"
|
|
116
|
+
# Run-id confinement (0758 R3). The run-scoped reason file is the only route artifact: a
|
|
117
|
+
# second copy at the fixed path `.spur/run/wrapup-route-reason.txt` had no reader and was
|
|
118
|
+
# overwritten by whichever run finished last, so a route claim read from it belonged to no
|
|
119
|
+
# particular run. The log append carries the run id for the same reason — an unattributed
|
|
120
|
+
# line is log scraping, which R5 rejects as evidence.
|
|
121
|
+
- kind: shell
|
|
122
|
+
options:
|
|
123
|
+
command: >-
|
|
124
|
+
mkdir -p .spur/run .spur/memory &&
|
|
125
|
+
RUN_ID="$__runId" &&
|
|
126
|
+
if [ -z "$RUN_ID" ]; then RUN_ID="wrapup"; fi &&
|
|
127
|
+
REASON_FILE=".spur/run/$RUN_ID-route-reason.txt" &&
|
|
128
|
+
if [ "$(echo "$tasks" | jq length 2>/dev/null || echo 0)" -eq 0 ]; then
|
|
129
|
+
echo "skipped:empty task list" > "$REASON_FILE";
|
|
130
|
+
elif [ "$mode" = "fast" ]; then
|
|
131
|
+
echo "fast:evidence complete+consistent" > "$REASON_FILE";
|
|
132
|
+
elif [ -z "$mode" ]; then
|
|
133
|
+
echo "safety:missing evidence (mode empty)" > "$REASON_FILE";
|
|
134
|
+
elif [ "$mode" = "unknown" ]; then
|
|
135
|
+
echo "safety:unknown evidence quality" > "$REASON_FILE";
|
|
136
|
+
elif [ "$mode" = "conflict" ]; then
|
|
137
|
+
echo "safety:conflicting evidence" > "$REASON_FILE";
|
|
138
|
+
else
|
|
139
|
+
echo "safety:unrecognized evidence (mode=$mode)" > "$REASON_FILE";
|
|
140
|
+
fi &&
|
|
141
|
+
printf '%s %s\n' "$RUN_ID" "$(cat "$REASON_FILE")"
|
|
142
|
+
>> .spur/memory/wrapup-routes.log &&
|
|
143
|
+
exit 0
|
|
144
|
+
# 0770 truthful input: parse and validate vars.tasks exactly once, here.
|
|
145
|
+
# Malformed JSON, a non-array, a non-string or empty entry, an unresolved
|
|
146
|
+
# task, or a task not at a completed status records FAIL (routed to
|
|
147
|
+
# `failed` by the first declared edge) and is never re-interpreted as an
|
|
148
|
+
# empty list by a later sibling guard. The normalized, deduplicated list
|
|
149
|
+
# is persisted run-scoped; metrics/doc siblings only ever read it.
|
|
150
|
+
- kind: shell
|
|
151
|
+
options:
|
|
152
|
+
command: >-
|
|
153
|
+
mkdir -p .spur/run &&
|
|
154
|
+
RUN_ID="$__runId" &&
|
|
155
|
+
if [ -z "$RUN_ID" ]; then
|
|
156
|
+
echo "task-resolve: __runId is empty — refusing the legacy fixed-path fallback" >&2;
|
|
157
|
+
exit 1;
|
|
158
|
+
fi &&
|
|
159
|
+
REASON_FILE=".spur/run/$RUN_ID-route-reason.txt" &&
|
|
160
|
+
STATUS_FILE=".spur/run/$RUN_ID-wrapup-resolve.status" &&
|
|
161
|
+
TASKS_FILE=".spur/run/$RUN_ID-wrapup-tasks.json" &&
|
|
162
|
+
if ! printf '%s' "$tasks" | jq -e 'type == "array" and all(.[]; type == "string" and length > 0)' > /dev/null 2>&1; then
|
|
163
|
+
echo "task-resolve: tasks must be a JSON array of non-empty WBS strings" >&2;
|
|
164
|
+
echo "failed:tasks is not a JSON array of non-empty WBS strings" > "$REASON_FILE";
|
|
165
|
+
printf 'FAIL\n' > "$STATUS_FILE";
|
|
166
|
+
else
|
|
167
|
+
printf '%s' "$tasks" | jq -c 'reduce .[] as $w ([]; if any(.[]; . == $w) then . else . + [$w] end)' > "$TASKS_FILE";
|
|
168
|
+
RESOLVE_RC=0;
|
|
169
|
+
for wbs in $(jq -r '.[]' "$TASKS_FILE"); do
|
|
170
|
+
TASK_JSON=$($spurBin task show "$wbs" --json 2>/dev/null || true);
|
|
171
|
+
TASK_STATUS=$(printf '%s' "$TASK_JSON" | jq -r '.frontmatter.status // .status // empty' 2>/dev/null || true);
|
|
172
|
+
if [ "$TASK_STATUS" != "done" ] && [ "$TASK_STATUS" != "cancelled" ]; then
|
|
173
|
+
STATUS_DISPLAY="$TASK_STATUS";
|
|
174
|
+
if [ -z "$STATUS_DISPLAY" ]; then STATUS_DISPLAY=unresolved; fi;
|
|
175
|
+
echo "task-resolve: task $wbs did not resolve to a completed status (status=$STATUS_DISPLAY)" >&2;
|
|
176
|
+
RESOLVE_RC=1;
|
|
177
|
+
fi;
|
|
178
|
+
done;
|
|
179
|
+
if [ "$RESOLVE_RC" -eq 0 ]; then
|
|
180
|
+
printf 'PASS\n' > "$STATUS_FILE";
|
|
181
|
+
else
|
|
182
|
+
echo "failed:unresolved or non-completed task (see $TASKS_FILE)" > "$REASON_FILE";
|
|
183
|
+
printf 'FAIL\n' > "$STATUS_FILE";
|
|
184
|
+
fi;
|
|
185
|
+
fi;
|
|
186
|
+
exit 0
|
|
88
187
|
|
|
89
188
|
- id: doc-sync
|
|
90
189
|
description: >
|
|
@@ -105,71 +204,92 @@ states:
|
|
|
105
204
|
docs/00_ADR.md, docs/03_ARCHITECTURE.md, docs/04_DESIGN.md, docs/design/* per constitution edit
|
|
106
205
|
rules; do not write task/feature corpus. THEN extract working learnings from tasks ${vars.tasks} —
|
|
107
206
|
conventions, errors fixed, patterns, gotchas, grouped by date and task WBS — as raw markdown
|
|
108
|
-
(no fences) and END your final message with that markdown (it is captured to
|
|
109
|
-
|
|
207
|
+
(no fences) and END your final message with that markdown (it is captured to the
|
|
208
|
+
run-scoped .spur/run/${vars.__runId}-wrapup-learnings.md).
|
|
209
|
+
# Declared Layer-1 role (0538 R2): routing reason beside the agent:
|
|
210
|
+
# pin.
|
|
110
211
|
role: coder
|
|
111
|
-
answerFile: .spur/run
|
|
112
|
-
expectFile: .spur/run
|
|
212
|
+
answerFile: .spur/run/${vars.__runId}-wrapup-learnings.md
|
|
213
|
+
expectFile: .spur/run/${vars.__runId}-wrapup-learnings.md
|
|
113
214
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
114
215
|
- kind: shell
|
|
115
216
|
options:
|
|
116
217
|
command: >-
|
|
117
|
-
mkdir -p .spur/memory &&
|
|
118
|
-
|
|
119
|
-
|
|
218
|
+
mkdir -p .spur/run .spur/memory &&
|
|
219
|
+
LEARNINGS_FILE=".spur/run/$__runId-wrapup-learnings.md" &&
|
|
220
|
+
if test -s "$LEARNINGS_FILE"; then
|
|
221
|
+
cat "$LEARNINGS_FILE" >> .spur/memory/learnings.md &&
|
|
120
222
|
printf '\n' >> .spur/memory/learnings.md;
|
|
121
223
|
else
|
|
122
|
-
echo "wrapup: learning capture missing or empty at $PWD
|
|
224
|
+
echo "wrapup: learning capture missing or empty at $PWD/$LEARNINGS_FILE - append skipped" >&2;
|
|
123
225
|
fi &&
|
|
124
226
|
exit 0
|
|
125
227
|
|
|
126
228
|
- id: metrics-record
|
|
127
229
|
description: >
|
|
128
230
|
Append one JSONL row per task to .spur/memory/wrapup-metrics.jsonl deterministically.
|
|
231
|
+
Reads ONLY the normalized run-scoped task list written by task-resolve (0770):
|
|
232
|
+
a lookup failure for any member records FAIL and routes to `failed` — a
|
|
233
|
+
missing metrics row is never silently absorbed as success.
|
|
129
234
|
onEnter:
|
|
130
235
|
- kind: shell
|
|
131
236
|
options:
|
|
132
237
|
command: >-
|
|
133
|
-
mkdir -p .spur/memory &&
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
fi;
|
|
146
|
-
ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ");
|
|
147
|
-
printf '{"wbs":"%s","feature_id":"%s","status":"%s","verdict":"%s","timestamp":"%s"}\n' "$wbs" "$feat" "$stat" "$verdict" "$ts" >> .spur/memory/wrapup-metrics.jsonl;
|
|
238
|
+
mkdir -p .spur/run .spur/memory &&
|
|
239
|
+
STATUS_FILE=".spur/run/$__runId-wrapup-metrics.status" &&
|
|
240
|
+
TASKS_FILE=".spur/run/$__runId-wrapup-tasks.json" &&
|
|
241
|
+
METRICS_RC=0 &&
|
|
242
|
+
for wbs in $(jq -r '.[]' "$TASKS_FILE" 2>/dev/null); do
|
|
243
|
+
task_json=$($spurBin task show "$wbs" --json 2>/dev/null || true);
|
|
244
|
+
if [ -n "$task_json" ]; then
|
|
245
|
+
feat=$(printf '%s' "$task_json" | jq -r '.frontmatter.feature_id // .feature_id // empty' 2>/dev/null || true);
|
|
246
|
+
stat=$(printf '%s' "$task_json" | jq -r '.frontmatter.status // .status // "unknown"' 2>/dev/null || true);
|
|
247
|
+
verdict="UNKNOWN";
|
|
248
|
+
if [ -f ".spur/run/$wbs-verdict.json" ]; then
|
|
249
|
+
v=$(jq -r '.verdict // "UNKNOWN"' ".spur/run/$wbs-verdict.json" 2>/dev/null || true);
|
|
250
|
+
if [ -n "$v" ]; then verdict="$v"; fi;
|
|
148
251
|
fi;
|
|
252
|
+
ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ");
|
|
253
|
+
printf '{"wbs":"%s","feature_id":"%s","status":"%s","verdict":"%s","timestamp":"%s"}\n' "$wbs" "$feat" "$stat" "$verdict" "$ts" >> .spur/memory/wrapup-metrics.jsonl;
|
|
254
|
+
else
|
|
255
|
+
echo "metrics-record: task $wbs did not resolve — recording FAIL instead of silently omitting its metrics row" >&2;
|
|
256
|
+
METRICS_RC=1;
|
|
149
257
|
fi;
|
|
150
|
-
done
|
|
258
|
+
done;
|
|
259
|
+
if [ "$METRICS_RC" -eq 0 ]; then printf 'PASS\n' > "$STATUS_FILE"; else printf 'FAIL\n' > "$STATUS_FILE"; fi;
|
|
151
260
|
exit 0
|
|
152
261
|
|
|
153
262
|
- id: feature-transition
|
|
154
263
|
description: >
|
|
155
264
|
If vars.feature is set, sync feature status via bounded feature-sync-bounded
|
|
156
|
-
(0411). Soft shell —
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
265
|
+
(0411). Soft shell — the run is never aborted after learnings/metrics
|
|
266
|
+
landed; instead (0770) a failed required sync or a failed feature gate
|
|
267
|
+
records FAIL in the run-scoped sync status and routes to `failed`, and a
|
|
268
|
+
successful sync that applied no transition is reported as an explicit
|
|
269
|
+
no-change. Empty vars.feature is NOT a blocked sync but a mis-invocation
|
|
270
|
+
(the engine only enters this state when vars.feature is set) and fails
|
|
271
|
+
loud (dogfood 2026-08-15, feature I3: a silent exit 0 made the wrap look
|
|
272
|
+
complete with no transition).
|
|
160
273
|
onEnter:
|
|
161
|
-
# Genuinely soft: the terminating `exit 0` must be reached on BOTH
|
|
162
|
-
#
|
|
163
|
-
#
|
|
164
|
-
#
|
|
165
|
-
#
|
|
166
|
-
#
|
|
274
|
+
# Genuinely soft: the terminating `exit 0` must be reached on BOTH
|
|
275
|
+
# branches, so
|
|
276
|
+
# the sync is chained with `;` — with `&&` a non-zero sync skipped `exit
|
|
277
|
+
# 0` and
|
|
278
|
+
# aborted the run at this state, discarding the learnings/metrics that
|
|
279
|
+
# already
|
|
280
|
+
# landed and never reaching branch-cleanup/done (the exact failure the
|
|
281
|
+
# state
|
|
282
|
+
# description promises against). `feature-sync-bounded.ts` is a
|
|
283
|
+
# Spur-monorepo
|
|
284
|
+
# path (`spur init` does not scaffold `plugins/sp/`), so seeded projects
|
|
285
|
+
# fall
|
|
167
286
|
# back to the plain `spur feature sync` verb.
|
|
168
|
-
# R1 (0625): a feature transition that changed state may have
|
|
169
|
-
#
|
|
170
|
-
#
|
|
171
|
-
# may follow a partial multi-hop
|
|
172
|
-
# by design: the operator
|
|
287
|
+
# R1 (0625): a feature transition that changed state may have left
|
|
288
|
+
# cross-task fallout the per-task fast gates never see. Run the
|
|
289
|
+
# affected-feature gate (featureGateCmd) after an
|
|
290
|
+
# applied sync OR a non-zero exit that may follow a partial multi-hop
|
|
291
|
+
# transition, then report PASS/FAIL. Soft by design: the operator
|
|
292
|
+
# decides, and wrap-up never hard-fails here.
|
|
173
293
|
# NOTE: do not add
|
|
174
294
|
# `#` comments INSIDE the folded shell string — YAML `>-` folds them onto
|
|
175
295
|
# the following statement line and comments it out.
|
|
@@ -189,24 +309,41 @@ states:
|
|
|
189
309
|
fi);
|
|
190
310
|
SYNC_RC=$?;
|
|
191
311
|
printf '%s\n' "$SYNC_OUTPUT";
|
|
192
|
-
|
|
312
|
+
if printf '%s' "$SYNC_OUTPUT" | jq -e 'has("applied")' > /dev/null 2>&1; then
|
|
313
|
+
APPLIED=$(printf '%s' "$SYNC_OUTPUT" | jq -r '.applied // false' 2>/dev/null || echo false);
|
|
314
|
+
else
|
|
315
|
+
APPLIED="invalid";
|
|
316
|
+
fi;
|
|
317
|
+
GATE="skipped";
|
|
193
318
|
if [ "$APPLIED" = "true" ] || [ "$SYNC_RC" -ne 0 ]; then
|
|
194
|
-
echo "feature-transition: sync applied or failed after a possible partial transition for $feature — running
|
|
319
|
+
echo "feature-transition: sync applied or failed after a possible partial transition for $feature — running feature gate: $featureGateCmd";
|
|
195
320
|
if sh -c "$featureGateCmd"; then
|
|
196
|
-
|
|
321
|
+
GATE="PASS";
|
|
322
|
+
echo "feature-transition: feature gate PASS for feature $feature";
|
|
197
323
|
else
|
|
198
|
-
|
|
324
|
+
GATE="FAIL";
|
|
325
|
+
echo "feature-transition: feature gate FAIL for feature $feature — inspect findings before reporting the transition complete" >&2;
|
|
199
326
|
fi;
|
|
200
327
|
else
|
|
201
|
-
echo "feature-transition: sync did not apply a transition (rc=$SYNC_RC, applied=$APPLIED) —
|
|
328
|
+
echo "feature-transition: sync did not apply a transition (rc=$SYNC_RC, applied=$APPLIED) — feature gate skipped (explicit no-change)";
|
|
329
|
+
fi;
|
|
330
|
+
SYNC_STATUS="PASS";
|
|
331
|
+
if [ "$SYNC_RC" -ne 0 ] || [ "$GATE" = "FAIL" ] || [ "$APPLIED" = "invalid" ]; then SYNC_STATUS="FAIL"; fi;
|
|
332
|
+
if [ "$SYNC_STATUS" = "PASS" ]; then
|
|
333
|
+
printf 'PASS\n' > ".spur/run/$__runId-wrapup-sync.status";
|
|
334
|
+
else
|
|
335
|
+
printf 'FAIL\n' > ".spur/run/$__runId-wrapup-sync.status";
|
|
202
336
|
fi;
|
|
203
337
|
exit 0
|
|
204
338
|
|
|
205
339
|
- id: branch-cleanup
|
|
206
340
|
description: >
|
|
207
|
-
Irreversible HITL gate. If vars.merge=true,
|
|
208
|
-
|
|
209
|
-
|
|
341
|
+
Irreversible-operation HITL gate, consent-only (0770). If vars.merge=true,
|
|
342
|
+
branch cleanup (merge or delete) is a candidate — but this gate records
|
|
343
|
+
the operator decision and performs NO git operation itself; an operator
|
|
344
|
+
or a separate approved surface executes any actual merge/delete. This
|
|
345
|
+
gate ALWAYS pauses — even under --auto — because branch operations are
|
|
346
|
+
irreversible (Auto-Decision Principle #6).
|
|
210
347
|
pause: true
|
|
211
348
|
onEnter:
|
|
212
349
|
- kind: hitl.confirm
|
|
@@ -215,13 +352,15 @@ states:
|
|
|
215
352
|
|
|
216
353
|
- id: done
|
|
217
354
|
description: >
|
|
218
|
-
Terminal — wrap-up complete.
|
|
219
|
-
metrics recorded, feature advanced
|
|
355
|
+
Terminal — wrap-up complete. Summary: tasks wrapped, learnings captured,
|
|
356
|
+
metrics recorded, feature advanced or explicitly no-change (if applicable),
|
|
357
|
+
branch-cleanup decision recorded (consent-only; no git operation performed).
|
|
220
358
|
onEnter:
|
|
221
359
|
- kind: note
|
|
222
360
|
options:
|
|
223
|
-
message: "Wrap-up pipeline complete for tasks: ${vars.tasks}. Learnings at .spur/memory/learnings.md, metrics at .spur/memory/wrapup-metrics.jsonl."
|
|
224
|
-
# Checkpoint write: record session state for resume (Phase 4, task 0171
|
|
361
|
+
message: "Wrap-up pipeline complete for tasks: ${vars.tasks}. Learnings at .spur/memory/learnings.md, metrics at .spur/memory/wrapup-metrics.jsonl. Branch cleanup: consent recorded only (merge=${vars.merge}); no git operation performed by wrap-up."
|
|
362
|
+
# Checkpoint write: record session state for resume (Phase 4, task 0171
|
|
363
|
+
# R3)
|
|
225
364
|
- kind: shell
|
|
226
365
|
options:
|
|
227
366
|
command: 'mkdir -p .spur/memory/sessions && echo "checkpoint: wrapup-pipeline done tasks=$tasks ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)" > .spur/memory/sessions/wrapup-checkpoint.md'
|
|
@@ -229,6 +368,13 @@ states:
|
|
|
229
368
|
- id: skipped
|
|
230
369
|
description: Terminal — wrap-up skipped (empty task list or operator abort).
|
|
231
370
|
|
|
371
|
+
- id: failed
|
|
372
|
+
description: >
|
|
373
|
+
Terminal — wrap-up failed (0770): invalid wrap input, an unresolved or
|
|
374
|
+
non-completed task, a failed metrics capture, a failed required sync, or
|
|
375
|
+
a failed feature gate. Already-written artifacts (learnings, metrics
|
|
376
|
+
rows, route reason) are preserved; nothing is rolled back.
|
|
377
|
+
|
|
232
378
|
transitions:
|
|
233
379
|
# ── start -> task-resolve ──
|
|
234
380
|
- from: start
|
|
@@ -237,18 +383,17 @@ transitions:
|
|
|
237
383
|
guard:
|
|
238
384
|
kind: always
|
|
239
385
|
|
|
240
|
-
# ── task-resolve:
|
|
241
|
-
# `|| echo 0` is load-bearing: a malformed vars.tasks (or a box without `jq`) makes
|
|
242
|
-
# `jq length` emit nothing, and bare `test "" -gt 0` exits 2 — so BOTH edges failed
|
|
243
|
-
# and the run died with an opaque `no-passing-transition` instead of skipping. The
|
|
244
|
-
# trailing `always` edge is the belt-and-braces defense for any other guard fault.
|
|
386
|
+
# ── task-resolve: validation fail + proportional route table (0758 R1-R4, 0770) ──
|
|
245
387
|
- from: task-resolve
|
|
246
|
-
to:
|
|
247
|
-
description:
|
|
388
|
+
to: failed
|
|
389
|
+
description: >
|
|
390
|
+
Validation FAIL (0770): malformed wrap input, a non-string/empty entry, an
|
|
391
|
+
unresolved or non-completed task, or an empty __runId. Declared before the
|
|
392
|
+
route edges so invalid input can never be absorbed by the empty-list skip.
|
|
248
393
|
guard:
|
|
249
394
|
kind: shell
|
|
250
395
|
options:
|
|
251
|
-
command: 'test "$(
|
|
396
|
+
command: 'test "$(cat .spur/run/$__runId-wrapup-resolve.status 2>/dev/null)" = FAIL'
|
|
252
397
|
- from: task-resolve
|
|
253
398
|
to: skipped
|
|
254
399
|
description: Task list is empty — skip wrap-up.
|
|
@@ -257,8 +402,24 @@ transitions:
|
|
|
257
402
|
options:
|
|
258
403
|
command: 'test "$(echo "$tasks" | jq length 2>/dev/null || echo 0)" -eq 0'
|
|
259
404
|
- from: task-resolve
|
|
260
|
-
to:
|
|
261
|
-
description:
|
|
405
|
+
to: metrics-record
|
|
406
|
+
description: Proportional fast path — complete and consistent evidence bypasses doc-sync.
|
|
407
|
+
guard:
|
|
408
|
+
kind: shell
|
|
409
|
+
options:
|
|
410
|
+
command: 'test "$(echo "$tasks" | jq length 2>/dev/null || echo 0)" -gt 0 && test "$mode" = fast'
|
|
411
|
+
- from: task-resolve
|
|
412
|
+
to: doc-sync
|
|
413
|
+
description: Proportional safety path — missing/unknown/conflicting evidence routes to full doc-sync.
|
|
414
|
+
guard:
|
|
415
|
+
kind: shell
|
|
416
|
+
options:
|
|
417
|
+
command: 'test "$(echo "$tasks" | jq length 2>/dev/null || echo 0)" -gt 0 && test "$mode" != fast'
|
|
418
|
+
- from: task-resolve
|
|
419
|
+
to: failed
|
|
420
|
+
description: >
|
|
421
|
+
Defense (0770) — no PASS/FAIL resolve status (shell crashed or never ran):
|
|
422
|
+
route to `failed` rather than claiming a skip.
|
|
262
423
|
guard:
|
|
263
424
|
kind: always
|
|
264
425
|
|
|
@@ -269,49 +430,71 @@ transitions:
|
|
|
269
430
|
guard:
|
|
270
431
|
kind: always
|
|
271
432
|
|
|
272
|
-
# ── metrics-record: conditional routing
|
|
273
|
-
# Declaration order matters:
|
|
274
|
-
#
|
|
433
|
+
# ── metrics-record: fail edge, then conditional routing (feature → merge → done) ──
|
|
434
|
+
# Declaration order matters: the FAIL edge is tried first (0770), then
|
|
435
|
+
# feature-transition (if feature is set), then branch-cleanup (if merge=true
|
|
436
|
+
# but no feature), then done (neither). Routing edges additionally require
|
|
437
|
+
# the metrics status PASS so a missing status can never claim success.
|
|
438
|
+
- from: metrics-record
|
|
439
|
+
to: failed
|
|
440
|
+
description: >
|
|
441
|
+
Metrics capture failed for at least one task — a missing metrics row is
|
|
442
|
+
recorded as failure, never silently omitted as success (0770).
|
|
443
|
+
guard:
|
|
444
|
+
kind: shell
|
|
445
|
+
options:
|
|
446
|
+
command: 'test "$(cat .spur/run/$__runId-wrapup-metrics.status 2>/dev/null)" = FAIL'
|
|
275
447
|
- from: metrics-record
|
|
276
448
|
to: feature-transition
|
|
277
|
-
description:
|
|
449
|
+
description: Metrics PASS and feature id is set — advance feature through legal lifecycle edges.
|
|
278
450
|
guard:
|
|
279
451
|
kind: shell
|
|
280
452
|
options:
|
|
281
|
-
command: 'test -n "$feature"'
|
|
453
|
+
command: 'test "$(cat .spur/run/$__runId-wrapup-metrics.status 2>/dev/null)" = PASS && test -n "$feature"'
|
|
282
454
|
- from: metrics-record
|
|
283
455
|
to: branch-cleanup
|
|
284
|
-
description:
|
|
456
|
+
description: Metrics PASS, no feature id, but merge is requested — go to branch cleanup.
|
|
285
457
|
guard:
|
|
286
458
|
kind: shell
|
|
287
459
|
options:
|
|
288
|
-
command: 'test -z "$feature" && test "$merge" = true'
|
|
460
|
+
command: 'test "$(cat .spur/run/$__runId-wrapup-metrics.status 2>/dev/null)" = PASS && test -z "$feature" && test "$merge" = true'
|
|
289
461
|
- from: metrics-record
|
|
290
462
|
to: done
|
|
291
|
-
description:
|
|
463
|
+
description: Metrics PASS, no feature id and no merge — wrap-up is done.
|
|
292
464
|
guard:
|
|
293
465
|
kind: shell
|
|
294
466
|
options:
|
|
295
|
-
command: 'test -z "$feature" && test "$merge" != true'
|
|
467
|
+
command: 'test "$(cat .spur/run/$__runId-wrapup-metrics.status 2>/dev/null)" = PASS && test -z "$feature" && test "$merge" != true'
|
|
296
468
|
|
|
297
|
-
# ── feature-transition:
|
|
469
|
+
# ── feature-transition: fail edge, then branch-cleanup (if merge) or done ──
|
|
470
|
+
- from: feature-transition
|
|
471
|
+
to: failed
|
|
472
|
+
description: >
|
|
473
|
+
Required synchronization failed (0770): non-zero sync exit, an invalid
|
|
474
|
+
sync result, or a failed feature gate. The failed status carries the
|
|
475
|
+
actual reason; already-written learnings/metrics stay on disk.
|
|
476
|
+
guard:
|
|
477
|
+
kind: shell
|
|
478
|
+
options:
|
|
479
|
+
command: 'test "$(cat .spur/run/$__runId-wrapup-sync.status 2>/dev/null)" = FAIL'
|
|
298
480
|
- from: feature-transition
|
|
299
481
|
to: branch-cleanup
|
|
300
|
-
description:
|
|
482
|
+
description: Sync PASS — merge is requested.
|
|
301
483
|
guard:
|
|
302
484
|
kind: shell
|
|
303
485
|
options:
|
|
304
|
-
command: 'test "$merge" = true'
|
|
486
|
+
command: 'test "$(cat .spur/run/$__runId-wrapup-sync.status 2>/dev/null)" = PASS && test "$merge" = true'
|
|
305
487
|
- from: feature-transition
|
|
306
488
|
to: done
|
|
307
|
-
description:
|
|
489
|
+
description: Sync PASS — no merge requested.
|
|
308
490
|
guard:
|
|
309
491
|
kind: shell
|
|
310
492
|
options:
|
|
311
|
-
command: 'test "$merge" != true'
|
|
493
|
+
command: 'test "$(cat .spur/run/$__runId-wrapup-sync.status 2>/dev/null)" = PASS && test "$merge" != true'
|
|
312
494
|
|
|
313
495
|
# ── branch-cleanup: HITL exhaustive routing (yes / no / cancel → done) ──
|
|
314
|
-
# No irreversible git op is wired yet; confirmation is recorded, then wrap
|
|
496
|
+
# No irreversible git op is wired yet; confirmation is recorded, then wrap
|
|
497
|
+
# completes.
|
|
315
498
|
- from: branch-cleanup
|
|
316
499
|
to: done
|
|
317
500
|
description: Branch cleanup HITL answered — wrap-up complete.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/spur",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.74",
|
|
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.56",
|
|
57
|
+
"@gobing-ai/ts-ai-runner": "^0.4.56",
|
|
58
|
+
"@gobing-ai/ts-dual-workflow-engine": "^0.4.56",
|
|
59
|
+
"@gobing-ai/ts-infra": "^0.4.56",
|
|
60
|
+
"@gobing-ai/ts-llm-jsonl-importer": "^0.4.56",
|
|
61
|
+
"@gobing-ai/ts-rule-engine": "^0.4.56",
|
|
62
|
+
"@gobing-ai/ts-runtime": "^0.4.56",
|
|
63
|
+
"@gobing-ai/ts-utils": "^0.4.56",
|
|
64
64
|
"@types/bun": "1.3.14",
|
|
65
65
|
"@types/figlet": "^1.7.0",
|
|
66
66
|
"@types/node-notifier": "8.0.5",
|
package/plugins/sp/plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.74",
|
|
4
4
|
"description": "Spur — a local-first harness engineering toolkit that wraps mainstream coding agents with constraint checking, workflow orchestration, and history analytics.",
|
|
5
5
|
"extensions": {
|
|
6
6
|
"pi": ["./hooks/pi/guard-extension.ts"]
|
|
@@ -194,15 +194,13 @@ async function getSpurHistoryHealth(date, dbPath = ".spur/spur.db") {
|
|
|
194
194
|
const cleanTool = lp.toolName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
195
195
|
const key = `repetition:${cleanTool}:${lp.argsDigest.slice(0, 16)}`;
|
|
196
196
|
const title = `Break execution loop in ${lp.toolName} (${lp.repeats} repeats)`;
|
|
197
|
-
const
|
|
198
|
-
const command = `spur task create --section "Fix ${cleanTool} repetition loop" --body "${body}"`;
|
|
197
|
+
const command = `spur task create "<title>" --feature <id> && spur task update <wbs> --section Plan --from-file <path>`;
|
|
199
198
|
remediationProposals.push({ key, title, command });
|
|
200
199
|
}
|
|
201
200
|
if (toolCalls > 0 && errorRatePct > 10) {
|
|
202
201
|
const key = "reliability:tooling:high-error-rate";
|
|
203
202
|
const title = `Investigate high tool error rate (${errorRatePct.toFixed(1)}%)`;
|
|
204
|
-
const
|
|
205
|
-
const command = `spur task create --section "Investigate tool error rate spikes" --body "${body}"`;
|
|
203
|
+
const command = `spur task create "<title>" --feature <id> && spur task update <wbs> --section Plan --from-file <path>`;
|
|
206
204
|
remediationProposals.push({ key, title, command });
|
|
207
205
|
}
|
|
208
206
|
return {
|