@gobing-ai/spur 0.4.30 → 0.4.31

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.
@@ -7,7 +7,7 @@
7
7
  "plugins": [
8
8
  {
9
9
  "name": "sp",
10
- "version": "0.4.30",
10
+ "version": "0.4.31",
11
11
  "source": "./plugins/sp"
12
12
  }
13
13
  ]
@@ -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" --executor "$agent"
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.30",
3
+ "version": "0.4.31",
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",
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "sp",
3
- "version": "0.4.30",
3
+ "version": "0.4.31",
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
  }
@@ -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,56 @@ 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
+
37
75
  ## Verb map
38
76
 
39
77
  | Verb | Purpose | Key flags |
40
78
  | ---- | ------- | --------- |
41
79
  | `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` |
80
+ | `show <wbs>` | Print one task's frontmatter + body; section-filtered or summary mode (1325) | `--section <name>` `--sections <csv>` `--summary` `--folder` `--json` |
43
81
  | `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
82
  | `deps <wbs> <op> [values...]` | Mutate `dependencies[]` frontmatter array (ops: `set`, `add`, `remove`, `clear`) | `--folder` `--json` |
45
83
  | `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