@gobing-ai/spur 0.3.35 → 0.3.37

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.
@@ -15,34 +15,6 @@
15
15
  "reason": "GENUINE PRE-FIX BYPASS. Closed 2026-08-06, well after the rule existed (2026-07-24), via the `--no-lifecycle` hole closed on 2026-08-07. Its Solution needs one file:line citation. The only entry here representing real unpaid debt.",
16
16
  "since": "2026-08-07"
17
17
  },
18
- {
19
- "kind": "feature",
20
- "id": "M",
21
- "code": "L3.ac-bdd-error",
22
- "reason": "CHECKER GAP, not corpus debt. M is a wayfinder MAP; its Acceptance Criteria section carries an explicit prose disclaimer ('This feature is a wayfinder MAP... It has no acceptance criteria — its target is the ## Goal'). The BDD validator requires a `Feature:` declaration and has no notion of the map feature type. Correct fix is to teach `feature check` about wayfinder maps, not to bolt fake Gherkin onto a map.",
23
- "since": "2026-08-07"
24
- },
25
- {
26
- "kind": "feature",
27
- "id": "M",
28
- "code": "L3.ac-bdd-invalid",
29
- "reason": "Downstream of the M ac-bdd-error entry — same missing-Feature-declaration cause, reported twice by the validator.",
30
- "since": "2026-08-07"
31
- },
32
- {
33
- "kind": "feature",
34
- "id": "F82",
35
- "code": "L3.ac-bdd-error",
36
- "reason": "CHECKER GAP, same class as M: wayfinder map with a deliberate no-AC prose disclaimer (plus a noted exception for two implementation-ready child tickets). Not a syntax error to fix.",
37
- "since": "2026-08-07"
38
- },
39
- {
40
- "kind": "feature",
41
- "id": "F82",
42
- "code": "L3.ac-bdd-invalid",
43
- "reason": "Downstream of the F82 ac-bdd-error entry — same cause, reported twice.",
44
- "since": "2026-08-07"
45
- },
46
18
  {
47
19
  "kind": "feature",
48
20
  "id": "F821",
@@ -0,0 +1,56 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key>
6
+ <string>ai.gobing.spur.history.daily</string>
7
+
8
+ <!-- ── Program: run the nightly history pipeline (task 0470+) ──────────── -->
9
+ <!-- Replace SPUR_BIN with the absolute path to your `spur` binary -->
10
+ <!-- (e.g. /opt/homebrew/bin/spur or the bun-linked monorepo CLI). -->
11
+ <!-- Replace PROJECT_DIR with the absolute path to your Spur project root. -->
12
+ <key>ProgramArguments</key>
13
+ <array>
14
+ <string>SPUR_BIN</string>
15
+ <string>history</string>
16
+ <string>daily</string>
17
+ <string>--json</string>
18
+ </array>
19
+
20
+ <!-- Working directory: the Spur project root. The daily command resolves -->
21
+ <!-- .spur/reports, .spur/logs, and the SQLite ledger relative to this. -->
22
+ <key>WorkingDirectory</key>
23
+ <string>PROJECT_DIR</string>
24
+
25
+ <!-- ── Schedule: daily wall-clock trigger (R3) ─────────────────────────── -->
26
+ <!-- 02:00 local time — picks a low-activity hour. Adjust Hour to taste. -->
27
+ <!-- launchd survives reboot and runs missed CalendarInterval jobs at the -->
28
+ <!-- next login, so a laptop closed overnight self-heals. -->
29
+ <key>StartCalendarInterval</key>
30
+ <dict>
31
+ <key>Hour</key>
32
+ <integer>2</integer>
33
+ <key>Minute</key>
34
+ <integer>0</integer>
35
+ </dict>
36
+
37
+ <!-- ── Log capture (R4): failures before Spur's own logging initializes ── -->
38
+ <!-- are still written here. launchd owns these file handles from the first -->
39
+ <!-- byte — there is no window where output is lost. -->
40
+ <key>StandardOutPath</key>
41
+ <string>PROJECT_DIR/.spur/logs/history-daily.out</string>
42
+ <key>StandardErrorPath</key>
43
+ <string>PROJECT_DIR/.spur/logs/history-daily.err</string>
44
+
45
+ <!-- RunAtLoad=false: do NOT fire every time the agent loads (boot/login). -->
46
+ <!-- StartCalendarInterval already re-fires a missed slot when the machine -->
47
+ <!-- wakes, so RunAtLoad would only cause duplicate runs. -->
48
+
49
+ <!-- If the process exits non-zero, do NOT auto-restart — the daily command -->
50
+ <!-- has its own retry semantics (checkpoint resume), and a tight retry loop -->
51
+ <!-- would mask a configuration error. The operator checks the four detection -->
52
+ <!-- layers (R5) instead. -->
53
+ <key>KeepAlive</key>
54
+ <false/>
55
+ </dict>
56
+ </plist>
@@ -60,6 +60,7 @@ rules:
60
60
  - "packages/app/src/services/token-ledger-service.ts" # FD byte-window log tailing
61
61
  - "packages/app/src/services/token-ledger-watcher.ts" # node:fs watch() live watcher
62
62
  - "packages/app/src/services/project-registry.ts" # atomic projects.json persistence
63
+ - "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)
63
64
  - "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
64
65
  - "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
65
66
  - "apps/web/src/modules/discover.ts" # Vite/Astro module scanner fallback under bun test
@@ -140,7 +140,7 @@ spur <noun> <verb> … --json
140
140
  spur <noun> --help
141
141
  ```
142
142
 
143
- **Long-tail:** Additional `/sp:dev-*` commands (handover, gitmsg, fixall, dogfood, reverse, arch,
143
+ **Long-tail:** Additional `/sp:dev-*` commands (handover, gitmsg, fixall, findconflict, dogfood, reverse, arch,
144
144
  …) are indexed in the project plugin README (`plugins/sp/README.md` when present).
145
145
 
146
146
  **Outside spur-cli:** Nouns not fully documented in `sp:spur-cli` (`agent`, `history`, `message`,
@@ -57,9 +57,11 @@ vars:
57
57
  # `--vars '{"agent":"claude"}'`. Pinned (not left to the AiRunner's <default>
58
58
  # selection) so a broken/misconfigured agent on the box can't silently capture the run.
59
59
  agent: "omp"
60
- # Implement-only executor override (R1, task 0454). Defaults to same as agent.
61
- # Override with `--vars '{"implementAgent":"omp-zai"}'` to pin implement to a
62
- # specific executor while other hops keep the default agent.
60
+ # Implement-only executor override (R1, task 0454). Resolves to this YAML literal
61
+ # unless overridden. The `--agent` flag (execution-batch.md §3.2) forwards into BOTH
62
+ # `agent` and `implementAgent`, so a pinned `--agent X` reaches implement too. To
63
+ # pin ONLY implement while other hops keep the default, pass
64
+ # `--vars '{"implementAgent":"omp-zai"}'`.
63
65
  implementAgent: "omp"
64
66
  # Step-level timeout for agentic hops (review / verify / test-fix) in ms.
65
67
  # Soft quality-gate shells are unbounded by this var (host shell only).
@@ -82,7 +84,7 @@ vars:
82
84
  # and the fixall slash input all use this same var so the command stays single-sourced.
83
85
  # TRUSTED CONFIG ONLY — this string is executed via `sh -c` (see test/test-recheck). Never
84
86
  # interpolate untrusted operator/LLM input into qualityGateCmd (task 0436 SECUA residual).
85
- qualityGateCmd: "bun run autofix && bun run spur-check"
87
+ qualityGateCmd: "bun run format && bun run spur-check"
86
88
  # Max /sp:dev-fixall attempts after a red quality-gate probe/recheck (bounded; no thrash).
87
89
  # Attempt counter: .spur/run/<wbs>-test-fix-attempt. Default 2 = two fixall hops before failed.
88
90
  qualityGateMaxFixAttempts: "2"
@@ -90,6 +92,12 @@ vars:
90
92
  # project can point it at its own formatter; invoked best-effort (a missing or
91
93
  # failing formatter must never abort a run — the quality gate is the real gate).
92
94
  formatCmd: "bun run format"
95
+ # Bounded `file:line` anchors extracted from the failing quality-gate log by the
96
+ # `test` / `test-recheck` hops (deduped, max 20, space-separated on one line).
97
+ # `test-fix` reads `.spur/run/<wbs>-test-gate.findings` into this var and names the
98
+ # anchors directly in the /sp:dev-fixall input, so the fix agent starts AT the failing
99
+ # file:line instead of re-deriving it from a fresh gate run (0482 R3).
100
+ gateFindings: ""
93
101
  # Max R-items in ## Requirements before size precheck fails (R2, task 0454).
94
102
  # Override with `--vars '{"maxImplementReqs":"12"}'`.
95
103
  maxImplementReqs: "5"
@@ -147,7 +155,7 @@ states:
147
155
  SIZE_FILE=".spur/run/$wbs-precheck-size.status" &&
148
156
  mkdir -p .spur/run &&
149
157
  bun plugins/sp/scripts/task-size-precheck.ts "$wbs"
150
- --max-reqs "$maxImplementReqs" --max-plan-items "$maxImplementPlanItems" &&
158
+ --spur-bin "$spurBin" --max-reqs "$maxImplementReqs" --max-plan-items "$maxImplementPlanItems" &&
151
159
  exit 0
152
160
 
153
161
  - id: implement
@@ -214,10 +222,14 @@ states:
214
222
  command: >-
215
223
  mkdir -p .spur/run &&
216
224
  STATUS_FILE=".spur/run/$wbs-test-gate.status" &&
225
+ LOG_FILE=".spur/run/$wbs-test-gate.log" &&
217
226
  ATTEMPT_FILE=".spur/run/$wbs-test-fix-attempt" &&
218
227
  echo 0 > "$ATTEMPT_FILE" &&
219
228
  set +e &&
220
- ( sh -c "$qualityGateCmd" ); gate_rc=$?; set -e &&
229
+ ( sh -c "$qualityGateCmd" ) > "$LOG_FILE" 2>&1; gate_rc=$?; set -e &&
230
+ cat "$LOG_FILE" &&
231
+ FINDINGS_FILE=".spur/run/$wbs-test-gate.findings" &&
232
+ set +e; grep -oE '[A-Za-z0-9_./-]+\.[A-Za-z]+:[0-9]+' "$LOG_FILE" | sort -u | head -20 | tr '\n' ' ' > "$FINDINGS_FILE"; set -e &&
221
233
  if [ "$gate_rc" -eq 0 ]; then
222
234
  printf 'PASS\n' > "$STATUS_FILE";
223
235
  else
@@ -238,10 +250,19 @@ states:
238
250
  ATTEMPT_FILE=".spur/run/$wbs-test-fix-attempt" &&
239
251
  n=$(cat "$ATTEMPT_FILE" 2>/dev/null || echo 0) &&
240
252
  printf '%s\n' "$((n + 1))" > "$ATTEMPT_FILE"
253
+ # R3 (0482): project the extracted gate anchors into a var so the dispatch input
254
+ # can NAME the failing file:line, not merely point at a log. A vars template cannot
255
+ # run a shell, so the read is an action, not an inline `$(cat ...)` substitution.
256
+ - kind: file.read.into-var
257
+ options:
258
+ path: .spur/run/${vars.wbs}-test-gate.findings
259
+ var: gateFindings
241
260
  - kind: agent.run
242
261
  options:
243
262
  agent: ${vars.agent}
244
- input: /sp:dev-fixall "${vars.qualityGateCmd}"
263
+ # R3 (0482): `--findings` names the failing anchors inline; `--gate-log` remains
264
+ # the full-context escape hatch when the digest is not enough.
265
+ input: /sp:dev-fixall "${vars.qualityGateCmd}" --gate-log .spur/run/${vars.wbs}-test-gate.log --findings "${vars.gateFindings}"
245
266
  timeoutMs: ${vars.stepTimeoutMs}
246
267
 
247
268
  - id: test-recheck
@@ -256,8 +277,12 @@ states:
256
277
  command: >-
257
278
  mkdir -p .spur/run &&
258
279
  STATUS_FILE=".spur/run/$wbs-test-gate.status" &&
280
+ LOG_FILE=".spur/run/$wbs-test-gate.log" &&
259
281
  set +e &&
260
- ( sh -c "$qualityGateCmd" ); gate_rc=$?; set -e &&
282
+ ( sh -c "$qualityGateCmd" ) > "$LOG_FILE" 2>&1; gate_rc=$?; set -e &&
283
+ cat "$LOG_FILE" &&
284
+ FINDINGS_FILE=".spur/run/$wbs-test-gate.findings" &&
285
+ set +e; grep -oE '[A-Za-z0-9_./-]+\.[A-Za-z]+:[0-9]+' "$LOG_FILE" | sort -u | head -20 | tr '\n' ' ' > "$FINDINGS_FILE"; set -e &&
261
286
  if [ "$gate_rc" -eq 0 ]; then
262
287
  printf 'PASS\n' > "$STATUS_FILE";
263
288
  else
@@ -290,8 +315,10 @@ states:
290
315
  - id: verify
291
316
  description: >
292
317
  Functional verification (BDD + traceability) via /sp:dev-verify. The agent's
293
- captured answer is written to `.spur/run/<wbs>-verify-answer.txt`; a deterministic
294
- shell step then derives the verdict and writes the gate artifact
318
+ captured answer is written to `.spur/run/<wbs>-verify-answer.txt` and MUST follow
319
+ the answer-file schema contract (explicit `Verdict: PASS|PARTIAL|FAIL` line plus
320
+ `| Req | Status | Evidence |` and `| AC | Status | Evidence Type | Evidence |` tables);
321
+ a deterministic shell step then derives the verdict and writes the gate artifact
295
322
  `.spur/run/<wbs>-verdict.json` (so the completion gate reads a real verdict, not
296
323
  agent discretion — R9). The verdict is PASS only if the agent both reported PASS
297
324
  AND `spur task check` passes; otherwise FAIL.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/spur",
3
- "version": "0.3.35",
3
+ "version": "0.3.37",
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",
@@ -50,14 +50,14 @@
50
50
  },
51
51
  "devDependencies": {
52
52
  "@commander-js/extra-typings": "^14.0.0",
53
- "@gobing-ai/ts-db": "^0.4.22",
54
- "@gobing-ai/ts-ai-runner": "^0.4.22",
55
- "@gobing-ai/ts-dual-workflow-engine": "^0.4.22",
56
- "@gobing-ai/ts-infra": "^0.4.22",
57
- "@gobing-ai/ts-llm-jsonl-importer": "^0.4.22",
58
- "@gobing-ai/ts-rule-engine": "^0.4.22",
59
- "@gobing-ai/ts-runtime": "^0.4.22",
60
- "@gobing-ai/ts-utils": "^0.4.22",
53
+ "@gobing-ai/ts-db": "^0.4.23",
54
+ "@gobing-ai/ts-ai-runner": "^0.4.23",
55
+ "@gobing-ai/ts-dual-workflow-engine": "^0.4.23",
56
+ "@gobing-ai/ts-infra": "^0.4.23",
57
+ "@gobing-ai/ts-llm-jsonl-importer": "^0.4.23",
58
+ "@gobing-ai/ts-rule-engine": "^0.4.23",
59
+ "@gobing-ai/ts-runtime": "^0.4.23",
60
+ "@gobing-ai/ts-utils": "^0.4.23",
61
61
  "@types/bun": "1.3.14",
62
62
  "@types/figlet": "^1.7.0",
63
63
  "@types/node-notifier": "8.0.5",