@gobing-ai/spur 0.4.30 → 0.4.33
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/workflows/history-anatomy.yaml +13 -2
- package/config/workflows/task-pipeline.yaml +5 -0
- package/package.json +1 -1
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/skills/history-anatomy/references/operations.md +11 -0
- package/plugins/sp/skills/history-anatomy/references/report-contract.md +28 -0
- package/plugins/sp/skills/spur-cli/references/tasks.md +45 -2
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +26 -0
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +5 -0
- package/spur.js +1038 -156
|
@@ -144,11 +144,13 @@ states:
|
|
|
144
144
|
onEnter:
|
|
145
145
|
- kind: shell
|
|
146
146
|
options:
|
|
147
|
+
# 1326: the selector is NOT provenance. `--executor "$agent"` used to stamp the literal
|
|
148
|
+
# `auto`; the resolved executor/model now come from the run's own action receipts at stamp.
|
|
147
149
|
command: >-
|
|
148
150
|
. .spur/run/$__runId-paths.txt;
|
|
149
151
|
$spurBin history anatomy-probe --artifact .spur/run/$__runId-history-anatomy-current.json
|
|
150
152
|
--baseline .spur/run/$__runId-history-anatomy-baseline.json --target "$HA_TARGET"
|
|
151
|
-
--mode "$mode" --date "$HA_DATE" --recompute "$recompute"
|
|
153
|
+
--mode "$mode" --date "$HA_DATE" --recompute "$recompute"
|
|
152
154
|
--skill-dir "$HA_SKILL" --contract "$HA_SKILL/references/report-contract.md"
|
|
153
155
|
--workflow "$workflowFile" --helper "$HA_HELPER" --contract-version "$contractVersion" --run-id "$__runId"
|
|
154
156
|
--out .spur/run/$__runId-provenance.json
|
|
@@ -322,14 +324,23 @@ states:
|
|
|
322
324
|
Cache-miss path. Attaches the full R7 frontmatter provenance block (identity tuple, window
|
|
323
325
|
state, generated/validated timestamps, artifact paths + digests, contract/skill/workflow
|
|
324
326
|
digests, per-source coverage + last_imported_at, spur/schema version, executor, run id,
|
|
325
|
-
cache disposition) and the freshness banner to the validated candidate.
|
|
327
|
+
cache disposition) and the freshness banner to the validated candidate. 1326 R5/R6: the
|
|
328
|
+
validated timestamp and the per-action executor/model rows come from the run's OWN receipts
|
|
329
|
+
(the validation artifact and the run trace), never from probe time or the selector.
|
|
326
330
|
onEnter:
|
|
331
|
+
- kind: shell
|
|
332
|
+
options:
|
|
333
|
+
command: >-
|
|
334
|
+
mkdir -p .spur/run;
|
|
335
|
+
$spurBin workflow trace "$__runId" --json > .spur/run/$__runId-trace.json
|
|
327
336
|
- kind: shell
|
|
328
337
|
options:
|
|
329
338
|
command: >-
|
|
330
339
|
. .spur/run/$__runId-paths.txt;
|
|
331
340
|
$spurBin history anatomy-stamp --candidate .spur/run/$__runId-candidate.md
|
|
332
341
|
--provenance .spur/run/$__runId-provenance.json
|
|
342
|
+
--validation .spur/run/$__runId-validation.txt
|
|
343
|
+
--trace .spur/run/$__runId-trace.json
|
|
333
344
|
--out .spur/run/$__runId-publishable.md
|
|
334
345
|
|
|
335
346
|
- id: publish
|
|
@@ -539,6 +539,11 @@ states:
|
|
|
539
539
|
executable: "${vars.spurBin}"
|
|
540
540
|
args: ["task", "gate", "${vars.wbs}", "${vars.gateMode}", "--no-logo"]
|
|
541
541
|
resultFile: .spur/run/${vars.__runId}-gate.status
|
|
542
|
+
# 1322 R1/R4: the step's own output persists at the gate log (`.spur/run/<wbs>-gate.log`),
|
|
543
|
+
# so the driver's single bounded wait reads evidence from a known path instead of
|
|
544
|
+
# sleep-polling, and a failing gate carries the resolved path in its step result
|
|
545
|
+
# (`data.logFile`) — remediation reads evidence without re-running anything.
|
|
546
|
+
logFile: .spur/run/${vars.wbs}-gate.log
|
|
542
547
|
softFail: true
|
|
543
548
|
# 1319 R2(c): group-owned backstop. The gate's own per-spawn bound (R1) bounds
|
|
544
549
|
# each command; this bounds the action and reaps any surviving descendant.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/spur",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.33",
|
|
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",
|
package/plugins/sp/plugin.json
CHANGED
|
@@ -35,6 +35,14 @@ Given the current and baseline artifacts, author the model half of the report. A
|
|
|
35
35
|
ownerSurface). Categories from the closed vocabulary; stable keys of the form
|
|
36
36
|
`<category>:<owner-surface>:<signal>`. Severity is the closed P1/P2/P3 vocabulary; reproCommand reproduces the observation verbatim; ownerSurface names
|
|
37
37
|
the concrete owning surface (file/package/command), consistent with the key's middle segment.
|
|
38
|
+
**Every derived numeric comparison, delta, ratio, or sum in `observation`/`inference` carries an
|
|
39
|
+
adjacent `calc:` assertion** (grammar in `report-contract.md`). Write the arithmetic, do not
|
|
40
|
+
assert the conclusion: a claim that one figure is larger needs `calc: <a> > <b>`, a gap needs
|
|
41
|
+
`calc: <a> - <b> = <gap>`. The deterministic structure gate fails a false assertion
|
|
42
|
+
(`arithmetic-contradiction`), a missing one on a derived field (`arithmetic-evidence-missing`),
|
|
43
|
+
and an unparsable one (`arithmetic-invalid-assertion`) before validation runs — the observed
|
|
44
|
+
2026-11-08 inversion (`719,838,772` called larger than `725,707,709`) burned a full correction
|
|
45
|
+
pass precisely because no gate could see it.
|
|
38
46
|
4. **Recurrence ledger** — classify every finding against the baseline on the **stable key**.
|
|
39
47
|
5. **Telemetry gaps** — every dimension the artifact cannot support, rendered `not available`.
|
|
40
48
|
6. **Remediation options** — proposals only (owner surface, expected impact, verification method,
|
|
@@ -74,6 +82,9 @@ Given a candidate report and the artifacts, independently verify:
|
|
|
74
82
|
- **Positive patterns** — held to the same standard; an anchor-less entry FAILs.
|
|
75
83
|
- **Remediation** — proposals only; any applied change, diff, or command the report claims to have
|
|
76
84
|
run FAILs.
|
|
85
|
+
- **Arithmetic** — every `calc:` assertion recomputes; a claim whose assertion does not hold, or a
|
|
86
|
+
derived claim with no assertion, is a finding against the report. Independent validation still
|
|
87
|
+
owns whether the operands belong to the cited artifact; the deterministic gate owns the math.
|
|
77
88
|
|
|
78
89
|
Emit `PASS` only when every rule holds. On FAIL, name the section, the finding key, and the rule
|
|
79
90
|
violated. Never launch a workflow.
|
|
@@ -68,6 +68,34 @@ Every finding — problem and positive alike — carries the full field set:
|
|
|
68
68
|
| `reproCommand` | The invocation that reproduces the observation — one command a reader can run verbatim (0680 R2). Gate-enforced. |
|
|
69
69
|
| `ownerSurface` | The concrete surface that owns the fix (a file path, package, or command), consistent with the key's `<owner-surface>` segment but named as a target rather than a slug (0680 R3). Gate-enforced. |
|
|
70
70
|
|
|
71
|
+
## Arithmetic assertions (`calc:`) — task 1326
|
|
72
|
+
|
|
73
|
+
A field that makes a **derived numeric claim** must carry its arithmetic next to it, in the same
|
|
74
|
+
field, as a machine-checkable line. The deterministic structure gate (`checkReportStructure`)
|
|
75
|
+
rejects a false assertion and a derived claim that has none — before independent validation, so the
|
|
76
|
+
model validator is never the only arithmetic check.
|
|
77
|
+
|
|
78
|
+
Grammar (frozen; no `eval`, no parentheses, no unit suffixes, no arbitrary JavaScript):
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
calc: <number> <+|-> [number ...] = <number> # left-associative sums and deltas
|
|
82
|
+
calc: <number> * <number> = <number> # product
|
|
83
|
+
calc: <number> / <number> = <number> # ratio
|
|
84
|
+
calc: <number> <|<=|>|>= <number> # ordering claim
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
- Integers are exact; a non-integer result uses a relative tolerance of `1e-9`.
|
|
88
|
+
- The label may be written bare (`calc: …`) or as inline code (`` `calc`: … ``) — the gate accepts
|
|
89
|
+
both, and both must parse.
|
|
90
|
+
- **Detection is deterministic and vocabulary-based.** An `observation` or `inference` field counts
|
|
91
|
+
as derived when it contains `%` or one of `larger|smaller|higher|lower|more than|less than|above|
|
|
92
|
+
below|exceeds|dominates|gap|delta|difference|sum|sums|sum of|share|ratio|mean|average`
|
|
93
|
+
(case-insensitive). Naming a raw artifact field alone (`totals.inputTokens`) is not a derived
|
|
94
|
+
claim. A grammar failure is `arithmetic-invalid-assertion`; a false assertion is
|
|
95
|
+
`arithmetic-contradiction`; a derived field with no assertion is `arithmetic-evidence-missing`.
|
|
96
|
+
- The assertion names the arithmetic; the operands still have to belong to the cited artifact, and
|
|
97
|
+
that ownership check stays with independent validation. The gate never silently corrects a report.
|
|
98
|
+
|
|
71
99
|
## Evidence rules
|
|
72
100
|
|
|
73
101
|
Enforcement rail for every claim:
|
|
@@ -28,18 +28,61 @@ spur task get <wbs> --json # alias of `show` — same output, no separate code
|
|
|
28
28
|
spur task path <wbs> --json # absolute path only
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
Sectioned and summary reads (task 1325) stay on the same resolution path — see
|
|
32
|
+
[Sectioned and summary reads](#sectioned-and-summary-reads-task-1325).
|
|
33
|
+
|
|
31
34
|
Do not search `docs/tasks*` or guess `--folder` to locate a known WBS. `show` is the default when an
|
|
32
35
|
agent needs to read a task; use `path` only when another filesystem command needs the absolute path.
|
|
33
36
|
Both commands resolve across configured task folders. Add `--folder` only when deliberately limiting
|
|
34
37
|
the lookup to one non-default corpus. Capture `show` once per run and reuse its response instead of
|
|
35
38
|
re-reading or re-tokenizing the task.
|
|
36
39
|
|
|
40
|
+
## Sectioned and summary reads (task 1325)
|
|
41
|
+
|
|
42
|
+
`show` can return part of a task instead of the whole body — useful when an agent only needs one
|
|
43
|
+
section or an index:
|
|
44
|
+
|
|
45
|
+
- **`--section <name>`** (repeatable) and **`--sections "A,B"`** (CSV) return only the named
|
|
46
|
+
sections. Each entry carries `name`, `headingLine`, `byteCount` (Buffer bytes, multi-byte aware),
|
|
47
|
+
and `content` — the section byte-exact as it sits in the file (heading line through the line
|
|
48
|
+
before the next heading), suitable for exact `--from-file`-style reuse. Frontmatter metadata and
|
|
49
|
+
`filePath` ride along; `totalBytes` is the whole file's byte size. Sections return in **file
|
|
50
|
+
order**, duplicates collapse. Unknown names exit `3` (same channel as `sections list`) and name
|
|
51
|
+
the valid sections.
|
|
52
|
+
- **`--summary`** returns the full section index (`name`/`headingLine`/`byteCount` per section plus
|
|
53
|
+
`totalBytes`) with **no** `content` fields — the cheap table-of-contents probe. `--summary` cannot
|
|
54
|
+
be combined with `--section`/`--sections` (exit `2`).
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
spur task show 1325 --section Plan --json # one section, byte-exact
|
|
58
|
+
spur task show 1325 --section Plan --section Q&A # repeatable, file order
|
|
59
|
+
spur task show 1325 --sections "Plan,History" --json
|
|
60
|
+
spur task show 1325 --summary --json # index only, no bodies
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Write-time size advisory (task 1325)
|
|
64
|
+
|
|
65
|
+
`create` and `batch-create` (and the service-level `updateBody` write path) report the written
|
|
66
|
+
file's size against the precheck budget at write time: a task at or over the R-item /
|
|
67
|
+
prose-paragraph threshold gets a `Task has N R-items (max 10)`-style advisory in the write
|
|
68
|
+
result's `warnings[]`. The advisory **never blocks the write** — it is the authoring-time early
|
|
69
|
+
warning that `task check --precheck` enforces as a gate later. Rendering: single `create`
|
|
70
|
+
surfaces top-level `warnings` in `--json` and prints each advisory bare to stderr in human
|
|
71
|
+
mode; `batch-create` surfaces per-child `warnings` rows (`[{"wbs", "warnings"}]`, present only
|
|
72
|
+
when non-empty) in `--json` and prints `<wbs>: <warning>` lines to stderr in human mode;
|
|
73
|
+
`update --section` section writes carried this advisory already.
|
|
74
|
+
|
|
75
|
+
When an advisory fires, split the task instead of growing it: decompose into smaller tasks before
|
|
76
|
+
pipeline execution (execution-workflow.md, "Split oversized tasks before pipeline execution").
|
|
77
|
+
`create --requirements/--plan` and `batch-create` body fields exist to author the split tasks'
|
|
78
|
+
content up front — not to accumulate more into one oversized task.
|
|
79
|
+
|
|
37
80
|
## Verb map
|
|
38
81
|
|
|
39
82
|
| Verb | Purpose | Key flags |
|
|
40
83
|
| ---- | ------- | --------- |
|
|
41
|
-
| `create <title>` | Allocate a new task (race-safe WBS) | `--feature <id>` `--no-reopen` `--parent <wbs>` `--template <variant>` `--dedupe-within <s>` `--allow-duplicate-name` `--skip-ready` `--agent <selector>` `--folder` `--json` |
|
|
42
|
-
| `show <wbs>` | Print one task's frontmatter + body | `--folder` `--json` |
|
|
84
|
+
| `create <title>` | Allocate a new task (race-safe WBS) | `--feature <id>` `--no-reopen` `--parent <wbs>` `--template <variant>` `--requirements <text>` `--plan <text>` `--dedupe-within <s>` `--allow-duplicate-name` `--skip-ready` `--agent <selector>` `--folder` `--json` |
|
|
85
|
+
| `show <wbs>` | Print one task's frontmatter + body; section-filtered or summary mode (1325) | `--section <name>` `--sections <csv>` `--summary` `--folder` `--json` |
|
|
43
86
|
| `update <wbs> [status]` | Lifecycle transition, section replace, **or** frontmatter set | `--section <name> --from-file <path>` `--assignee <spec-id>` (exclusive with `--section`) `--feature <id>` `--no-reopen` `--priority <p>` `--ac-numbering task-local` `--ac-altitude <a>` `--estimate-hours <n>` `--no-lifecycle` `--force-done` `--reason <text>` `--provenance-bypass` `--verdict-dir <path>` `--folder` `--json` |
|
|
44
87
|
| `deps <wbs> <op> [values...]` | Mutate `dependencies[]` frontmatter array (ops: `set`, `add`, `remove`, `clear`) | `--folder` `--json` |
|
|
45
88
|
| `sections <wbs> <op> [name]` | Initialize, add, or list canonical task sections (ops: `init`, `add`, `list`) | `--folder` `--json` |
|
|
@@ -470,6 +470,32 @@ implement stage then conflated. The pipeline precheck prints a WARNING with the
|
|
|
470
470
|
tree has uncommitted non-corpus changes; treat it as a stop-and-commit, not noise. It warns rather
|
|
471
471
|
than blocks because a legitimately in-progress tree is the operator's call.
|
|
472
472
|
|
|
473
|
+
## Long-running commands: launch detached, wait bounded
|
|
474
|
+
|
|
475
|
+
Applies whenever a step runs a command that takes minutes — full gates, coverage passes, e2e
|
|
476
|
+
rehearsals, engine runs. KK's post-0.4.30 history analysis (2026-11-10) shows the failure mode:
|
|
477
|
+
implement sessions spent **88 minutes in literal `sleep` calls plus 28 poll loops** babysitting
|
|
478
|
+
~18-minute gates, and one rehearsal session emitted **3,455 near-identical poll messages over 14.5
|
|
479
|
+
hours** while hand-dispatching LLM stages as background tasks.
|
|
480
|
+
|
|
481
|
+
- **Start it detached, then wait once, bounded.** Background the process with a log file
|
|
482
|
+
(`cmd > .spur/run/<name>.log 2>&1 &`), then use a single bounded wait — one tail after the
|
|
483
|
+
expected duration, or `spur workflow trace <run> --follow --timeout 600000` for engine runs.
|
|
484
|
+
Never loop `sleep 30` turns or re-report the same wait in a fresh message.
|
|
485
|
+
- **Multi-dispatch flows run as a workflow DAG.** A rehearsal driving several LLM stages
|
|
486
|
+
(curate → write → judge → revise) belongs in `spur workflow run` so the engine owns dispatch,
|
|
487
|
+
timeout, and retry — the session orchestrates, it does not babysit.
|
|
488
|
+
- **Cheap tiers between phases, full gate at the boundary.** A full-repo gate costs 15–19 minutes
|
|
489
|
+
in a mid-size monorepo; interleave the light tier and reserve the full pass for the quality
|
|
490
|
+
boundary (see [`../../spur-check/SKILL.md`](../../spur-check/SKILL.md)). The task pipeline does this
|
|
491
|
+
deterministically (1322): recheck visits triage through the affected-scope light tier first —
|
|
492
|
+
a red tier fails the visit without paying the full chain, and a selection that touches a shared
|
|
493
|
+
package or an unmapped path falls back to the full suite rather than trusting the tier.
|
|
494
|
+
- **Gate output persists at a known path.** The pipeline's gate step tees its output to
|
|
495
|
+
`.spur/run/<wbs>-gate.log` (1322 R1); wait once, bounded, then read the log — never sleep-poll
|
|
496
|
+
a running gate. A failing gate carries that resolved path in its step result (`data.logFile`),
|
|
497
|
+
so remediation reads evidence without re-running anything (1322 R4).
|
|
498
|
+
|
|
473
499
|
## Iron Laws
|
|
474
500
|
|
|
475
501
|
Seven non-negotiable invariants for the spur-dev lifecycle. These are laws, not guidelines — a
|
|
@@ -155,6 +155,11 @@ background and let its exit report the terminal verdict. Use `spur workflow trac
|
|
|
155
155
|
Synchronous invocation (`--json` without `--async`) is acceptable **only** for short pipelines
|
|
156
156
|
(< 2 min, e.g. precheck-only or a dry-run). Do not use it for the full task pipeline.
|
|
157
157
|
|
|
158
|
+
Gate steps are the >5-minute case (1322): the gate's output persists at
|
|
159
|
+
`.spur/run/<wbs>-gate.log`, and a failing gate's step result carries that resolved path
|
|
160
|
+
(`logFile`), so the wait is one `--follow` call and the evidence read is one file read — never a
|
|
161
|
+
`sleep`/`trace` poll loop (0421).
|
|
162
|
+
|
|
158
163
|
On the subprocess path, when `--agent <value>` is set (passed through from the thin wrapper), merge it into the vars as
|
|
159
164
|
**both** `agent` and `implementAgent` (task 0483 R2):
|
|
160
165
|
`--vars '{"wbs":"<wbs>","agent":"<value>","implementAgent":"<value>"}'`. The pipeline YAML reads
|