@gobing-ai/spur 0.4.10 → 0.4.12

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.
Files changed (60) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/config.example.yaml +22 -0
  3. package/config/rules/quality/coverage-gate.yaml +2 -2
  4. package/config/rules/surface/check-cli-surface.yaml +4 -0
  5. package/config/workflows/feature-verification.yaml +14 -5
  6. package/config/workflows/idea-pipeline.yaml +92 -12
  7. package/config/workflows/pr-review.yaml +72 -224
  8. package/config/workflows/task-pipeline.yaml +456 -830
  9. package/config/workflows/wayfinder-resolution.yaml +20 -3
  10. package/config/workflows/wrapup-pipeline.yaml +22 -9
  11. package/package.json +1 -1
  12. package/plugins/sp/README.md +2 -2
  13. package/plugins/sp/agents/super-planner.md +2 -2
  14. package/plugins/sp/agents/super-reviewer.md +17 -8
  15. package/plugins/sp/bunfig.toml +3 -3
  16. package/plugins/sp/commands/dev-changelog.md +1 -1
  17. package/plugins/sp/commands/dev-gitmsg.md +2 -2
  18. package/plugins/sp/commands/dev-handover.md +1 -1
  19. package/plugins/sp/commands/dev-idea.md +23 -13
  20. package/plugins/sp/commands/dev-job-dump.md +1 -1
  21. package/plugins/sp/commands/dev-plan.md +17 -6
  22. package/plugins/sp/commands/dev-pr-review.md +27 -17
  23. package/plugins/sp/commands/dev-review.md +1 -1
  24. package/plugins/sp/commands/dev-run.md +7 -6
  25. package/plugins/sp/commands/dev-runall.md +1 -1
  26. package/plugins/sp/plugin.json +1 -1
  27. package/plugins/sp/skills/code-implementation/SKILL.md +7 -7
  28. package/plugins/sp/skills/code-refactoring/SKILL.md +1 -1
  29. package/plugins/sp/skills/code-verification/SKILL.md +1 -1
  30. package/plugins/sp/skills/code-verification/references/secu-review.md +8 -4
  31. package/plugins/sp/skills/code-verification/references/verdict-schema.md +23 -10
  32. package/plugins/sp/skills/doc-evolve/SKILL.md +1 -1
  33. package/plugins/sp/skills/next-router/references/routing-table.md +1 -1
  34. package/plugins/sp/skills/parallel-execution/references/dispatch-surface.md +7 -7
  35. package/plugins/sp/skills/pr-reviewing/SKILL.md +157 -210
  36. package/plugins/sp/skills/source-driven-development/SKILL.md +14 -6
  37. package/plugins/sp/skills/spec-decomposition/references/decomposition.md +4 -4
  38. package/plugins/sp/skills/spur-check/SKILL.md +9 -8
  39. package/plugins/sp/skills/spur-cli/SKILL.md +3 -3
  40. package/plugins/sp/skills/spur-cli/references/builder.md +86 -13
  41. package/plugins/sp/skills/spur-cli/references/self.md +37 -2
  42. package/plugins/sp/skills/spur-cli/references/tasks/l3-guard-cheatsheet.md +1 -1
  43. package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +3 -3
  44. package/plugins/sp/skills/spur-cli/references/tasks.md +1 -1
  45. package/plugins/sp/skills/spur-cli/references/workflows.md +5 -3
  46. package/plugins/sp/skills/spur-dev/SKILL.md +3 -3
  47. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +9 -8
  48. package/plugins/sp/skills/spur-dev/references/dev-operations.md +11 -11
  49. package/plugins/sp/skills/spur-dev/references/done-housekeeping.md +4 -3
  50. package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +4 -4
  51. package/plugins/sp/skills/spur-dev/references/execution-batch.md +13 -11
  52. package/plugins/sp/skills/spur-dev/references/execution-workflow.md +31 -22
  53. package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +5 -5
  54. package/plugins/sp/skills/spur-dev/references/execution-worktree-setup.md +4 -4
  55. package/plugins/sp/skills/spur-dev/references/flag-glossary.md +3 -2
  56. package/plugins/sp/skills/spur-dev/references/gate-checklists.md +4 -4
  57. package/schemas/spur-config.schema.json +19 -1
  58. package/spur.js +32388 -31906
  59. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +0 -997
  60. package/plugins/sp/skills/spur-dev/references/structured-trace-emission.md +0 -178
@@ -159,13 +159,13 @@ must not be changed without updating the backing skill.
159
159
  - **Inputs:** `<wbs>` (required). `--mode <full|implement>` selects the execution mode. `implement` invokes `sp:code-implementation` inline by default. Interactive `full` resolves the selector uniformly (task 0687): omit ≡ explicit `--agent inline`; full mode reads `task-pipeline.yaml` and drives its actions/guards in the host session — host-controlled and non-subprocess, with 0508 eligibility applying to the resolved-inline selector (eligible `agent.run` stages dispatch once to a native subagent, host fallback)). `--agent auto`, a name, or headless invocation launches the workflow subprocess. `--agent <inline|auto|name>` selects the execution surface (see [SSOT](cross-cutting.md#inline-default-execution-surface)). `--auto` skips the HITL approve gate / confirmations and propagates down the `--next` chain. `--next` controls chaining only and never changes the mode; a pipeline implement stage must invoke `/sp:dev-run <wbs> --mode implement`. On implement success with `--next`, transition `todo → wip → testing` through the FSM (guards honored — no `--no-lifecycle`) + chain to `/sp:dev-verify <wbs> --auto --next`. On a guard failure, stop as review-pending. **Partial-deliverable rule:** if the task ships only part of its requirements (e.g. an R1/R2 split with the rest in a follow-up task), the `## Solution` section must state that explicitly and the verify verdict will record the scope. `--worktree [<name>]` runs the full pipeline inside an isolated git worktree (create or reuse; FF-merge on success, retain on failure) — the batch lifecycle in [execution-batch.md § Worktree isolation](execution-batch.md#worktree-isolation---worktree-name) applied to a batch of one; rejected with `--mode implement`. `--no-summary` skips the [execution summary](#execution-summary) printed after a full-mode run. `--wrap` hands off to `/sp:dev-wrap <wbs>` after the main step; the `--agent` selector is preserved into that handoff when supplied (omission remains omission), and the wrap hop reports its own trigger-3 subprocess override per the wrap contract.
160
160
  - **Backing:** `sp:spur-dev` skill — `run` operation for the full pipeline (the spine drives it); `sp:code-implementation` competency skill for the implement step (the spine dispatches to it).
161
161
  - **Modes:**
162
- - **`full`** (default): Drive the full pipeline — precheck → implement → test → review → approve(HITL) → verify → record → done. Interactive omit/inline uses [inline-pipeline-driver.md](inline-pipeline-driver.md) (host-controlled; eligible stages may use a native subagent); explicit/headless executor selection invokes `spur workflow run task-pipeline.yaml --vars '{"wbs":"<wbs>"}'` (with `profile: auto` when `--auto`). Both monitor/surface HITL and preserve the YAML gates. `--next` never changes this mode.
162
+ - **`full`** (default): Drive the full pipeline — precheck → implement → test → review → approve(HITL) → verify → record → done. Interactive omit/inline runs `spur workflow run task-pipeline.yaml --vars '{"wbs":"<wbs>","executor":"host"}'` and performs each returned work item (eligible stages may use a native subagent); explicit/headless executor selection runs the same definition without the `executor` var (with `profile: auto` when `--auto`). Both monitor/surface HITL and preserve the YAML gates. `--next` never changes this mode.
163
163
  - **`implement`** (explicit `--mode implement` only): Execute only the implement step. Read the task's `## Requirements` / `## Design` / `## Plan`, write the code that satisfies them, author the `## Solution` change-map section (file:line + what/why per changed file) via `spur task update <wbs> --section Solution --from-file`. This is the implement step the pipeline calls — it is NOT the pipeline driver. With `--next`: on success, transition `todo → wip → testing` through the FSM (guards honored — no `--no-lifecycle`) + chain to `/sp:dev-verify <wbs> --auto --next`; on a guard failure, stop as review-pending. **Partial-deliverable rule:** if the task ships only part of its requirements (e.g. an R1/R2 split with the rest in a follow-up task), the `## Solution` and `## Review` sections MUST carry a `⚠️ PARTIAL` marker naming the deferred part and the follow-up WBS — see `plugins/sp/commands/dev-run.md` → "Section ownership".
164
- - **Delegation:** `Skill(skill="sp:spur-dev", args="run-inline $ARGUMENTS")` for interactive full omit/inline (resolved-inline semantics, task 0687); `Skill(skill="sp:spur-dev", args="run $ARGUMENTS")` for explicit/headless full mode; `Skill(skill="sp:code-implementation", args="$ARGUMENTS")` for implement mode.
164
+ - **Delegation:** `Skill(skill="sp:spur-dev", args="run $ARGUMENTS")` for both full-mode paths (the `executor` var picks host session vs child process); `Skill(skill="sp:code-implementation", args="$ARGUMENTS")` for implement mode.
165
165
 
166
166
  #### Execution summary
167
167
 
168
- Produced by the run's own close (1146 R1), not by a trailing step: `spur workflow inline close` writes
168
+ Produced by the run's own terminal close (1146 R1), not by a trailing step: the close writes
169
169
  `.spur/run/<runId>-summary.md` + `.json` and reports `summaryFile` in its stdout JSON. The driver's only
170
170
  job is to print that file's contents under an `### Execution summary` heading, and to pass `--no-summary`
171
171
  through to `inline close`. `--no-summary` is an `inline close` flag: anywhere else it is a usage error.
@@ -551,7 +551,7 @@ gate commands, pending questions and approval requirements.>
551
551
  Ties lean design — when the signal is ambiguous, `system-design` runs. Task Design defaults on
552
552
  unless `--skip-design`. Plan path uses the same package contract (no `--design` force flag).
553
553
 
554
- - **Operator gates:** every pause is one `AskUserQuestion` [decision brief](decision-brief.md) — recommended option first; see the gate table in `commands/dev-idea.md` and the `hitl.confirm` rule in [inline-pipeline-driver.md](inline-pipeline-driver.md).
554
+ - **Operator gates:** every pause is one `AskUserQuestion` [decision brief](decision-brief.md) — recommended option first; see the gate table in `commands/dev-idea.md` and the `hitl.confirm` rule in [execution-workflow.md](execution-workflow.md).
555
555
 
556
556
  ### 17. refactor
557
557
 
@@ -568,7 +568,7 @@ gate commands, pending questions and approval requirements.>
568
568
  These define their procedure directly in the command file. No `Skill()` delegation — the command
569
569
  is the procedure. The backing is a combination of git CLI, `spur` CLI, and agent reasoning.
570
570
 
571
- **Gate ownership.** A project quality gate (`bun run gate`, `spur-check`, lint, typecheck, tests,
571
+ **Gate ownership.** A project quality gate (`spur self exec autofix|check|lint`, typecheck, tests,
572
572
  rule runs) runs only where an operation's own contract names it: the pipeline `test` hop
573
573
  (`run`/`runall`), `fixall`, `gtd`, `fixgha`, `unit`, and the `--check <cmd>` loops. The scribe
574
574
  operations — `changelog`, `gitmsg`, `handover`, `job-dump` — produce text or a commit, not code, so
@@ -595,7 +595,7 @@ host instructions does not add one. An opt-in gate is explicit: `gitmsg --check
595
595
  - **Defect precision decides what must be fixed.** The check is precise once a bullet is classified as a claim: an annotation missing its anchor is unambiguously wrong and blocks emitting. A summary broader than its evidence is reworded to what the anchor shows. Which bullets *are* claims is the heuristic half — resolve it by this step's rule, and for a genuinely borderline bullet say so in the bullet's body rather than annotating or skipping it silently.
596
596
  8. Self-check before emitting: every short hash in `<since>..<until>` appears exactly once, and every claim bullet carries exactly one `<!-- src: … -->` comment holding exactly one anchor — no unannotated claim bullets, no duplicate annotations. Fix any miss or duplicate; do not report a verdict.
597
597
  9. Reply in at most three lines: the range, bullet counts per category plus the verified-claim count, and the output target.
598
- - **Invariants:** Bullets are derived from `git log` only; the claim-verification pass (step 7) adds evidence from commit diffs, the working tree, and official docs. No quality gates — no `bun run gate`/lint/typecheck/test/`spur-check`/rule runs, and no edits beyond the one inserted section (its verification comments included). The file is left uncommitted.
598
+ - **Invariants:** Bullets are derived from `git log` only; the claim-verification pass (step 7) adds evidence from commit diffs, the working tree, and official docs. No quality gates — no `spur self exec autofix|check|lint` and no typecheck, test or rule run, and no edits beyond the one inserted section (its verification comments included). The file is left uncommitted.
599
599
 
600
600
  ### 9. gitmsg
601
601
 
@@ -678,7 +678,7 @@ host instructions does not add one. An opt-in gate is explicit: `gitmsg --check
678
678
 
679
679
  Hooks stay enabled; a failing hook stops the run with its output — never `--no-verify`.
680
680
 
681
- - **Invariants:** Git and POSIX shell only — no helper script, no temp file. At most two Bash calls: gather, then (only when committing) commit — three with `--check`. No quality gate (`bun run gate`, lint, typecheck, tests, `spur-check`) unless `--check` names one. With neither `--commit` nor `--squash`, never runs `git commit` — message only. Neither flag commits across a secret hit, and `--commit` never splits across a partial staging. Without `--all` the change set is the index, so plain `/sp:dev-gitmsg` keeps pre-commit semantics; only `--all` reaches unstaged and untracked files, and only a committing run stages them. The empty-index widen is read-only and always announced — a committing run never widens its own scope. An explicit `--scope <path>` always bounds the change set, with or without `--all`. One bounded, context-free capture per run — never a second, wider read.
681
+ - **Invariants:** Git and POSIX shell only — no helper script, no temp file. At most two Bash calls: gather, then (only when committing) commit — three with `--check`. No quality gate (`spur self exec autofix|check|lint`, typecheck, tests) unless `--check` names one. With neither `--commit` nor `--squash`, never runs `git commit` — message only. Neither flag commits across a secret hit, and `--commit` never splits across a partial staging. Without `--all` the change set is the index, so plain `/sp:dev-gitmsg` keeps pre-commit semantics; only `--all` reaches unstaged and untracked files, and only a committing run stages them. The empty-index widen is read-only and always announced — a committing run never widens its own scope. An explicit `--scope <path>` always bounds the change set, with or without `--all`. One bounded, context-free capture per run — never a second, wider read.
682
682
 
683
683
  ### 10. fixall
684
684
 
@@ -686,16 +686,16 @@ host instructions does not add one. An opt-in gate is explicit: `gitmsg --check
686
686
  - **Inputs:** `--scope <path>` (default: entire working tree) — limits fixes to a file or directory. `--gate-log <path>` (R3, task 0482) — before fixing, read this captured validation-run log and start at the `file:line` anchors its findings name, rather than re-deriving the failure from a fresh gate run. `--findings <anchors>` (R3, task 0482) — space-separated `file:line` anchors already extracted from that log by the `test` hop; when present these ARE the failing set, so fix them in order and do not run the gate to discover what broke.
687
687
  - **Backing:** `inline` — lint + test fix loop.
688
688
  - **Behavior:** 0. **Start at the anchors, never at a discovery run.** If `--findings <anchors>` is given, that space-separated `file:line` list IS the failing set — open those locations first, in order, and fix them; do not run the gate to find out what broke. If only `--gate-log <path>` is given, read that log first and identify the failing findings (lint/test errors with their `file:line` anchors) before running the loop — the captured log is the authoritative source. Either way the first action of this hop is a read, not a gate run (R3, task 0482).
689
- 1. Inspect the project's formatter command, then settle formatter-only diffs. Without `--scope`, use the project formatter (here `bun run format`). With `--scope`, use a formatter invocation whose only path operands are inside the selected scope — for Biome, `bunx biome check --write "$SCOPE"`. Never append a path to a script already containing `.`: `biome check . --write <path>` still writes across the repository. Preserve quoted paths and apply the same scope to every later formatting pass. If no scoped formatter is available, report that limitation and leave out-of-scope files untouched. Here `bun run lint` checks formatting without rewriting it and also runs typecheck; formatting errors fail that check.
690
- 2. Run the project's read-only lint/type check (here `bun run lint`). Collect all errors. A repository-wide check may report outside-scope errors; report them without widening the authorized fix scope.
689
+ 1. Inspect the project's formatter command, then settle formatter-only diffs. Without `--scope`, use the project formatter (here `spur self exec format`). With `--scope`, use a formatter invocation whose only path operands are inside the selected scope — for Biome, `bunx biome check --write "$SCOPE"`. Never append a path to a script already containing `.`: `biome check . --write <path>` still writes across the repository. Preserve quoted paths and apply the same scope to every later formatting pass. If no scoped formatter is available, report that limitation and leave out-of-scope files untouched. Here `spur self exec lint` checks formatting without rewriting it and also runs typecheck; formatting errors fail that check.
690
+ 2. Run the project's read-only lint/type check (here `spur self exec lint`). Collect all errors. A repository-wide check may report outside-scope errors; report them without widening the authorized fix scope.
691
691
  3. **Surface-change sweep (before any fix).** If the failing set is a **surface/parity** change — CLI noun/verb/flag surface, reference-file structure, or a renamed/moved symbol — do one upfront `rg` sweep across ALL test dirs + docs for stale references to the changed symbols (e.g. `rg -l "<old-noun>|<old-file>.md|Tier C" apps/*/tests plugins/sp/tests plugins/sp/skills docs`), and fix every in-scope pinned test/docs file in that sweep; report outside-scope matches without editing them. A surface change is pinned in **multiple independent test files** (each with its own hardcoded noun→reference map); discovering them one-by-one via full-suite probes is the dominant waste — each wave costs a suite run and a fresh edit loop (task 0436 R2 lesson). One sweep produces the complete edit set upfront.
692
692
  4. If lint is clean, skip to step 6.
693
693
  5. **Lint fix loop:** for each in-scope error, diagnose the root cause and apply the smallest fix. **Use targeted probes to verify each fix** (`bunx tsc --noEmit` on the affected package, `bunx biome check <file>`) — do NOT re-run the full gate after every batch. Loop until lint is green, or report outside-scope blockers without editing them.
694
694
  6. For standalone execution, run `bun run test` and collect all failures. With `--gate-log`, use its failing set and targeted tests instead of running the full suite here.
695
695
  7. If tests are green, proceed to the applicable standalone confirmation or pipeline handoff below.
696
696
  8. **Test fix loop:** for each in-scope failure, diagnose (test bug vs implementation bug), apply the fix, re-run the **failing test only** (`bun test <file> --test-name-pattern "<test>"`). Report outside-scope blockers without editing them. Do NOT re-run the full suite per fix — it is the dominant loop cost (task 0436 R2).
697
- 9. **Confirming run — standalone invocation only (at most once).** After all fixes, repeat the scope-safe formatting invocation from step 1, then run the selected validation command **at most once** to confirm (here the default project gate is `bun run spur-check`). If it passes, the hop is done. **Skip this step entirely when `--gate-log` is set** (see step 10) — under the pipeline nothing consumes its verdict.
698
- 10. **Pipeline-awareness (R4, task 0483; confirming run dropped 2026-08-31).** `--gate-log` is set only by the pipeline's `test-fix` hop, so treat it as the pipeline signal. There, `test-recheck` runs the full `${vars.qualityGateCmd}` gate immediately after this hop returns — that is the **deciding** run, the only one that writes PASS to `.spur/run/<wbs>-test-gate.status`. A confirming run inside this hop decides nothing and costs a second full gate (~105 s here) to answer a question `test-recheck` is about to answer anyway. So under `--gate-log`: **run no full gate at all.** Your self-check is what step 8 already gives you — the failing tests you re-ran individually — plus **one `bun run lint`** (~7 s: Biome + typecheck) to catch type or lint breakage your fixes introduced. Then return and let `test-recheck` judge. `test-recheck` opens with the same cheap `${vars.gateProbeCmd}` probe, so a still-red tree is caught in seconds rather than by a full suite. Historical anti-pattern this replaces: 0482 ran the gate 3× plus a standalone `bun run test`, and `test-recheck` then ran it a 5th time.
697
+ 9. **Confirming run — standalone invocation only (at most once).** After all fixes, repeat the scope-safe formatting invocation from step 1, then run the selected validation command **at most once** to confirm (here the default project gate is `spur self exec check`). If it passes, the hop is done. **Skip this step entirely when `--gate-log` is set** (see step 10) — under the pipeline nothing consumes its verdict.
698
+ 10. **Pipeline-awareness (R4, task 0483; confirming run dropped 2026-08-31).** `--gate-log` is set only by the pipeline's `test-fix` hop, so treat it as the pipeline signal. There, `test-recheck` runs the full `${vars.qualityGateCmd}` gate immediately after this hop returns — that is the **deciding** run, the only one that writes PASS to `.spur/run/<wbs>-test-gate.status`. A confirming run inside this hop decides nothing and costs a second full gate (~105 s here) to answer a question `test-recheck` is about to answer anyway. So under `--gate-log`: **run no full gate at all.** Your self-check is what step 8 already gives you — the failing tests you re-ran individually — plus **one `spur self exec lint`** (~7 s: Biome + typecheck) to catch type or lint breakage your fixes introduced. Then return and let `test-recheck` judge. `test-recheck` opens with the same cheap `${vars.gateProbeCmd}` probe, so a still-red tree is caught in seconds rather than by a full suite. Historical anti-pattern this replaces: 0482 ran the gate 3× plus a standalone `bun run test`, and `test-recheck` then ran it a 5th time.
699
699
  11. Report: list what was fixed (file + one-line summary per fix). If any error could not be resolved, report it explicitly — do not suppress.
700
700
  - **Invariants:** Never bypass with `--no-verify`, `--force`, or new `biome-ignore`/`eslint-disable` suppressions. Never skip or `.skip` a test to make the suite green. Fix the root cause, not the symptom. Here lint checks formatting and types; it does not prove that the full test gate passed. Keep every write inside `--scope`. **Never re-run the full gate more than once per confirming pass** (R4) — use targeted probes during the fix loops and let the pipeline's `test-recheck` state be the deciding run.
701
701
  - **MANDATORY Exit Condition — standalone only.** At step 9, execute the following once; do not add another confirming run:
@@ -45,7 +45,8 @@ run-id) or you walked it manually (name the gate you checked).
45
45
  Threshold is by **change type**, not priority: a task is high-stakes if it touches code, tests, or
46
46
  shared infrastructure. Priority (P1/P2) is advisory - it does not by itself force raw paste on a
47
47
  pure doc/markdown edit. For high-stakes tasks, paste the **raw tail output** of every verification
48
- gate that applies - not a one-line "green" summary. Include:
48
+ gate that applies - not a one-line "green" summary. Include the tails of spur-new's own gate
49
+ scripts (any other project: the same actions through `spur self exec lint` / `test`):
49
50
 
50
51
  - `bun run lint` tail (last 20 lines minimum)
51
52
  - `bun run test` tail (last 20 lines minimum)
@@ -92,8 +93,8 @@ spur workflow trace <run-id> # confirm the terminal state
92
93
  has verified the tree. You must:
93
94
 
94
95
  ```bash
95
- bun run lint
96
- bun run test
96
+ spur self exec lint
97
+ spur self exec test
97
98
  ```
98
99
 
99
100
  Both must pass before you go further. Establish the environmental baseline first if you are in a
@@ -85,14 +85,14 @@ batch can never destroy its own evidence. Reuse mode retains its operator-owned
85
85
  persists the Step 5 report under the invoking tree; the reused tree's `.spur/run/` remains the live
86
86
  copy while that tree lives on. The per-run provenance — the worktree DB's run/action rows and the
87
87
  `.spur/memory/runs/<runId>.md` + `.state.json` records — is persisted mechanically by WT-4a's
88
- `spur workflow inline persist-out --from <worktree> [--task-file <merged-task>]...` call,
88
+ `spur workflow persist-out --from <worktree> [--task-file <merged-task>]...` call,
89
89
  not by hand.
90
90
 
91
91
  **Stage records are worktree-local too (0948 R9, E7 Finding 5; persisted by 0975 R1).** Each task's
92
92
  own per-stage run record (`.spur/memory/runs/<runId>.md` + `.state.json`) and the worktree DB's run rows are
93
93
  written inside the worktree and **are removed with it** in create mode — the E7 batch lost exactly
94
94
  this evidence. Copying them out is no longer a manual audit-time duty: WT-4a (create-mode block
95
- below) runs `spur workflow inline persist-out --from "$WT_PATH"` **before** WT-4b holder cleanup,
95
+ below) runs `spur workflow persist-out --from "$WT_PATH"` **before** WT-4b holder cleanup,
96
96
  which copies the run/action/phase/transition/workflow-state rows and both record files into the
97
97
  invoking tree.
98
98
 
@@ -106,7 +106,7 @@ obligation; neither do root-qualified paths (`knowledge-kit/.spur/run/…`, `/ab
106
106
  which cite another project's evidence — cite foreign run artifacts that way, never bare. A citation missing in BOTH trees, a divergent cited file (never overwritten — reconcile
107
107
  by hand), an unreadable task file, or more than 64 distinct cited files fails the pass → WT-5.
108
108
 
109
- **Durable planes ride it too (E71).** Persist-out copies canonical task verdicts, feature run/latest receipts, nested run artifacts and owned sessions before teardown. Artifact and task-run-link rows are exported with run rows, and stored path references are redirected to the invoking tree. Conflicting or unreadable **owned** retained evidence refuses teardown. A divergent copy of evidence a forwarded task does **not** own (its WBS is not a forwarded task's and its receipt `runId` is not a worktree run row) is `foreign-divergent` (1139 R5): it is skipped, reported as `evidenceSkipped[{name, reason:'foreign-divergent', newer:'invoking'|'worktree'}]`, and the invoking-tree copy is never overwritten — another task's newer verdict can no longer block this batch's teardown. A divergent **owned** file still fails with `durable evidence conflicts`. `spur workflow inline persist-check` accepts the same foreign-divergent set from the success JSON and does not recompute it.
109
+ **Durable planes ride it too (E71).** Persist-out copies canonical task verdicts, feature run/latest receipts, nested run artifacts and owned sessions before teardown. Artifact and task-run-link rows are exported with run rows, and stored path references are redirected to the invoking tree. Conflicting or unreadable **owned** retained evidence refuses teardown. A divergent copy of evidence a forwarded task does **not** own (its WBS is not a forwarded task's and its receipt `runId` is not a worktree run row) is `foreign-divergent` (1139 R5): it is skipped, reported as `evidenceSkipped[{name, reason:'foreign-divergent', newer:'invoking'|'worktree'}]`, and the invoking-tree copy is never overwritten — another task's newer verdict can no longer block this batch's teardown. A divergent **owned** file still fails with `durable evidence conflicts`. `spur workflow persist-check` accepts the same foreign-divergent set from the success JSON and does not recompute it.
110
110
 
111
111
  **Owned evidence rides it too (1012).** With at least one `--task-file`, persist-out also treats as
112
112
  copy obligations the worktree's `.spur/run/` direct children named `<wbs>-…` (the WBS is each
@@ -124,7 +124,7 @@ pass before the invoking tree is written → WT-5. Without `--task-file` nothing
124
124
  The shapes are pinned (task 0975 R1; `record-missing`, citation behavior per 0984; `evidenceSkipped` per 1139 R5): idempotent on re-persist;
125
125
  success exits 0 printing
126
126
  `{"ok":true,"persisted":<n>,"skipped":[{"id":<run-id>,"reason":"id-exists"|"external-key-conflict"|"record-conflict:<file>"|"record-missing:<file>"|"cited-directory:<name>"|"cited-symlink:<name>"|"cited-non-file:<name>"}],"evidenceSkipped":[{"name":<file>,"reason":"foreign-divergent","newer":"invoking"|"worktree"}]}`
127
- (`evidenceSkipped` is omitted when empty; the same object is written to `<invoking-tree>/.spur/run/persist-out.json` for `spur workflow inline persist-check` to read)
127
+ (`evidenceSkipped` is omitted when empty; the same object is written to `<invoking-tree>/.spur/run/persist-out.json` for `spur workflow persist-check` to read)
128
128
  — an `external-key-conflict` skip leaves the target run unchanged **and fails the pass (1049)**: the delegate exits 1 printing `{"ok":false,"error":…}` naming the skipped source run ids, because that (workflow, external key) identity already belongs to a different receiving run — the batch was not persisted, so reconcile the source worktree by hand (it stays the provenance owner of record) before any teardown: auditable reconciliation names the original archived DB snapshot and the skipped source run identities, verifies the archive by file hash and by merged-commit ancestry of the source branch, and treats any residual deletion as explicit operator-authorized cleanup under the strict canonical evidence rules above. Task 1149 R1 grades a terminal duplicate lifecycle row (`task-lifecycle`/`feature-lifecycle`) as `external-key-conflict-bookkeeping` (non-fatal), so guarded transitions driven inside the worktree do not block teardown; the receiving lifecycle row is reconciled automatically during persist-out. An `id-exists` replay repairs missing owned artifacts and task links without duplicating them. A
129
129
  `record-conflict:<file>` never overwrites a divergent invoking-tree record and causes the delegate to exit 1, retaining the worktree. Before reconciling one by hand, check **what** the invoking record is: when the run closed `done` inside a linked worktree the owning tree holds that run's **WT-0 landing obligation** (`landing: required branch=… path=… base=…`, see [execution-worktree-landing.md § WT-0](execution-worktree-landing.md#wt-0--markerless-worktree-runs)) — a 3-line record that is *not* a stub of the worktree's stage narrative, so copying the worktree record over it destroys the very obligation a later merge must rewrite. The reconciliation is therefore: discharge the obligation in place (`landing: merged <sha>` or `landing: retained`), and preserve the worktree narrative beside it (e.g. `<runId>.worktree-provenance.md`) rather than in place of it. A
130
130
  `record-missing:<file>` skip is a known `task-lifecycle`/`feature-lifecycle` row with no record file
@@ -52,8 +52,8 @@ that a flat FSM cannot express.
52
52
  The active batch orchestrator owns the spaces **between** task runs: resolve+freeze the set,
53
53
  topo-sort, run each task's pipeline, inspect terminal state, decide continue/halt, and emit the
54
54
  report. It does not redefine individual steps. Interactive sequential omit/inline delegates each
55
- ready WBS to [inline-pipeline-driver.md](inline-pipeline-driver.md); explicit/parallel/headless paths
56
- delegate to the workflow engine and `vars.agent` resolution.
55
+ ready WBS to the same engine loop as every other path (host executor by default); explicit/parallel/
56
+ headless paths delegate to the workflow engine and `vars.agent` resolution.
57
57
 
58
58
  ## Step 1 — Selector resolution (R1)
59
59
 
@@ -316,7 +316,8 @@ per-task letter is ever hand-assigned.
316
316
  4. **When a task ends:** mark the task's letter (and its published digits) with the task's terminal
317
317
  outcome — `done` renders completed; `failed`/`skipped`/`blocked` render pending + ` [<outcome>]`
318
318
  per the frozen status mapping in
319
- [inline-pipeline-driver.md](inline-pipeline-driver.md) § Host todo update styles.
319
+ [cross-cutting.md](cross-cutting.md#shared-startup-contract-task-0814-r1r3r4r6r7r8) § Publish the
320
+ generated plan (per-item and full-list host update styles).
320
321
 
321
322
  ## Step 3 — The driver loop (R3, R4)
322
323
 
@@ -338,7 +339,8 @@ for wbs in plan: # default sequential mode
338
339
  report += preflight-skip(wbs, preflight); continue
339
340
  visible plan (§2.7): letter in_progress + task-children digits at start; letter ← outcome at end
340
341
  run: if interactive sequential omit/inline:
341
- inline-pipeline-driver(task-pipeline.yaml, wbs)
342
+ spur workflow run task-pipeline.yaml --vars <vars with executor=host> --json
343
+ perform the returned work item; spur workflow continue <run-id> --answer-text <receipt>
342
344
  else:
343
345
  spur workflow run task-pipeline.yaml --vars <vars> --async --json
344
346
  follow trace until terminal # spur workflow trace "$RUN" --follow --timeout 600000
@@ -371,9 +373,9 @@ record the reason.
371
373
  ### 3.1 Per-task execution reuses the pipeline verbatim (R4)
372
374
 
373
375
  Each task runs through the **standard single-task pipeline** — `task-pipeline.yaml`
374
- — with no new FSM and no step edits. Interactive sequential omit/inline invokes the host
375
- [inline pipeline driver](inline-pipeline-driver.md), which interprets that file; explicit/parallel
376
- execution invokes the workflow engine. The batch loop inspects the result and never redefines a
376
+ — with no new FSM and no step edits. Interactive sequential omit/inline and explicit/parallel
377
+ execution both invoke the workflow engine on that file; only the executor differs (host session vs
378
+ child process). The batch loop inspects the result and never redefines a
377
379
  step.
378
380
 
379
381
  **Explicit/parallel path: launch async, then one bounded follow** (per execution-workflow.md §"Step 2"): a pipeline with
@@ -606,15 +608,15 @@ command doc so it does not read as a bug.
606
608
 
607
609
  The cheap rule gates fail fast when run first — the full-gate run is dominated by the ~65 s test
608
610
  suite, so a gate run that dies at `test-post-check` or `corpus-check` wastes most of its wall time.
609
- Before launching a full `spur-check-new`:
611
+ Before launching a full `spur-check:full`:
610
612
 
611
- 1. **Run the static checks first** — `bun run lint` (biome over the whole tree plus every workspace
613
+ 1. **Run the static checks first** — `spur self exec lint` (biome over the whole tree plus every workspace
612
614
  typecheck). They fail in ~20 s and are the two defects that most often kill a full launch: a
613
615
  formatting miss in a touched test file, or a generated declaration surface that no longer lists a
614
616
  new export. Measured 2026-10-12: two consecutive `spur-check` launches died here and each spent
615
617
  its whole wall time before reaching the suite.
616
- 2. **Then the two rule gates** — `bun run test-pre-check` (43 rules: `no-console-output`,
617
- `no-direct-process-spawn`, `cli-*`, `require-corresponding-test`) and `bun run test-post-check`
618
+ 2. **Then the two rule gates** — `spur self exec rules-pre` (43 rules: `no-console-output`,
619
+ `no-direct-process-spawn`, `cli-*`, `require-corresponding-test`) and `spur self exec rules-post`
618
620
  (`every-export-has-tsdoc`, `coverage-gate`). They catch boundary/TSDoc violations in seconds.
619
621
  Two `coverage-gate` facts change how you call post-check: it **fails closed when
620
622
  `.coverage/lcov.info` is absent**, which is the normal state of a fresh worktree, so run it after
@@ -46,14 +46,15 @@ one thing and yields, so the **pipeline (not the agent) owns the loop**.
46
46
  | Stage | Operation | Defined in |
47
47
  | ------- | ----------- | ------------ |
48
48
  | `implement` | `/sp:dev-run --mode implement <wbs>` — write the code that satisfies the task; author `## Solution`. | [dev-operations.md §4 run](dev-operations.md) → `sp:code-implementation` |
49
- | `test` → (`test-fix` ↔ `test-recheck`) → `review` \| `failed` | **Project quality gate** (not `/sp:dev-unit`). Soft shell probe of `${vars.qualityGateCmd}` (default `bun run spur-check`) — green path pays **one** full gate run. On FAIL: bounded `/sp:dev-fixall` loop (`qualityGateMaxFixAttempts`, default 2) with soft recheck; exhausted attempts route to pipeline `failed`. `/sp:dev-unit` remains **coverage gap-fill** (router C3/C5 / standalone). | [dev-operations.md §10 fixall](dev-operations.md); unit op still §1 |
49
+ | `test` → (`test-fix` ↔ `test-recheck`) → `review` \| `failed` | **Project quality gate** (not `/sp:dev-unit`). Soft shell probe of `${vars.qualityGateCmd}` (default `spur self exec check`) — green path pays **one** full gate run. On FAIL: bounded `/sp:dev-fixall` loop (`qualityGateMaxFixAttempts`, default 2) with soft recheck; exhausted attempts route to pipeline `failed`. `/sp:dev-unit` remains **coverage gap-fill** (router C3/C5 / standalone). | [dev-operations.md §10 fixall](dev-operations.md); unit op still §1 |
50
50
  | `review` | `/sp:dev-review --tasks <wbs>` — SECUA-framework review of the diff. | [dev-operations.md §2 review](dev-operations.md) |
51
51
  | `verify` | `sp:code-verification` — requirements traceability + verdict. | [dev-operations.md §3 verify](dev-operations.md) |
52
52
 
53
- Interactive omit/`inline` executes these model stages through the
54
- [inline pipeline driver](inline-pipeline-driver.md) in the host session and records stage/session
55
- provenance. The host is the controller; eligible `agent.run` stages may dispatch once to a native
56
- subagent (task 0508), with host fallback and no post-dispatch replay — see the driver reference.
53
+ Interactive omit/`inline` executes these model stages in the host session through the engine loop:
54
+ the engine owns every guard and transition, the host performs each returned work item and records
55
+ its receipt. Eligible `agent.run` stages may dispatch once to a native subagent (task 0508), with
56
+ host fallback and no post-dispatch replay — see
57
+ [cross-cutting.md](cross-cutting.md#inline-default-execution-surface).
57
58
  `--agent <name|auto>`, parallel batches, and headless workflow invocation select
58
59
  the existing subprocess actions. Direct invocations of the same dev operations remain inline by
59
60
  default. See the [inline-default execution-surface contract](cross-cutting.md#inline-default-execution-surface).
@@ -106,9 +107,9 @@ and merge-or-retain after the run reports. The lifecycle is
106
107
  applied to a batch of one. Rejected with `--mode implement`.
107
108
 
108
109
  **Choose the surface before execution.** In an interactive `/sp:dev-run --mode full` invocation,
109
- omit/`--agent inline` selects the [inline pipeline driver](inline-pipeline-driver.md). Read the YAML
110
- at invocation time, allocate the inline run id/session provenance, record `task run-link`, and walk
111
- its actions/guards in declaration order. Do not call `spur workflow run` on this path. `--agent auto`,
110
+ omit/`--agent inline` selects the host executor. Start the run with `spur workflow run <file>
111
+ --vars '{"executor":"host",…}'` and advance it with `spur workflow continue <run-id> --answer-text
112
+ <receipt>`; the engine walks the YAML's actions/guards, this session never re-executes them. `--agent auto`,
112
113
  a named executor, an explicit `vars.agent`/`vars.implementAgent`, or a headless caller selects the
113
114
  workflow subprocess path below. Never fall back silently from inline to `agent.default`.
114
115
 
@@ -171,13 +172,7 @@ Two branches reach the bounded repair hop, not one (session finding after 1088):
171
172
  an on-graph, bounded path to remediation instead of dead-ending the run, and the fix re-enters
172
173
  quality → review → verify on a freshly captured proof digest.
173
174
 
174
- The completion gate reads deterministic evidence only (F511, task 1207): both `verify → record` and
175
- `record → done` check the proof digests in `.spur/run/<wbs>-verdict.json` — verdict PASS, matching
176
- run/definition digests, review stage completed. No confidence is read, stated or acknowledged; the
177
- asserted `Confidence:` line and the `ackLowConfidence` operator var were retired because a model
178
- that can write a PASS can write `HIGH` — the grade verified nothing. What protects a hand-driven
179
- `task update <wbs> done` is unchanged: the digests must match the run's captured proof, and the
180
- review stage must actually be completed in the artifact.
175
+ The completion gate reads deterministic evidence only: the review stage's **verdict artifact** (`.spur/run/<wbs>-verdict.json`, derived by `spur task verdict --from-answer`) and the **tree-hash gate receipt** (`.spur/run/<wbs>-check-receipt.json`, reused while the tree is unchanged). The ADR-071 proof chain this paragraph used to describe — proof digests, `verify → record`, review-inside-the-bracket — was deleted by the D65 pipeline rewrite; `record` now runs inside `done`'s onEnter and `verify` merged into `review`. Two grades are read, and only as cross-checks against the answer's own rows: the review answer states one `Confidence: HIGH|MEDIUM|LOW` line (ADR-141) and the review gate recomputes it from the finding dispositions, rejecting a disagreement. Nothing grades the *verifier's* opinion: an open P1–P3 finding blocks `done` on its own, and what protects a hand-driven `task update <wbs> done` is unchanged — the gate must show a PASS receipt for this tree and a recorded review artifact.
181
176
 
182
177
  Agentic steps use the pure slash inputs declared by each YAML `agent.run` action (ADR-043). The
183
178
  workflow engine dispatches them; the interactive driver invokes their backing skills in-session.
@@ -325,8 +320,8 @@ the partial work still in the working tree. The failure output names the partial
325
320
  1. **Recognise.** `.spur/memory/runs/<runId>/artifacts/<runId>-implement-partial.md` exists, the run reported `exited
326
321
  with code 3`, the task is at `todo`. The artifact's `git diff --stat` section is the partial
327
322
  work inventory.
328
- 2. **Establish green from the partial files.** `bun run format` then `bun run lint` + `bun
329
- test` (or the affected packages). Fix mechanical formatting/type fallout first; the partial
323
+ 2. **Establish green from the partial files.** `spur self exec format` then `spur self exec lint` + `spur
324
+ self exec test` (or the affected packages). Fix mechanical formatting/type fallout first; the partial
330
325
  tree is the baseline, not a code review target.
331
326
  3. **Resume the remaining requirements against that tree.** Re-run the implement step with the
332
327
  partial diff as explicit context — hand the continuation agent `git diff` (the partial
@@ -338,11 +333,25 @@ the partial work still in the working tree. The failure output names the partial
338
333
  [`done-housekeeping.md`](done-housekeeping.md) F6 — it carries the provenance obligations
339
334
  (honest `done_reason`, verdict regeneration).
340
335
 
341
- **Inline path (task 0727).** There is no `<runId>-implement-partial.md` artifact on the inline
342
- driver path — it is written only by the subprocess `agent.run` action. The inline equivalent is
343
- the dispatch-timeout contract in [`inline-pipeline-driver.md`](inline-pipeline-driver.md):
344
- resume from the partial tree, never restart the stage inline; the partial working tree is the
345
- recovery input.
336
+ **Host-executor path (task 0727).** There is no `<runId>-implement-partial.md` artifact on the
337
+ host path — it is written only by the subprocess `agent.run` action. A host-stage timeout is a
338
+ recorded stage failure: resume from the partial tree, never restart the stage inline; the partial
339
+ working tree is the recovery input.
340
+
341
+ **3a. Verify dispatch floor (task 1039).** A small, clean diff does not need a separate verify
342
+ session: when the diff is below the floor, `verify` runs host-inline in the current session and
343
+ records exactly one log line —
344
+
345
+ stage verify executed inline in session <session-id> (below dispatch floor: diffstat files <f> lines <n>)
346
+
347
+ The floor is read from the recorded diffstat artifact under `.spur/run/`; the filter that decides
348
+ it is
349
+
350
+ ([.files, .insertions, .deletions] | all(type == "number")) and .files <= 3 and (.insertions + .deletions) <= 60 and .sensitive == false
351
+
352
+ A diffstat that is missing, malformed, or names a sensitive path is never below the floor: the
353
+ floor is an optimisation, so an unreadable artifact dispatches the normal verify executor. (This
354
+ contract moved here from the retired inline driver reference, D65 1242.)
346
355
 
347
356
  **3. Match the executor to the size (task 0487 R3).** Size and executor capability are one
348
357
  decision, not two. **≥ 6 requirements or ≥ 9 Plan items → a `reviewer`-role executor (the
@@ -116,7 +116,7 @@ WT_PATH="$(cd "../<worktree-dir>" && pwd)" # hoisted: needed by WT-4a AND WT-4
116
116
  # automatically reconciles each forwarded terminal task's receiving lifecycle row in
117
117
  # the invoking tree (finalizing any stale in-flight or interrupted row left from the
118
118
  # task's creation), so a manual DB edit is never required:
119
- spur workflow inline persist-out --from "$WT_PATH" "${TASK_FILE_ARGS[@]}" \
119
+ spur workflow persist-out --from "$WT_PATH" "${TASK_FILE_ARGS[@]}" \
120
120
  || { echo "halt: worktree run-record persist-out failed - worktree retained (WT-5)" >&2; write_marker retained; exit 1; }
121
121
  #
122
122
  # WT-4b — bounded CWD-holder cleanup (task 0720 R1). $WT_PATH above is the EXACT
@@ -155,9 +155,9 @@ fi # NO prune/remove/branch delete
155
155
  # worktree while it still owns evidence absent from (or divergent from) the invoking tree —
156
156
  # the D63 landing skipped persist-out and silently lost verdicts + receipts. WBSes ride the
157
157
  # same TASK_FILE_ARGS resolved for WT-4a; run ids are a separate `--run-id <id>` per run and
158
- # belong to THIS check only — `spur workflow inline persist-out` accepts `--from` and
158
+ # belong to THIS check only — `spur workflow persist-out` accepts `--from` and
159
159
  # `--task-file` alone and exits 2 on `--run-id` (1089 driver note).
160
- spur workflow inline persist-check --from "$WT_PATH" "${TASK_FILE_ARGS[@]}" \
160
+ spur workflow persist-check --from "$WT_PATH" "${TASK_FILE_ARGS[@]}" \
161
161
  || { echo "WT-4 halt: worktree evidence would be abandoned - persist-out missing or stale (WT-5)" >&2; write_marker retained; exit 1; }
162
162
  git worktree remove "../<worktree-dir>" \
163
163
  || { echo "WT-4 halt: worktree remove failed after landed merge $BATCH_TIP" >&2; write_marker retained; exit 1; }
@@ -408,7 +408,7 @@ conflicts itself. Other halt causes (task failure, HITL pause) keep the hint as
408
408
  # (a test, a hook, a tool invocation) discards MERGE_HEAD silently. The failure is invisible
409
409
  # until the history is read — it produced a single-parent commit in run ada5a36c (task 1090),
410
410
  # which then needed an extra empty ancestry merge to repair. A cheap probe could not
411
- # reproduce it (MERGE_HEAD survived `bun run lint`, the rule preset and a test-file subset),
411
+ # reproduce it (MERGE_HEAD survived `spur self exec lint` (recorded then as the raw lint script), the rule preset and a test-file subset),
412
412
  # so the cross-command dependency is removed rather than diagnosed.
413
413
  git checkout <base-ref> && git merge --no-ff -m "<prepared message>" <branch>
414
414
 
@@ -443,7 +443,7 @@ git checkout <base-ref> && git merge --no-ff -m "<prepared message>" <branch>
443
443
  # ancestry).
444
444
 
445
445
  # 6. persist evidence out (WT-4a), then WT-4b/4c cleanup, and set the marker to merged
446
- spur workflow inline persist-out --from <worktree> --task-file …
446
+ spur workflow persist-out --from <worktree> --task-file …
447
447
  ```
448
448
 
449
449
  The report reuses the [`--next` chain contract](flag-glossary.md#--next-chain-contract) halt-report
@@ -79,8 +79,8 @@ inherits no cwd from the previous call, so "the loop runs with the worktree as p
79
79
  bootstrap convenience, never a carried guarantee. Every host command boundary in this lifecycle —
80
80
  corpus reads/writes, the WT-3b commit, WT-4's merge and evidence persistence, the WT-4a
81
81
  persist-out — re-selects its intended tree within the same call via the canonical per-call pin
82
- protocol ([inline-pipeline-driver.md](inline-pipeline-driver.md) § "Per-call execution-tree
83
- pin"): `cd --` to the quoted absolute tree, verify `pwd -P` against the recorded physical tree,
82
+ protocol ([execution-batch.md](execution-batch.md) § "Per-call execution-tree pin"):
83
+ `cd --` to the quoted absolute tree, verify `pwd -P` against the recorded physical tree,
84
84
  verify Git top-level and expected branch, then execute the already-resolved Spur invocation. The
85
85
  identity is consumed, not re-derived: it is the WT-3 marker's confirmed `path` + `branch` (or the
86
86
  resolved worktree's physical path + checked-out branch in reuse mode). The one deliberate tree
@@ -269,8 +269,8 @@ the tree's own source:
269
269
  cd "<worktree>" && bun apps/cli/src/index.ts task check <wbs> --json
270
270
 
271
271
  Run that as a per-call pin subshell (task 1058) — `cd --` plus the `pwd -P` / git-top-level /
272
- branch identity checks of the canonical protocol (inline-pipeline-driver.md § "Per-call
273
- execution-tree pin") — rather than relying on a persistent `cd` between host tool calls.
272
+ branch identity checks of the canonical protocol (execution-batch.md § "Per-call execution-tree
273
+ pin") — rather than relying on a persistent `cd` between host tool calls.
274
274
 
275
275
  Confirm isolation by making a distinctive change in the worktree and checking that
276
276
  the command reflects it.
@@ -209,8 +209,9 @@ wraps the one resolved ticket inside the worktree before the merge, and is skipp
209
209
  **Anchor:** `#flag-force`.
210
210
 
211
211
  Bypass a specific named guard that would otherwise stop the command (e.g. a shippable check on
212
- `dev-verify`/`dev-verifyall`). Named, not global: the command documentation states which guard is
213
- forced. Never bypasses lifecycle status transitions or irreversible HITL gates.
212
+ `dev-verify`/`dev-verifyall`, or the per-HEAD review dedupe on `dev-pr-review`). Named, not global:
213
+ the command documentation states which guard is forced. Never bypasses lifecycle status transitions
214
+ or irreversible HITL gates.
214
215
 
215
216
  ### `--feature <id>` — scope the operation to a feature
216
217
 
@@ -17,7 +17,7 @@ catch it (the CLI catches schema violations, not intent errors).
17
17
  Run each checklist as an actual command sequence, not a mental confirmation:
18
18
 
19
19
  1. Read the checklist for the gate you are about to enter.
20
- 2. Run each verification command (grep, `spur task check`, `bun run lint`, etc.).
20
+ 2. Run each verification command (grep, `spur task check`, `spur self exec lint`, etc.).
21
21
  3. If any checkbox fails: stop, fix, re-run. Do not enter the gate.
22
22
  4. Only when every checkbox passes: enter the gate (run the CLI verb or trigger the workflow state).
23
23
 
@@ -89,7 +89,7 @@ Entered before `task-pipeline.yaml` `review` state dispatches `sp:code-verificat
89
89
  - [ ] The implementation matches the task's `## Plan` checklist (every checked item maps to a code/test/doc change).
90
90
  - [ ] `git status` shows only changes traceable to this task's Plan (no drive-by edits).
91
91
  - [ ] Check receipt current: `spur task gate <wbs> status` reports `reuse: true` for
92
- `.spur/run/<wbs>-check-receipt.json` at the current digest; run `bun run spur-check` only
92
+ `.spur/run/<wbs>-check-receipt.json` at the current digest; run `spur self exec check` only
93
93
  when it reports stale or missing (0940 — never re-run the full chain on a reusable receipt).
94
94
  - [ ] New `biome-ignore` / `eslint-disable` suppressions: none, or each is justified inline.
95
95
  - [ ] No new `console.*` in scripts (use a project logger if one exists).
@@ -176,9 +176,9 @@ transition to `done`.
176
176
 
177
177
  > **Finding-rule discipline (ADR-108).** When checker policy changes, run focused tests and one explicit unsuppressed `bun run corpus-check` audit; record/reconcile essential failures without waivers (T10). Ordinary corpus commit prep checks changed tasks/features and their required linked evidence, not the corpus (T11). The baseline and regenerators remain deleted; tests are not a substitute for the explicit policy-change audit.
178
178
  >
179
- > **Transition-shim discipline (`transition-shim-check`, task 0541 / ADR-058).** Wired as an early step of `spur-check` / `spur-check-new` (second, after `link-check` and before `lint`) so a shim violation fails in ~0.3 s rather than after the test run. Two-sided against `config/transition-shims.json`: an `@transition-shim(<id>)` marker with no manifest entry fails as a **new unregistered shim**, **and** a listed entry whose marker is gone from source fails as a **stale entry**. Emptying the manifest is the definition of the agent-role transition being complete. A removal condition must be objectively checkable against the repository. Shapes: `docs/04_DESIGN.md` §2.5.
179
+ > **Transition-shim discipline (`transition-shim-check`, task 0541 / ADR-058).** Wired as an early step of `spur-check-feature` (second, after `link-check`) so a shim violation fails in ~0.3 s rather than after the test run. Two-sided against `config/transition-shims.json`: an `@transition-shim(<id>)` marker with no manifest entry fails as a **new unregistered shim**, **and** a listed entry whose marker is gone from source fails as a **stale entry**. Emptying the manifest is the definition of the agent-role transition being complete. A removal condition must be objectively checkable against the repository. Shapes: `docs/04_DESIGN.md` §2.5.
180
180
 
181
- > **Sandbox test baseline (`bun run test` / `spur-check`).** In the restricted Bash sandbox
181
+ > **Sandbox test baseline (`bun run test` / `spur-check`).** In spur-new's restricted Bash sandbox
182
182
  > `bun run spur-check` exits **1 regardless of code health**: ~24 tests bind real ports and the
183
183
  > sandbox denies `listen`, so they fail environmentally. They cluster in seven suites — `spur
184
184
  > projects CLI`, `startServer`, `createServerContext`, `healthModule`, `rpc client`,
@@ -343,8 +343,26 @@
343
343
  },
344
344
  "builder": {
345
345
  "type": "object",
346
- "description": "Builder noun configuration. Currently hosts the release plumbing knobs for `spur builder bump-ver` / `drop-tags`; every field defaults to the built-in release convention, so an absent builder section keeps default behavior.",
346
+ "description": "Builder noun configuration. Hosts the release plumbing knobs for `spur builder bump-ver` / `drop-tags` and the SDLC action binding (`preset` + `actions`, ADR-139) that binds the reserved action vocabulary to this project's commands. Every field defaults to the built-in behavior, so an absent builder section keeps default behavior.",
347
347
  "properties": {
348
+ "preset": {
349
+ "type": "string",
350
+ "enum": ["bun", "none"],
351
+ "default": "bun",
352
+ "description": "Stack preset for the SDLC action vocabulary. `bun` (default) binds every reserved action to the legacy bun scripts, so a bun project needs no config change; `none` binds only the stack-neutral `spur rule run` presets (rules-pre, rules-post) and leaves every other undeclared action unbound."
353
+ },
354
+ "actions": {
355
+ "type": "object",
356
+ "default": {},
357
+ "propertyNames": { "pattern": "^[a-z][a-z0-9-]*$" },
358
+ "additionalProperties": {
359
+ "oneOf": [
360
+ { "type": "string", "minLength": 1, "pattern": "\\S" },
361
+ { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 } }
362
+ ]
363
+ },
364
+ "description": "Action name -> one shell command, or a list of action names (composite, executed in order, fail-fast). A declared entry replaces the preset binding whole. Reserved names (closed in packages/config): build, dev, clean, format, autofix, lint, typecheck, test, test-full, test-heavy, rules-pre, rules-post, check, check-full, check-feature, lint-changed, typecheck-changed, test-changed; other names are custom (exec-only). Placeholders are allowed only in the scoped actions, each with its own set: lint-changed {files}, typecheck-changed {workspace}, test-changed {workspace} {paths}; and each substituted item is shell-quoted."
365
+ },
348
366
  "bump-ver": {
349
367
  "type": "object",
350
368
  "description": "Release knobs for bump-ver/drop-tags. Defaults: -v separator, publish.yml workflow, chore(release) commit, gh run list limit 5.",