@gobing-ai/spur 0.3.28 → 0.3.30
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 +5 -7
- package/config/templates/docs/04_DESIGN.md +8 -5
- package/config/templates/docs/99_PROJECT_CONSTITUTION.md +4 -2
- 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 +9 -9
- package/schemas/spur-config.schema.json +1 -1
- package/spur.js +1309 -696
|
@@ -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`,
|
|
@@ -101,14 +104,9 @@ feature status → `05`. Working-layer, audit, and satellite rules live in the p
|
|
|
101
104
|
|
|
102
105
|
## Design system
|
|
103
106
|
|
|
104
|
-
**Conditional contract:** If repository-root `DESIGN.md` exists,
|
|
105
|
-
implementing any change to UI, styling, interaction, accessibility, or responsive behavior. Treat
|
|
106
|
-
it as the project source of truth for visual and interaction design — tokens, components, patterns,
|
|
107
|
-
and UX constraints — and keep affected work consistent with it. If it is absent, continue with the
|
|
108
|
-
project's established UI conventions.
|
|
107
|
+
**Conditional contract:** If repository-root `DESIGN.md` exists, leverage it dynamically as the industry-standard SSOT for UI design documentation — visual language, color tokens, typography, component specs, layout, micro-animations, accessibility, and responsive patterns. Read it before planning or implementing any UI changes, and keep affected work consistent with it. If `DESIGN.md` is absent, ignore it and continue with the project's established UI conventions.
|
|
109
108
|
|
|
110
|
-
Root `DESIGN.md` owns UI/UX guidance; `docs/04_DESIGN.md`
|
|
111
|
-
surface shapes under the doc map above.
|
|
109
|
+
**Boundary distinction:** Root `DESIGN.md` owns UI/UX design guidance; `docs/04_DESIGN.md` owns non-UI surface design by default (command signatures, flags, config schemas, DTOs, and system boundaries). When working with design teams, choose `DESIGN.md` for UI/UX visual design and `docs/04_DESIGN.md` for non-UI API/schema surfaces.
|
|
112
110
|
|
|
113
111
|
---
|
|
114
112
|
|
|
@@ -18,12 +18,15 @@ sync: [T3, T9]
|
|
|
18
18
|
> `docs/design/<slug>.md` satellite; this index carries the surface map + pointers.
|
|
19
19
|
> Edit order: satellite first, then index row — same change (T9).
|
|
20
20
|
|
|
21
|
-
## UI/UX boundary
|
|
21
|
+
## UI/UX boundary & DESIGN.md
|
|
22
22
|
|
|
23
|
-
Repository-root `DESIGN.md` owns all UI/UX design
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
Repository-root `DESIGN.md` owns all UI/UX design documentation (industry standard visual language, color tokens, typography, component specs, accessibility, and responsive patterns). Read and update it for UI work; keep `docs/04_DESIGN.md` focused on non-UI surface design by default. If `DESIGN.md` is absent, ignore it and follow the project's established UI conventions.
|
|
24
|
+
|
|
25
|
+
By contrast, `docs/04_DESIGN.md` is our SSOT of non-UI surface design by default — covering CLI command signatures, flags, config schemas, DTOs, tables, and system boundaries.
|
|
26
|
+
|
|
27
|
+
When collaborating with the design team:
|
|
28
|
+
- **UI/UX & Visual Design:** Refer to and update repository-root `DESIGN.md`.
|
|
29
|
+
- **Non-UI Surface & API/Schema DTOs:** Refer to and update `docs/04_DESIGN.md` (and `docs/design/<slug>.md` satellites).
|
|
27
30
|
|
|
28
31
|
## 1. CLI commands
|
|
29
32
|
|
|
@@ -80,7 +80,8 @@ Each project's `AGENTS.md` embeds an instantiated copy of this table (§4.4). A
|
|
|
80
80
|
| `docs/01_PRD.md` | **WHAT** — product vision, users, scope (in / out / deferred) | **Authoritative on scope** | Read before adding a command/feature; edit when scope changes |
|
|
81
81
|
| `docs/02_ROADMAP.md` | **WHEN** — phases, current vs deferred, sequencing | Derived | Read to place work in a phase; edit when phase status changes |
|
|
82
82
|
| `docs/03_ARCHITECTURE.md` | **HOW** — module boundaries, data flow, runtime model, invariants, rationale-in-depth | Derived (ADR wins) | Read before cross-module/seam/schema work; edit when boundaries or mechanisms change |
|
|
83
|
-
| `docs/04_DESIGN.md` | **SURFACE** — concrete shapes: every CLI command, flag, config key, env var, table, DTO; **index over `docs/design/<slug>.md`** (§4.5) | Derived | Read/edit when changing a command, flag, env var, or schema — same commit |
|
|
83
|
+
| `docs/04_DESIGN.md` | **SURFACE** — concrete shapes: every CLI command, flag, config key, env var, table, DTO; **index over `docs/design/<slug>.md`** (§4.5) | Derived | Read/edit when changing a non-UI command, flag, env var, or schema — same commit |
|
|
84
|
+
| `DESIGN.md` (repo root) | **UI/UX SURFACE** — visual design, color tokens, typography, component specs, layout, micro-animations, accessibility | **Authoritative for UI/UX when present** | Read/edit when planning or implementing UI/UX visual changes (dynamically supported; ignored when absent) |
|
|
84
85
|
| `docs/05_FEATURES.md` | **STATUS** — feature decomposition + state (✅ done / 🔶 partial / ⏳ planned / 💤 deferred); **index over `docs/features/<id>_<slug>.md`** (§4.5) | Derived | Read to find a feature's state; edit when a feature's status changes |
|
|
85
86
|
| `docs/99_PROJECT_CONSTITUTION.md` | **PROCESS** — how the files above are maintained | **Authoritative on process** | Read before editing any doc above; edit per §6.8 |
|
|
86
87
|
| `AGENTS.md` (repo root) | **ENTRY** — how agents work in this repo: stack, commands, gates, conventions + the instantiated doc map | Derived (from 99 + 00/01/04) | Read first every session; regenerate factual blocks from code (§6.7) |
|
|
@@ -89,7 +90,8 @@ Each project's `AGENTS.md` embeds an instantiated copy of this table (§4.4). A
|
|
|
89
90
|
|
|
90
91
|
- Decision + one-line reason → `00`. Rationale/mechanism in depth → `03`.
|
|
91
92
|
- Scope (in/out/deferred) → `01`. Mechanism / data flow / invariants → `03`.
|
|
92
|
-
-
|
|
93
|
+
- UI/UX visual design, design tokens, component specs & accessibility → `DESIGN.md` (when present; otherwise follow established project UI conventions).
|
|
94
|
+
- Non-UI command/flag/config/schema/DTO shapes → `04`. Phase timing → `02`. Feature status → `05`.
|
|
93
95
|
- If you are writing *how it's built* or *why* inside `00`/`01`/`02`, it belongs in `03`/`04`.
|
|
94
96
|
|
|
95
97
|
### 4.2 Working layers (outside the authority chain)
|
|
@@ -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.30",
|
|
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.
|
|
54
|
-
"@gobing-ai/ts-ai-runner": "^0.4.
|
|
55
|
-
"@gobing-ai/ts-dual-workflow-engine": "^0.4.
|
|
56
|
-
"@gobing-ai/ts-infra": "^0.4.
|
|
57
|
-
"@gobing-ai/ts-llm-jsonl-importer": "^0.4.
|
|
58
|
-
"@gobing-ai/ts-rule-engine": "^0.4.
|
|
59
|
-
"@gobing-ai/ts-runtime": "^0.4.
|
|
60
|
-
"@gobing-ai/ts-utils": "^0.4.
|
|
53
|
+
"@gobing-ai/ts-db": "^0.4.16",
|
|
54
|
+
"@gobing-ai/ts-ai-runner": "^0.4.16",
|
|
55
|
+
"@gobing-ai/ts-dual-workflow-engine": "^0.4.16",
|
|
56
|
+
"@gobing-ai/ts-infra": "^0.4.16",
|
|
57
|
+
"@gobing-ai/ts-llm-jsonl-importer": "^0.4.16",
|
|
58
|
+
"@gobing-ai/ts-rule-engine": "^0.4.16",
|
|
59
|
+
"@gobing-ai/ts-runtime": "^0.4.16",
|
|
60
|
+
"@gobing-ai/ts-utils": "^0.4.16",
|
|
61
61
|
"@types/bun": "1.3.14",
|
|
62
62
|
"@types/figlet": "^1.7.0",
|
|
63
63
|
"@types/node-notifier": "8.0.5",
|
|
@@ -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",
|