@gobing-ai/spur 0.3.65 → 0.3.67
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/corpus-baseline.json +495 -10491
- package/config/workflows/history-anatomy.yaml +5 -4
- package/package.json +1 -1
- package/plugins/sp/agents/expert-spur.md +61 -88
- package/plugins/sp/commands/dev-review-session.md +11 -6
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +4 -0
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +4 -0
- package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +1 -1
- package/plugins/sp/scripts/dogfood-testing/validate-report.ts +3 -2
- package/plugins/sp/skills/dogfood-testing/SKILL.md +11 -11
- package/plugins/sp/skills/session-review/SKILL.md +39 -6
- package/plugins/sp/skills/spur-cli/SKILL.md +38 -13
- package/plugins/sp/skills/spur-cli/references/agent.md +7 -4
- package/plugins/sp/skills/spur-cli/references/history.md +69 -0
- package/plugins/sp/skills/spur-cli/references/message.md +2 -2
- package/plugins/sp/skills/spur-cli/references/projects.md +59 -0
- package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +35 -1
- package/plugins/sp/skills/spur-cli/references/team.md +1 -1
- package/plugins/sp/skills/spur-cli/references/workflows.md +6 -0
- package/plugins/sp/skills/spur-dev/SKILL.md +5 -0
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +2 -2
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +71 -15
- package/spur.js +363 -174
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spur-cli-history
|
|
3
|
+
description: "spur-cli noun reference: operate `spur history` to import coding-agent transcripts, aggregate versioned forensic artifacts, render an artifact without database access, or run the checkpoint-resumed daily pipeline."
|
|
4
|
+
see_also:
|
|
5
|
+
- spur-cli
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# spur history - local forensic analytics
|
|
9
|
+
|
|
10
|
+
`spur history` is the local history data plane: import transcripts into SQLite, aggregate a
|
|
11
|
+
versioned artifact, render that artifact, or run the daily composition. Command registration and
|
|
12
|
+
CLI guards live in `apps/cli/src/commands/history.ts` (`registerHistoryCommand`); payload contracts
|
|
13
|
+
live in `packages/app/src/services/history-service.ts` (`FanOutResult`, `DailyResult`,
|
|
14
|
+
`HistoryService`, `runHistoryReport`) and `packages/domain/src/analytics/artifact.ts`
|
|
15
|
+
(`HistoryArtifact`).
|
|
16
|
+
|
|
17
|
+
## Verb map
|
|
18
|
+
|
|
19
|
+
| Verb | Purpose | Key flags |
|
|
20
|
+
| ---- | ------- | --------- |
|
|
21
|
+
| `import` | Import one source or fan out across all supported sources | `--source <source>` `--file <path>` `--root <path>` `--mode <mode>` `--dry-run` `--source-timeout <ms>` `--json` |
|
|
22
|
+
| `analyze` | Aggregate imported rows and write a versioned forensic artifact | `--since <iso>` `--until <iso>` `--source <source>` `--session <id>` `--run <runId>` `--task <wbs>` `--top <n>` `--out <path>` `--json` |
|
|
23
|
+
| `report [path]` | Purely render an existing artifact; default to `latest.json` | `--mode <name>` `--task <wbs>` `--top <n>` `--json` |
|
|
24
|
+
| `daily` | Run import-all → analyze → artifact → 90-day report pruning once | `--since <iso>` `--until <iso>` `--root <path>` `--source-timeout <ms>` `--mode <name>` `--json` |
|
|
25
|
+
|
|
26
|
+
Every JSON-capable verb also advertises `--json-envelope`; use the facade's machine-output contract.
|
|
27
|
+
|
|
28
|
+
## `import` - isolated fan-out
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
bun run apps/cli/src/index.ts history import --source all --dry-run --json
|
|
32
|
+
bun run apps/cli/src/index.ts history import --source codex --mode incremental --json
|
|
33
|
+
bun run apps/cli/src/index.ts history import --source codex --file session.jsonl --mode force-file --json
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- `--source all` and a single source use the same per-source fan-out path. A failed/timed-out source
|
|
37
|
+
does not abort its siblings.
|
|
38
|
+
- Modes are `incremental`, `full`, and `force-file`. `--file` with the default `all` source is a
|
|
39
|
+
usage error. `--file --mode full` requires `--dry-run`; use `force-file` for a real single-file
|
|
40
|
+
write.
|
|
41
|
+
- JSON contains `entries`, `warnings`, `exitCode`, and CLI/importer `provenance`. For real-data
|
|
42
|
+
validation, invoke the source-local CLI and record that provenance; never trust a bare global
|
|
43
|
+
`spur` that may be stale.
|
|
44
|
+
- Exit `0` when every source is clean/empty, `2` for a mixed failure or any degraded source, and `1`
|
|
45
|
+
when all sources fail. CLI usage guards also exit `1` on this noun.
|
|
46
|
+
|
|
47
|
+
## `analyze` - artifact writer
|
|
48
|
+
|
|
49
|
+
`analyze` performs SQL aggregation over imported history and writes a stable, versioned
|
|
50
|
+
`HistoryArtifact`. Selectors combine with AND. `--top` bounds leaderboards, not totals. `--out`
|
|
51
|
+
overrides the dated report path; otherwise the service writes under `.spur/reports/history/` and
|
|
52
|
+
updates `latest.json`. Human mode renders a summary; `--json` emits the artifact.
|
|
53
|
+
|
|
54
|
+
## `report` - pure artifact renderer
|
|
55
|
+
|
|
56
|
+
`report` never opens the database. It reads an explicit artifact or the `latest.json` pointer,
|
|
57
|
+
validates the schema version, optionally narrows the loaded artifact with `--task` / `--top`, and
|
|
58
|
+
renders `default` or `forensics` mode. Unknown modes, invalid `--top`, missing/mismatched task
|
|
59
|
+
dimensions, missing artifacts, and schema mismatches exit `1` instead of silently widening output.
|
|
60
|
+
|
|
61
|
+
## `daily` - run-once composition
|
|
62
|
+
|
|
63
|
+
`daily` runs incremental import-all, analyze, artifact write, and report-directory pruning in one
|
|
64
|
+
process. `--since` / `--until` scope analysis only, not import. Checkpoints make a missed run resume
|
|
65
|
+
without double-counting. The result carries `{ fanOut, artifact, pruned, coverage, reportPath? }`;
|
|
66
|
+
its exit code is the fan-out exit code. `--mode` adds a rendered sidecar after analysis.
|
|
67
|
+
|
|
68
|
+
For the full artifact and data-plane contracts, use `docs/04_DESIGN.md` history sections and
|
|
69
|
+
`docs/design/history-data-processing.md`; do not infer fields from rendered prose.
|
|
@@ -24,8 +24,8 @@ use it well*.
|
|
|
24
24
|
| `reply <msg-id> <body>` | Thread a reply to a message | `--json` |
|
|
25
25
|
| `watch` | Follow an agent inbox - surface new messages as they arrive | `--agent <id>` `--interval <ms>` `--json` |
|
|
26
26
|
|
|
27
|
-
All verbs accept `--json`
|
|
28
|
-
invalid usage.
|
|
27
|
+
All verbs accept `--json` and `--json-envelope`. `watch` applies the envelope per emitted row.
|
|
28
|
+
**Exit codes:** `0` success, `1` error, `2` invalid usage.
|
|
29
29
|
|
|
30
30
|
## `send` - enqueue a message
|
|
31
31
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spur-cli-projects
|
|
3
|
+
description: "spur-cli noun reference: operate `spur projects` to register local project roots, inspect live status, start detached project servers, and stop or remove registry entries."
|
|
4
|
+
see_also:
|
|
5
|
+
- spur-cli
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# spur projects - local multi-project registry
|
|
9
|
+
|
|
10
|
+
`spur projects` manages the machine-local registry resolved by
|
|
11
|
+
`packages/config/src/projects.ts` (`getProjectsFilePath`) and implemented by
|
|
12
|
+
`packages/app/src/services/project-registry.ts` (`ProjectRegistry`). Server startup is owned by
|
|
13
|
+
`packages/app/src/services/project-start.ts` (`startRegisteredProject`); CLI registration and JSON
|
|
14
|
+
shapes live in `apps/cli/src/commands/projects.ts`.
|
|
15
|
+
|
|
16
|
+
## Verb map
|
|
17
|
+
|
|
18
|
+
| Verb | Purpose | Key flags |
|
|
19
|
+
| ---- | ------- | --------- |
|
|
20
|
+
| `add <path>` | Upsert an existing path in the registry | `--name <name>` `--json` |
|
|
21
|
+
| `remove <target>` | Remove an entry by display name or path | `--json` |
|
|
22
|
+
| `list` | List entries with live running status | `--json` |
|
|
23
|
+
| `start <target>` | Start or reuse a detached project server | `--port <n>` `--json` |
|
|
24
|
+
| `stop <target>` | Best-effort stop the listener and clear its recorded port | `--json` |
|
|
25
|
+
|
|
26
|
+
Every verb also advertises `--json-envelope`; use the facade's machine-output contract. Success is
|
|
27
|
+
exit `0`; validation, registry, spawn, health, or lookup failure is exit `1`.
|
|
28
|
+
|
|
29
|
+
## Registry behavior
|
|
30
|
+
|
|
31
|
+
- The default file is `~/.config/spur/projects.json`; `SPUR_PROJECTS_FILE` overrides it. Entries are
|
|
32
|
+
`{ name, path, port }`, where `port: 0` means stopped.
|
|
33
|
+
- Paths are normalized (including `~`) and existing paths resolve to real paths. Name lookup is
|
|
34
|
+
case-insensitive. Registry mutations use an advisory lock.
|
|
35
|
+
- `add` requires an existing path, resolves a relative path from the current working directory, and
|
|
36
|
+
defaults the display name to its basename. It upserts; it does not start a server. The current
|
|
37
|
+
source does not enforce a `.spur/` marker or directory type.
|
|
38
|
+
- `list` probes recorded ports and heals stale entries to `port: 0` before reporting `running`.
|
|
39
|
+
|
|
40
|
+
## Server lifecycle
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
spur projects start my-project --json
|
|
44
|
+
spur projects start /path/to/unregistered/project --port 3333 --json
|
|
45
|
+
spur projects stop my-project --json
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`start` resolves by name or path. An existing unregistered path is auto-registered. A live recorded
|
|
49
|
+
port is returned idempotently; otherwise the service allocates a port (3000–3999 unless explicitly
|
|
50
|
+
set), spawns `spur serve --host 127.0.0.1 --no-open` detached in the project root, waits for the port,
|
|
51
|
+
then persists it.
|
|
52
|
+
|
|
53
|
+
`stop` finds processes bound to the recorded port, sends `SIGTERM` best-effort while excluding the
|
|
54
|
+
CLI and its parent, and clears the registry port. It is not a persistent supervisor contract.
|
|
55
|
+
`remove` only removes the registry entry; stop a running project first when process cleanup matters.
|
|
56
|
+
|
|
57
|
+
Raw JSON success payloads are verb-specific (`project`, `projects`, `removed`, `stopped`, or start
|
|
58
|
+
status fields). Under `--json-envelope` they move beneath `data`; failures normalize beneath
|
|
59
|
+
`error`. Read `apps/cli/src/commands/projects.ts` for exact fields.
|
|
@@ -54,7 +54,9 @@ frontmatter scalar.
|
|
|
54
54
|
cancelled`. Two transitions run a target-aware `check` guard (§7.5): `wip→testing` →
|
|
55
55
|
`spur task check <wbs> --as testing`; `testing→done` → `spur task check <wbs> --as done`
|
|
56
56
|
(F92 R3 — each evaluates the transition target, so `testing→done` checks the `done` row).
|
|
57
|
-
A failing gate blocks the transition.
|
|
57
|
+
A failing gate blocks the transition. The `wip→testing` guard additionally requires a Solution
|
|
58
|
+
section citing concrete `file:line` evidence — a missing/unsubstantiated Solution is rejected with
|
|
59
|
+
`[invalid-solution]`.
|
|
58
60
|
- **`--no-lifecycle`**: suppress lifecycle workflow *run record* creation (use inside pipeline runs
|
|
59
61
|
to avoid orphaned nested lifecycle runs). **It is not a guard bypass** — the `wip→testing` and
|
|
60
62
|
`testing→done` `check` gates above still run; the CLI evaluates them inline when the FSM guard
|
|
@@ -221,6 +223,18 @@ the active tasks folder (or `--folder`).
|
|
|
221
223
|
| `--folder <path>` | Custom tasks folder. |
|
|
222
224
|
| `--json` | Machine-readable report envelope. |
|
|
223
225
|
|
|
226
|
+
## `migrate-anchors`
|
|
227
|
+
|
|
228
|
+
Qualify ambiguous in-repo evidence anchors to repo-relative paths across the task corpus. Run
|
|
229
|
+
`--dry-run` first: unambiguous matches are reported in `qualified`, multiple matches in `ambiguous`
|
|
230
|
+
without rewriting, and schema-incompatible files in `skipped`. The write path uses the planning
|
|
231
|
+
service rather than raw file edits.
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
spur task migrate-anchors --dry-run --json
|
|
235
|
+
spur task migrate-anchors --json
|
|
236
|
+
```
|
|
237
|
+
|
|
224
238
|
## `resolve <file-path>`
|
|
225
239
|
|
|
226
240
|
Map a file path to its **owning task** — returns the WBS + task file. Strategies, in order: direct
|
|
@@ -230,6 +244,10 @@ only the exact corpus path, with no basename-WBS fallback. Returns exit `1` if n
|
|
|
230
244
|
|
|
231
245
|
## `verdict <wbs>`
|
|
232
246
|
|
|
247
|
+
There is **no `verify` verb**. The verify leg is three steps: the agent writes its structured
|
|
248
|
+
evidence write-up to `.spur/run/<wbs>-verify-answer.txt`, then `spur task verdict <wbs>` derives
|
|
249
|
+
the verdict artifact, then `spur task record <wbs>` lands it into the task.
|
|
250
|
+
|
|
233
251
|
Derive a PASS / PARTIAL / FAIL / UNKNOWN verdict from a verify-step answer text file (the agent's
|
|
234
252
|
structured evidence write-up). Replaces the pipeline's verify→record transition's previous grep/shell
|
|
235
253
|
ladder (0108; ADR-022). On a readable answer, emits `.spur/run/<wbs>-verdict.json` (mkdir-p the
|
|
@@ -323,6 +341,19 @@ A free-form prose answer (no tables, or tables missing the required headers) yie
|
|
|
323
341
|
the artifact source and directs the operator to `/sp:dev-verify <wbs>`. Re-run verify with the
|
|
324
342
|
table format above.
|
|
325
343
|
|
|
344
|
+
## `verifyall-aggregate`
|
|
345
|
+
|
|
346
|
+
Read a JSON array of `{wbs,outcome[,reason]}` rows from `--from-file` (default
|
|
347
|
+
`.spur/run/verifyall-batch-input.json`) and derive one deterministic batch verdict. Valid outcomes
|
|
348
|
+
are `PASS`, `PARTIAL`, `FAIL`, `NOT-STARTED`, and `UNKNOWN`; `NOT-STARTED` rows are reported but
|
|
349
|
+
excluded from rollup. Exit `1` when the aggregate verdict is `FAIL` or the input is invalid.
|
|
350
|
+
|
|
351
|
+
## `scaffold-tests <wbs>`
|
|
352
|
+
|
|
353
|
+
Generate BDD stubs from the task's Acceptance Criteria. `--file <path>` overrides the target test
|
|
354
|
+
file; `--folder <path>` overrides task lookup. JSON reports the target plus created, skipped,
|
|
355
|
+
drifted, and warning results.
|
|
356
|
+
|
|
326
357
|
## `refresh-roster <wbs>`
|
|
327
358
|
|
|
328
359
|
Regenerate a parent task's sub-task roster block in `## Plan` — the marker-delimited table that the
|
|
@@ -361,12 +392,15 @@ spur task sections <wbs> <init|add|list> [name] [--folder] [--json]
|
|
|
361
392
|
spur task list [--status <s>] [--phase <p>] [--parent <wbs>] [--feature <id>] [--folder] [--json]
|
|
362
393
|
spur task refresh [--folder] [--json]
|
|
363
394
|
spur task migrate [--dry-run] [--folder] [--json]
|
|
395
|
+
spur task migrate-anchors [--dry-run] [--json]
|
|
364
396
|
spur task refresh-roster <wbs> [--folder] [--json]
|
|
365
397
|
spur task batch-create --file <path> [--folder] [--json]
|
|
366
398
|
spur task record <wbs> [--verdict-file <p>] [--solution-from-diff] [--transition <s>] [--folder] [--json]
|
|
367
399
|
spur task verdict <wbs> [--from-answer <p>] [--folder] [--json]
|
|
400
|
+
spur task verifyall-aggregate [--from-file <path>] [--json]
|
|
368
401
|
spur task check [wbs] [--strict] [--as <status>] [--strict-core] [--folder] [--json]
|
|
369
402
|
spur task resolve <file-path> [--strict] [--folder] [--json]
|
|
370
403
|
spur task path <wbs> [--folder] [--json]
|
|
371
404
|
spur task run-link <wbs> [--source <src>] [--run-id <id>] [--json]
|
|
405
|
+
spur task scaffold-tests <wbs> [--file <path>] [--folder] [--json]
|
|
372
406
|
```
|
|
@@ -26,7 +26,7 @@ use it well*.
|
|
|
26
26
|
| `start <agent-id>` | Start a supervised agent process (requires `spur serve`) | `--server <url>` `--json` |
|
|
27
27
|
| `stop <agent-id>` | Stop a supervised agent process (requires `spur serve`) | `--server <url>` `--json` |
|
|
28
28
|
|
|
29
|
-
All verbs accept `--json`
|
|
29
|
+
All verbs except the text-only `assign` accept `--json` and `--json-envelope`. `--server <url>` (default:
|
|
30
30
|
`http://localhost:3000/api`) targets the supervisor API started by `spur serve`. **Exit codes:** `0`
|
|
31
31
|
success, `1` error, `2` invalid usage.
|
|
32
32
|
|
|
@@ -264,6 +264,12 @@ spur workflow list [--json]
|
|
|
264
264
|
spur workflow trace [run-id] [--workflow <name>] [--status <s>] [--since <iso>] [--last <n>] [--follow] [--poll <ms>] [--output] [--json]
|
|
265
265
|
```
|
|
266
266
|
|
|
267
|
+
### `show` - project a definition
|
|
268
|
+
|
|
269
|
+
`spur workflow show <file>` renders the declared graph as Mermaid. `--format todo` instead emits a
|
|
270
|
+
declared-step checklist; `--json` serializes the selected projection. This verb intentionally does
|
|
271
|
+
not advertise `--json-envelope` because its JSON projection is a kept-raw document surface.
|
|
272
|
+
|
|
267
273
|
| Flag (on `run`) | Effect |
|
|
268
274
|
| --------------- | ------ |
|
|
269
275
|
| `--vars <json>` | Per-run variable overrides (JSON object). Merged over the workflow's `vars`. Values must be strings. User vars win over injected defaults (`spurBin`). |
|
|
@@ -234,6 +234,11 @@ or `args="refine <wbs>"` for task refinement, or `args="refineall --feature <id>
|
|
|
234
234
|
batch refine under a feature (add `--depth ready` for implement-ready freeze). Use `spur agent run`
|
|
235
235
|
for isolated LLM invocations within pipeline steps.
|
|
236
236
|
|
|
237
|
+
**Argument parsing.** Split `$ARGUMENTS` into target + flags before dispatching. Unknown flags are
|
|
238
|
+
not silently dropped: note them in the plan line, or stop (rule imported from `next-router`,
|
|
239
|
+
task 0701 R3 — e.g. `--worktree` on `dev-refine` is undeclared, so it must be surfaced, never
|
|
240
|
+
discarded).
|
|
241
|
+
|
|
237
242
|
### Codex / OpenClaw / OpenCode / Antigravity
|
|
238
243
|
|
|
239
244
|
Run `spur` CLI via the Bash tool; parse `--json` output. Invoke this skill directly for
|
|
@@ -236,7 +236,7 @@ must not be changed without updating the backing skill.
|
|
|
236
236
|
- **Purpose:** Batch-refine a set of tasks (or all refine-eligible tasks under a feature) — resolve a set, topo-sort by dependencies, run per-task `refine`, emit a summary report. Planning-half counterpart of `verifyall` / `runall` for the just-in-time spec-completion gate. With `--depth ready`, batch **implement-ready** freeze before multi-agent implement or runall.
|
|
237
237
|
- **Inputs:**
|
|
238
238
|
- `--feature <id>` **or** `--tasks <selector>` (required — at least one). `--feature` is sugar for `--tasks feature:<id>` (shared selector grammar: explicit WBS list, `feature:<id>`, `ready`, status pseudo-list — [execution-batch.md](execution-batch.md) Step 1). If both are present, `--tasks` wins (one-line note in the report).
|
|
239
|
-
- Shared refine flags (passed through to each per-task refine): `--focus <mode>`, `--description <text>`, `--depth <standard|ready>`, `--agent <inline|auto|name>`, `--auto
|
|
239
|
+
- Shared refine flags (passed through to each per-task refine): `--focus <mode>`, `--description <text>`, `--depth <standard|ready>`, `--agent <inline|auto|name>`, `--auto`.
|
|
240
240
|
- Batch-only flags: `--keep-going` (continue independents after a failure; default halt), `--status <s>` (filter resolved membership; default **`backlog,todo`** — planning-side fill candidates), `--json` (machine-readable batch report).
|
|
241
241
|
- **Backing:** `sp:spur-dev` skill, `refineall` operation (orchestrates; per-task body is the single-task `refine` operation — never a second refine implementation).
|
|
242
242
|
- **Behavior:**
|
|
@@ -248,7 +248,7 @@ must not be changed without updating the backing skill.
|
|
|
248
248
|
6. Emit a batch report (markdown or `--json`) that records `depth` once at the header.
|
|
249
249
|
- **Per-task outcome vocabulary:** `refined` (synthesis wrote sections) | `SKIP` (already meets the active depth bar under `--auto`) | `failed` | `skipped` (dep failed under `--keep-going`) | `not-attempted` (halted) | `blocked` (unmet out-of-set dep).
|
|
250
250
|
- **Batch verdict:** `clean` (all attempted tasks `refined` or `SKIP`) | `halted` (a failure stopped the batch) | `aborted` (cycle / unknown selector / empty set after filter).
|
|
251
|
-
- **`--next`
|
|
251
|
+
- **`--next` is not accepted** (dropped by feature H8, 2026-07-31 — see `plugins/sp/commands/dev-refineall.md` for the removal record). Chain execution explicitly: refineall, then `/sp:dev-runall --feature <id>`.
|
|
252
252
|
- **`--auto` recommendation:** Batch refine without `--auto` requires per-task interactive Q&A and does not scale. Default operator path: `/sp:dev-refineall --feature <id> --auto`. For implement handoffs: `/sp:dev-refineall --feature <id> --auto --depth ready`.
|
|
253
253
|
- **Delegation:** `Skill(skill="sp:spur-dev", args="refineall $ARGUMENTS")` → per task `Skill(skill="sp:spur-dev", args="refine <wbs> $SHARED_FLAGS")` (shared flags include `--depth` when set).
|
|
254
254
|
|
|
@@ -61,6 +61,7 @@ function normalizeArgs(raw: Args): Args {
|
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
**Normalization rules (performed by the command layer before the skill sees $ARGUMENTS, or by the batch resolver):**
|
|
64
|
+
|
|
64
65
|
- If `--feature FOO` is present and `--tasks` is absent, treat the effective selector as `feature:FOO`.
|
|
65
66
|
- If both are present, `--tasks` wins (with a one-line note in the batch report).
|
|
66
67
|
|
|
@@ -101,7 +102,7 @@ bun run apps/cli/src/index.ts feature check <id> --strict --json
|
|
|
101
102
|
kickoff** — the driver never re-queries `spur task list` to recompute membership mid-batch (R2.1).
|
|
102
103
|
|
|
103
104
|
| Selector form | Regex / match | Resolution |
|
|
104
|
-
|
|
105
|
+
| --- | --- | --- |
|
|
105
106
|
| Explicit WBS list | `^[0-9, ]+$` | Split on comma; validate each token is a 4-digit WBS; collect the explicit set. (R1.1) |
|
|
106
107
|
| `feature:<id>` (via `--tasks` or `--feature <id>`) | literal `feature:` prefix or `--feature` flag | `spur task list --feature <id> --json`; collect `wbs` from each row. The `--feature` flag is sugar that becomes `--tasks feature:<id>` at the command layer. (R1.3) |
|
|
107
108
|
| `ready` | literal `ready` | Resolve the union of `spur task list --status todo --json` + `spur task list --status backlog --json`, drop tasks with open children (R1.5, umbrella-parent exclusion below), then keep only tasks whose every `dependencies[]` entry resolves to `status == done` (via `spur task show <dep> --json | jq '{wbs, status, dependencies, feature_id}'` — R5 metadata-only). Report each excluded task with its unmet dependency. (R1.4) |
|
|
@@ -185,7 +186,7 @@ node "$(superskill script path sp batch-preflight.mjs)" \
|
|
|
185
186
|
```
|
|
186
187
|
|
|
187
188
|
| Result | Batch action |
|
|
188
|
-
|
|
189
|
+
| -------- | ---------------- |
|
|
189
190
|
| `action: run` | Launch `task-pipeline.yaml` for this WBS (happy path **unchanged**) |
|
|
190
191
|
| `action: skip` code **A2** | Do not launch; report `preflight-skip` + unmet deps (mirrors TABLE A2) |
|
|
191
192
|
| `action: skip` code **A7** | Do not launch; report blocked (handover is operator-side) |
|
|
@@ -253,7 +254,7 @@ Only two flags cross the orchestrator→pipeline boundary; both are merged into
|
|
|
253
254
|
`--vars` JSON:
|
|
254
255
|
|
|
255
256
|
| Flag | Effect on per-task `--vars` |
|
|
256
|
-
|
|
257
|
+
| --- | --- |
|
|
257
258
|
| `--auto` | sets `"profile":"auto"` (skips the HITL approve gate). Omitting it forwards nothing, so the pipeline uses its default profile (standard — HITL pause surfaces to the operator). (R4.2) |
|
|
258
259
|
| `--agent <value>` | omit/`inline` in interactive sequential mode selects the host driver and is not forwarded. `auto` or a name sets **both** `"agent":"<value>"` and `"implementAgent":"<value>"` so every workflow `agent.run` step — including implement — spawns that executor. Headless omit/inline falls through the executor precedence chain. To pin ONLY implement, pass `--vars '{"implementAgent":"..."}'` separately; that explicit var selects the subprocess path. (R4.3, tasks 0483/0503) |
|
|
259
260
|
|
|
@@ -283,7 +284,7 @@ node "$(superskill script path sp batch-preflight.mjs)" --wbs <wbs> --status <st
|
|
|
283
284
|
```
|
|
284
285
|
|
|
285
286
|
| Rule | Detail |
|
|
286
|
-
|
|
287
|
+
| ------ | -------- |
|
|
287
288
|
| Budget | **≤ 1** recovery consult per WBS per batch — never loop until done |
|
|
288
289
|
| Default | Print the exact child command in the batch report |
|
|
289
290
|
| `--auto` batch | May dispatch the child **once** when cardinality is 1 and the hop is a single lifecycle command |
|
|
@@ -295,7 +296,6 @@ in next-router; this only maps status → primary TABLE A hop for recovery.
|
|
|
295
296
|
|
|
296
297
|
### 3.3c Bounded feature-sync retry suppression (task 0411)
|
|
297
298
|
|
|
298
|
-
|
|
299
299
|
During a batch, the per-task `record` step and the wrap-up `feature-transition` step each invoke
|
|
300
300
|
feature status sync. When a feature is L4-gate-blocked (e.g. not all linked tasks are `done`), the
|
|
301
301
|
identical blocked proposal repeats on every call with no intervening input change — in the H9
|
|
@@ -413,6 +413,14 @@ The per-task outcome vocabulary: `done` | `failed` | `blocked` | `skipped` | `no
|
|
|
413
413
|
The batch verdict: `clean` (all attempted tasks `done`) | `halted` (a failure stopped the batch) |
|
|
414
414
|
`aborted` (cycle or selector error before any run).
|
|
415
415
|
|
|
416
|
+
**Zero-task rule (task 0701 R7b).** A selector that resolves to an **empty set after the status
|
|
417
|
+
filter** is an `aborted` verdict (`aborted (empty set after filter)`), matching dev-operations.md
|
|
418
|
+
§5a. Under `--worktree`, **WT-2 is skipped entirely**: no worktree is cut and no WT-3 marker is
|
|
419
|
+
written for a batch with nothing to run. The early-exit report carries zero per-task rows,
|
|
420
|
+
`Steps: 0 derived, 0 executed`, and the `aborted` verdict; no WT-3b commit step and no WT-4/WT-5
|
|
421
|
+
terminal action runs. A contract test pins this
|
|
422
|
+
(`plugins/sp/tests/dogfood-testing/execution-batch-contract.test.ts`).
|
|
423
|
+
|
|
416
424
|
## Worktree isolation (`--worktree [<name>]`)
|
|
417
425
|
|
|
418
426
|
When a batch command (`dev-runall`, `dev-refineall`, `dev-verifyall`) is invoked with
|
|
@@ -487,7 +495,12 @@ Create one worktree on a new branch cut from the current HEAD's ref (the **base
|
|
|
487
495
|
BASE_REF=$(git rev-parse --abbrev-ref HEAD)
|
|
488
496
|
BASE_SHA=$(git rev-parse HEAD)
|
|
489
497
|
BRANCH="sp/<command>-<selector-slug>-<short-id>" # e.g. sp/runall-h1-a3f2
|
|
490
|
-
git worktree add
|
|
498
|
+
# `git worktree add -b` creates the branch BEFORE the directory, so a failed create leaves a
|
|
499
|
+
# dangling branch and the natural retry dies on "a branch named ... already exists"
|
|
500
|
+
# (task 0701 R2b). Wrap the create: on failure, delete the branch — or derive a fresh
|
|
501
|
+
# short-id per attempt — before surfacing the error.
|
|
502
|
+
git worktree add "../<repo>-<command>-<selector-slug>-<short-id>" -b "$BRANCH" "$BASE_REF" \
|
|
503
|
+
|| { git branch -D "$BRANCH"; false; }
|
|
491
504
|
```
|
|
492
505
|
|
|
493
506
|
Branch and directory names are derived (command + selector slug + short id); the create path never
|
|
@@ -497,10 +510,15 @@ not resolve is an error, not a create).
|
|
|
497
510
|
A fresh worktree has no `node_modules` (gitignored), so the first `bun test` or
|
|
498
511
|
typecheck fails on the first workspace import. Install before any task work:
|
|
499
512
|
|
|
500
|
-
cd "../<worktree-dir>" && bun install --frozen-lockfile
|
|
513
|
+
cd "../<worktree-dir>" && bun install --frozen-lockfile --ignore-scripts
|
|
501
514
|
|
|
502
515
|
`--frozen-lockfile` pins the worktree to `bun.lock` rather than re-resolving,
|
|
503
|
-
so the worktree's dependency tree matches the base ref's.
|
|
516
|
+
so the worktree's dependency tree matches the base ref's. `--ignore-scripts` is required, not
|
|
517
|
+
stylistic (task 0701 R2a): worktrees share the main tree's `.git`, and this repo's `prepare`
|
|
518
|
+
script is `lefthook install` (`package.json`) — a bare install rewrites the operator's
|
|
519
|
+
main-repo hooks from inside the "isolated" tree. Scripts are skipped only at this call site;
|
|
520
|
+
a normal clone keeps `prepare`. The worktree still gets a usable dependency tree — the install
|
|
521
|
+
exists so the first `bun test` resolves workspace imports.
|
|
504
522
|
|
|
505
523
|
#### Reuse mode (`--worktree <name>`)
|
|
506
524
|
|
|
@@ -510,8 +528,8 @@ invoking tree's current HEAD ref (not the worktree's branch) and `BASE_SHA` is
|
|
|
510
528
|
`git merge-base <BASE_REF> <BRANCH>` — so WT-4's FF-merge lands the worktree's accumulated commits
|
|
511
529
|
onto the invoking tree's base ref, exactly as create mode does.
|
|
512
530
|
|
|
513
|
-
`bun install --frozen-lockfile` runs **only when `node_modules` is absent** in
|
|
514
|
-
worktree. A warm reused tree does not re-pay the install; a cold one (hand-made, or a retained tree
|
|
531
|
+
`bun install --frozen-lockfile --ignore-scripts` runs **only when `node_modules` is absent** in
|
|
532
|
+
the resolved worktree (same `--ignore-scripts` rationale as create mode — task 0701 R2a). A warm reused tree does not re-pay the install; a cold one (hand-made, or a retained tree
|
|
515
533
|
whose deps were removed) installs exactly once before the first task. This is the R3 conditional
|
|
516
534
|
install rule (source: task 0481) — create mode always installs because a fresh tree is always cold.
|
|
517
535
|
|
|
@@ -570,7 +588,9 @@ verb) whose path argument you control.
|
|
|
570
588
|
|
|
571
589
|
Worktree identity lives on disk under `.spur/run/`, not only in the orchestrator's memory, so a
|
|
572
590
|
session that dies mid-batch is recoverable. Write the marker at creation and update it at the
|
|
573
|
-
terminal transition (merged / retained).
|
|
591
|
+
terminal transition (merged / retained). The marker is written to the **invoking** tree's
|
|
592
|
+
`.spur/run/` (task 0701 R2c) — the tree where the driver process started, not the worktree's own
|
|
593
|
+
`.spur/run/` — so WT-6's resume scan finds it regardless of where the operator stands. Schema:
|
|
574
594
|
|
|
575
595
|
```json
|
|
576
596
|
{
|
|
@@ -617,6 +637,22 @@ Reuse mode resolves the marker by the resolved worktree's `path` (not by `comman
|
|
|
617
637
|
(AGENTS.md one-writer-per-tree; task 0487 R5). Overridable with `--force` (the operator can tell
|
|
618
638
|
a crashed-session marker from a live-session one; the harness cannot).
|
|
619
639
|
|
|
640
|
+
### WT-3b — Commit the batch's writes on `$BRANCH` (task 0701 R1)
|
|
641
|
+
|
|
642
|
+
Before any terminal action, commit the batch's corpus writes **on `$BRANCH`, inside the
|
|
643
|
+
worktree** — including the generated task files under `docs/tasks*/` and the kanban index:
|
|
644
|
+
|
|
645
|
+
```bash
|
|
646
|
+
cd "../<worktree-dir>"
|
|
647
|
+
git add <files-the-batch-wrote>
|
|
648
|
+
git commit -m "<type>(<scope>): <command> <selector> batch writes"
|
|
649
|
+
cd - >/dev/null
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
The FF-only git merge carries only commits — uncommitted writes in the worktree would be left
|
|
653
|
+
behind by the merge and then destroyed by create mode's `git worktree remove`. WT-3b exists so
|
|
654
|
+
that can never happen.
|
|
655
|
+
|
|
620
656
|
### WT-4 — Success path (R4)
|
|
621
657
|
|
|
622
658
|
When the batch completes with **no failed task**, fast-forward-merge the worktree branch onto the
|
|
@@ -629,6 +665,11 @@ only what it created*):
|
|
|
629
665
|
# Run these from the main tree (not inside the worktree) - you merge the worktree branch
|
|
630
666
|
# back onto the base ref there:
|
|
631
667
|
git checkout "$BASE_REF"
|
|
668
|
+
# Guard (task 0701 R1): a zero-commit branch makes the FF-only git merge exit 0
|
|
669
|
+
# ("Already up to date") while merging nothing — the two lines below would then delete
|
|
670
|
+
# the worktree holding the only copy of the batch's writes. Refuse instead:
|
|
671
|
+
[ "$(git rev-list --count "$BASE_SHA..$BRANCH")" -gt 0 ] \
|
|
672
|
+
|| { echo "halt: branch carries no commits - nothing to merge" >&2; false; } # -> WT-5
|
|
632
673
|
git merge --ff-only "$BRANCH" # FF-only: never rebase, merge-commit, or resolve conflicts
|
|
633
674
|
# if FF succeeded:
|
|
634
675
|
git worktree remove "../<worktree-dir>"
|
|
@@ -636,6 +677,9 @@ git branch -d "$BRANCH"
|
|
|
636
677
|
# update marker: status = "merged"
|
|
637
678
|
```
|
|
638
679
|
|
|
680
|
+
On the zero-commit guard firing, fall through to **WT-5** with the halt cause *"branch carries no
|
|
681
|
+
commits — nothing to merge"*: the worktree and branch are retained, never removed (task 0701 R1).
|
|
682
|
+
|
|
639
683
|
#### Reuse mode — merge, retain
|
|
640
684
|
|
|
641
685
|
The FF-merge runs identically (same `git checkout "$BASE_REF" && git merge --ff-only "$BRANCH"`),
|
|
@@ -667,12 +711,23 @@ risk losing work); WT-5 retains the worktree and branch whenever FF is impossibl
|
|
|
667
711
|
Reuse mode is **narrower** than the carve-out (it merges but does not delete the branch), so the
|
|
668
712
|
carve-out text needs no widening.
|
|
669
713
|
|
|
714
|
+
**Lifecycle-DB disposition (task 0701 R2d).** The worktree has its own `.spur` lifecycle DB, and
|
|
715
|
+
WT-4/WT-5 remove or retain that tree — the DB state does **not** travel with the merge. The
|
|
716
|
+
**committed task file is authoritative**: after a green merge the branch's task files read
|
|
717
|
+
`done`/`testing` while the invoking tree's DB still reports the pre-batch statuses. Re-sync
|
|
718
|
+
explicitly by replaying the recorded terminal transitions in the invoking tree (`spur task update
|
|
719
|
+
<wbs> <status>` per task, then `spur task record <wbs>`), or treat the batch report's per-task
|
|
720
|
+
table as the source of truth. This is a deliberate choice over auto-migrating DB state: the DB is
|
|
721
|
+
per-tree by design and the committed corpus files are the durable record.
|
|
722
|
+
|
|
670
723
|
### WT-5 — Failure path: retain and report (R5)
|
|
671
724
|
|
|
672
725
|
On any per-task failure, batch halt, HITL pause that ends the run, or non-FF merge from WT-4, the
|
|
673
726
|
worktree directory and branch are left **intact**. No destructive automation on this path under any
|
|
674
727
|
flag combination (`--auto`, `--force`, `--keep-going` — all leave the worktree in place). Update the
|
|
675
|
-
marker: `status = "retained"`.
|
|
728
|
+
marker: `status = "retained"`. The worktree's own `.spur` lifecycle DB is retained with the tree,
|
|
729
|
+
so nothing is lost on this path (see the WT-4 lifecycle-DB disposition for the merged case —
|
|
730
|
+
task 0701 R2d). Emit a retention report in the existing halt-report shape:
|
|
676
731
|
|
|
677
732
|
```
|
|
678
733
|
## Worktree retained — <command> <selector>
|
|
@@ -706,7 +761,8 @@ its WT-3 marker rather than creating a second one. Marker lookup tries two paths
|
|
|
706
761
|
This path covers the common resume shapes: the operator remembers the name used last time, or
|
|
707
762
|
passes the path (tier-1 match).
|
|
708
763
|
2. **Command+selector fallback (bare `--worktree` or absent flag)** — scan
|
|
709
|
-
`.spur/run/worktree-*.json`
|
|
764
|
+
`.spur/run/worktree-*.json` **in the invoking tree** (where WT-3 wrote the marker —
|
|
765
|
+
task 0701 R2c) for a marker whose `command` + `selector` match the current
|
|
710
766
|
invocation and whose `status` is `active` or `retained`. Create-mode runs that did not name their
|
|
711
767
|
tree resolve here.
|
|
712
768
|
3. **Found by either path** → `cd` into the marker's `path`, skip WT-1/WT-2 (no new worktree), and
|
|
@@ -769,7 +825,7 @@ time. Before launching a full `spur-check-new`:
|
|
|
769
825
|
## AC traceability
|
|
770
826
|
|
|
771
827
|
| AC | Where satisfied |
|
|
772
|
-
|
|
828
|
+
| --- | --- |
|
|
773
829
|
| R1.1–R1.4 (selector grammar) | Step 1 — selector resolution table |
|
|
774
830
|
| R1.5 (umbrella-parent exclusion) | Step 1 — "Umbrella-parent exclusion" paragraph |
|
|
775
831
|
| R2.1 (freeze at kickoff) | Step 2.1 |
|
|
@@ -793,6 +849,7 @@ time. Before launching a full `spur-check-new`:
|
|
|
793
849
|
When a batch contains tasks with **zero dependency edges between them** and **no file-overlap conflicts**, the orchestrator can fan them out in parallel instead of running them sequentially. This is an **orchestrator-level optimization** — the per-task pipeline (`task-pipeline.yaml`) is unchanged; only the execution order differs.
|
|
794
850
|
|
|
795
851
|
**Decision framework:** `sp:parallel-execution` owns the full fan-out decision logic and patterns. Consult its [fan-out-patterns.md](../../parallel-execution/references/fan-out-patterns.md) before parallelizing. The orchestrator's responsibility is:
|
|
852
|
+
|
|
796
853
|
1. Identify the independent subset from the topo-sorted batch (tasks with no edges to each other).
|
|
797
854
|
2. Check for file-overlap conflicts (two tasks touching the same `file:line` range must serialize).
|
|
798
855
|
3. Verify token budget supports N-way fan-out.
|
|
@@ -803,7 +860,6 @@ When a batch contains tasks with **zero dependency edges between them** and **no
|
|
|
803
860
|
|
|
804
861
|
**See also:** `sp:parallel-execution` skill, `sp:super-planner` agent (parallel mode), `/sp:dev-parallel` command.
|
|
805
862
|
|
|
806
|
-
|
|
807
863
|
## Subagent execution disciplines
|
|
808
864
|
|
|
809
865
|
Parallel fan-out and any subagent dispatch obey the four disciplines owned by
|