@gobing-ai/spur 0.3.28 → 0.3.29
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/config/rules/strict/runtime-boundaries.yaml +1 -0
- package/config/rules/typescript/no-syscall-emulation-in-boundary-mock.yaml +57 -0
- package/config/templates/AGENTS.md +3 -0
- package/config/workflows/feature-lifecycle.yaml +9 -7
- package/config/workflows/task-pipeline.yaml +3 -2
- package/config/workflows/wrapup-pipeline.yaml +5 -4
- package/package.json +1 -1
- package/schemas/spur-config.schema.json +1 -1
- package/spur.js +916 -312
|
@@ -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/observability/run-output-sink.ts" # sync FD append for mid-run tail-able artifact (task 0414 R2); observe() is sync from the lifecycle relay
|
|
63
64
|
- "apps/web/src/modules/discover.ts" # Vite/Astro module scanner fallback under bun test
|
|
64
65
|
|
|
65
66
|
# Synchronous bootstrap & path resolution:
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
$schema: "@gobing-ai/spur/schemas/rule-file.schema.json"
|
|
2
|
+
# Forbid mocking the syscall under test where the mock emulates it (task 0415 R4).
|
|
3
|
+
#
|
|
4
|
+
# WHY: `sp:test-driven-development` SKILL.md:164 already states the rule — "Mock
|
|
5
|
+
# what crosses a process/IO boundary; never mock the code under test." This rule
|
|
6
|
+
# enforces the narrow, mechanically detectable case: a test that mocks a
|
|
7
|
+
# subprocess/syscall boundary (Bun.spawnSync / node:child_process) AND whose mock
|
|
8
|
+
# handler emulates the syscall output (stat / mtime / %m) that the code under test
|
|
9
|
+
# consumes. The 0411 defect is the canonical fixture: the verdict-mtime tests
|
|
10
|
+
# mocked `Bun.spawnSync` INCLUDING the `stat -f %m` call, so the BSD-only stat
|
|
11
|
+
# syntax silently returned nothing on Linux while 57 tests stayed green — the mock
|
|
12
|
+
# replaced the very portability the tests were supposed to verify.
|
|
13
|
+
#
|
|
14
|
+
# SCOPE / discriminator: mocking the subprocess boundary to control *subprocess
|
|
15
|
+
# responses* (e.g. feature-sync-bounded.test.ts intercepting `show`/`list`/`sync`
|
|
16
|
+
# commands) is legitimate and NOT flagged — the boundary mock returns canned
|
|
17
|
+
# responses, it does not re-implement the syscall the code under test calls. The
|
|
18
|
+
# rule fires only when the mock handler itself references `stat`/`%m`/`birthtime`
|
|
19
|
+
# (emulating the syscall) within a bounded window after the mock installation.
|
|
20
|
+
#
|
|
21
|
+
# FIX: use real files + real statSync/utimesSync (the 0411 remediation), or mock a
|
|
22
|
+
# higher-level seam the code under test does NOT depend on for the behavior under
|
|
23
|
+
# test. If a legit syscall-emulating mock is unavoidable, waive with a stated
|
|
24
|
+
# reason in the test.
|
|
25
|
+
include:
|
|
26
|
+
- "apps/**/tests/**/*.test.ts"
|
|
27
|
+
- "apps/**/tests/**/*.test.tsx"
|
|
28
|
+
- "packages/**/tests/**/*.test.ts"
|
|
29
|
+
- "packages/**/tests/**/*.test.tsx"
|
|
30
|
+
- "plugins/**/tests/**/*.test.ts"
|
|
31
|
+
- "plugins/**/tests/**/*.test.tsx"
|
|
32
|
+
exclude:
|
|
33
|
+
- "**/node_modules/**"
|
|
34
|
+
- "**/dist/**"
|
|
35
|
+
|
|
36
|
+
rules:
|
|
37
|
+
- id: no-syscall-emulation-in-boundary-mock
|
|
38
|
+
description: >
|
|
39
|
+
A test that mocks a subprocess/syscall boundary (Bun.spawnSync assignment,
|
|
40
|
+
spyOn(Bun,'spawnSync'), or mock.module('node:child_process')) MUST NOT have
|
|
41
|
+
the mock handler emulate the syscall output the code under test consumes
|
|
42
|
+
(stat / `-f %m` / birthtime / mtime-derived fingerprints). That mocks the
|
|
43
|
+
code under test, not the boundary: platform-specific syscall syntax (e.g.
|
|
44
|
+
BSD-only `stat -f %m`) silently no-ops on Linux while the test stays green —
|
|
45
|
+
the 0411 verdict-mtime defect. Prefer real files + real statSync/utimesSync
|
|
46
|
+
(the 0411 remediation) or mock a higher-level seam. Control of *subprocess
|
|
47
|
+
responses* (show/list/sync commands) is fine — only syscall emulation is
|
|
48
|
+
forbidden. sp:test-driven-development SKILL.md:164.
|
|
49
|
+
severity: warning
|
|
50
|
+
evaluator:
|
|
51
|
+
type: rg
|
|
52
|
+
config:
|
|
53
|
+
# Correlation in one bounded window: a boundary-mock installation followed
|
|
54
|
+
# (within ~700 chars) by stat/mtime syscall emulation. The window keeps
|
|
55
|
+
# legit subprocess-response mocks (which never mention stat/%m) quiet.
|
|
56
|
+
multiline: true
|
|
57
|
+
pattern: "(?:Bun\\.spawnSync\\s*=|spyOn\\(\\s*Bun\\s*,\\s*['\"]spawnSync['\"]\\s*\\)|mock\\.module\\(\\s*['\"]node:child_process['\"]\\s*\\))[\\s\\S]{0,700}?(?:cmd\\.includes\\(\\s*['\"]stat|stat\\s+-[fc]|%m|birthtime)"
|
|
@@ -63,6 +63,9 @@ All product development work goes through the harness by default.
|
|
|
63
63
|
multi-noun corpus → `sp:expert-spur`; review → `sp:super-reviewer`; docs process → `sp:doc-evolve`.
|
|
64
64
|
5. **Keep tool ownership explicit** — project lifecycle/corpus/gates → Spur; plugin installation and
|
|
65
65
|
capability lifecycle → Superskill. Do not hand-maintain per-platform adapters Superskill generates.
|
|
66
|
+
6. **Run dev skills inline by default** — direct model-bearing `/sp:dev-*` commands execute in the
|
|
67
|
+
current coding-agent session. `--subprocess` or a named dispatch-surface trigger uses
|
|
68
|
+
`spur agent run`; direct `spur agent run` and workflow `agent.run` remain subprocess surfaces.
|
|
66
69
|
|
|
67
70
|
**Platform fallback:** Platforms without slash commands and/or subagents still use the harness.
|
|
68
71
|
Install the plugin through Superskill for the target platform, then use skills `sp:spur-dev`,
|
|
@@ -5,11 +5,13 @@ description: >
|
|
|
5
5
|
Feature lifecycle FSM (design §2.3, §5.1, DD-13). States are the canonical
|
|
6
6
|
FeatureStatus vocabulary; transitions encode the §2.3 graph including the
|
|
7
7
|
`verifying` status (DD-13). Guards invoke `${vars.spurBin} feature check`
|
|
8
|
-
at the active→verifying and verifying→done placements
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
at the active→verifying and verifying→done placements, passing the edge's
|
|
9
|
+
target via `--as <status>` (0418) so the one-active-goal rule evaluates the
|
|
10
|
+
post-transition state and never denies the exit it would relieve.
|
|
11
|
+
`cancelled` is terminal. Guard commands reference the check verb (0057) —
|
|
12
|
+
structural validation passes today, behavioral wiring activates as the verb
|
|
13
|
+
ships. Unconditional transitions use the `always` guard (externally-driven
|
|
14
|
+
via `requestTransition`, not auto-advance).
|
|
13
15
|
initialState: backlog
|
|
14
16
|
terminalStates:
|
|
15
17
|
- cancelled
|
|
@@ -47,7 +49,7 @@ transitions:
|
|
|
47
49
|
guard:
|
|
48
50
|
kind: shell
|
|
49
51
|
options:
|
|
50
|
-
command: '${vars.spurBin} feature check ${vars.featureId}'
|
|
52
|
+
command: '${vars.spurBin} feature check ${vars.featureId} --as verifying'
|
|
51
53
|
- from: verifying
|
|
52
54
|
to: done
|
|
53
55
|
description: >
|
|
@@ -55,7 +57,7 @@ transitions:
|
|
|
55
57
|
guard:
|
|
56
58
|
kind: shell
|
|
57
59
|
options:
|
|
58
|
-
command: '${vars.spurBin} feature check ${vars.featureId} --strict'
|
|
60
|
+
command: '${vars.spurBin} feature check ${vars.featureId} --strict --as done'
|
|
59
61
|
|
|
60
62
|
# Rework: verifying → active (mandatory History entry)
|
|
61
63
|
- from: verifying
|
|
@@ -182,7 +182,8 @@ states:
|
|
|
182
182
|
Record pipeline results into the task file via `spur task record` —
|
|
183
183
|
Testing/Review from the verdict, Solution backfilled from git diff as a
|
|
184
184
|
safety net, optional transition to testing. Post-record step conditionally syncs
|
|
185
|
-
feature status
|
|
185
|
+
feature status via the bounded `feature-sync-bounded` wrapper (task 0411
|
|
186
|
+
retry-suppression) if `feature_id` is present, or appends an orphan link proposal
|
|
186
187
|
to the run report if absent (task 0328 / ADR-0322).
|
|
187
188
|
onEnter:
|
|
188
189
|
- kind: shell
|
|
@@ -190,7 +191,7 @@ states:
|
|
|
190
191
|
command: "${vars.spurBin} task record ${vars.wbs} --solution-from-diff --transition testing"
|
|
191
192
|
- kind: shell
|
|
192
193
|
options:
|
|
193
|
-
command: 'FID=$(${vars.spurBin} task show ${vars.wbs} --json 2>/dev/null | jq -r ".feature_id // .frontmatter.feature_id // empty"); if [ -n "$FID" ]; then
|
|
194
|
+
command: 'FID=$(${vars.spurBin} task show ${vars.wbs} --json 2>/dev/null | jq -r ".feature_id // .frontmatter.feature_id // empty"); if [ -n "$FID" ]; then bun plugins/sp/scripts/feature-sync-bounded.ts "$FID" --spur-bin "${vars.spurBin}" --json; else echo "Orphan task ${vars.wbs} — no feature_id linked; proposal: consider linking to a parent feature." >> .spur/run/${vars.wbs}-report.txt; fi'
|
|
194
195
|
|
|
195
196
|
- id: done
|
|
196
197
|
description: >
|
|
@@ -117,13 +117,14 @@ states:
|
|
|
117
117
|
|
|
118
118
|
- id: feature-transition
|
|
119
119
|
description: >
|
|
120
|
-
If vars.feature is set, derive and sync feature status with linked task states via
|
|
121
|
-
`
|
|
122
|
-
Replaces prior unconditional `spur feature advance` with conservative status sync
|
|
120
|
+
If vars.feature is set, derive and sync feature status with linked task states via the
|
|
121
|
+
bounded `feature-sync-bounded` wrapper (task 0328 / ADR-0322, task 0411 retry-suppression).
|
|
122
|
+
Replaces prior unconditional `spur feature advance` with conservative status sync that
|
|
123
|
+
suppresses identical blocked retries until relevant inputs change.
|
|
123
124
|
onEnter:
|
|
124
125
|
- kind: shell
|
|
125
126
|
options:
|
|
126
|
-
command: '${vars.
|
|
127
|
+
command: 'bun plugins/sp/scripts/feature-sync-bounded.ts ${vars.feature} --spur-bin "${vars.spurBin}" --json'
|
|
127
128
|
|
|
128
129
|
- id: branch-cleanup
|
|
129
130
|
description: >
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/spur",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.29",
|
|
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",
|
|
@@ -239,7 +239,7 @@
|
|
|
239
239
|
},
|
|
240
240
|
"tasks": {
|
|
241
241
|
"type": "object",
|
|
242
|
-
"description": "Task-folder registration (design §9). Absorbs the legacy docs/.tasks/config.json folders +
|
|
242
|
+
"description": "Task-folder registration (design §9). Absorbs the legacy docs/.tasks/config.json folders + baseCounter concepts. Mirrors @gobing-ai/spur-config tasksConfigSchema (Zod is SSOT).",
|
|
243
243
|
"properties": {
|
|
244
244
|
"folders": {
|
|
245
245
|
"type": "object",
|