@gobing-ai/spur 0.4.14 → 0.4.15

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 (31) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/pipeline-budgets.json +0 -14
  3. package/config/workflow-candidates.json +18 -0
  4. package/config/workflows/feature-lifecycle.yaml +9 -16
  5. package/config/workflows/idea-pipeline.yaml +158 -740
  6. package/config/workflows/wrapup-pipeline.yaml +91 -623
  7. package/package.json +9 -9
  8. package/plugins/sp/README.md +8 -4
  9. package/plugins/sp/commands/dev-idea.md +9 -5
  10. package/plugins/sp/commands/dev-plan.md +1 -1
  11. package/plugins/sp/commands/dev-pr-review.md +6 -6
  12. package/plugins/sp/commands/dev-wrap.md +1 -1
  13. package/plugins/sp/commands/dev-wrapall.md +4 -4
  14. package/plugins/sp/plugin.json +1 -1
  15. package/plugins/sp/skills/pr-reviewing/SKILL.md +36 -18
  16. package/plugins/sp/skills/spec-decomposition/references/decomposition.md +2 -1
  17. package/plugins/sp/skills/spur-cli/references/builder.md +2 -2
  18. package/plugins/sp/skills/spur-cli/references/features.md +7 -0
  19. package/plugins/sp/skills/spur-cli/references/tasks.md +1 -1
  20. package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +2 -1
  21. package/plugins/sp/skills/spur-dev/references/dev-operations.md +10 -10
  22. package/plugins/sp/skills/spur-dev/references/execution-batch.md +9 -10
  23. package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +1 -2
  24. package/plugins/sp/skills/spur-dev/references/execution-worktree-setup.md +4 -4
  25. package/plugins/sp/skills/spur-dev/references/planning-workflow.md +7 -6
  26. package/plugins/sp/skills/wayfinder/SKILL.md +11 -1
  27. package/spur.js +800 -799
  28. package/config/workflows/feature-verification.yaml +0 -98
  29. package/config/workflows/pr-review.yaml +0 -209
  30. package/config/workflows/wayfinder-resolution.yaml +0 -302
  31. package/plugins/sp/skills/wayfinder/references/pipeline-resolution.md +0 -30
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/spur",
3
- "version": "0.4.14",
3
+ "version": "0.4.15",
4
4
  "description": "Spur CLI — local-first harness for mainstream coding agents: constraint checking, workflow orchestration, agent health, and history analytics. Bun-native; exposes the `spur` command.",
5
5
  "keywords": [
6
6
  "spur",
@@ -57,14 +57,14 @@
57
57
  },
58
58
  "devDependencies": {
59
59
  "@commander-js/extra-typings": "^14.0.0",
60
- "@gobing-ai/ts-db": "^0.5.28",
61
- "@gobing-ai/ts-ai-runner": "^0.5.28",
62
- "@gobing-ai/ts-dual-workflow-engine": "^0.5.28",
63
- "@gobing-ai/ts-infra": "^0.5.28",
64
- "@gobing-ai/ts-llm-jsonl-importer": "^0.5.28",
65
- "@gobing-ai/ts-rule-engine": "^0.5.28",
66
- "@gobing-ai/ts-runtime": "^0.5.28",
67
- "@gobing-ai/ts-utils": "^0.5.28",
60
+ "@gobing-ai/ts-db": "^0.5.30",
61
+ "@gobing-ai/ts-ai-runner": "^0.5.30",
62
+ "@gobing-ai/ts-dual-workflow-engine": "^0.5.30",
63
+ "@gobing-ai/ts-infra": "^0.5.30",
64
+ "@gobing-ai/ts-llm-jsonl-importer": "^0.5.30",
65
+ "@gobing-ai/ts-rule-engine": "^0.5.30",
66
+ "@gobing-ai/ts-runtime": "^0.5.30",
67
+ "@gobing-ai/ts-utils": "^0.5.30",
68
68
  "@types/bun": "1.4.3",
69
69
  "@types/figlet": "^1.7.0",
70
70
  "@types/node-notifier": "8.0.5",
@@ -95,8 +95,8 @@ list this README is checked against.
95
95
 
96
96
  | Command | What it does |
97
97
  | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
98
- | `dev-idea` | Turn a vague idea into a feature with AC and a decomposed task batch — discovery, idea-eval, feature-create, AC, feature-check, system-design, decompose, batch-create, handoff |
99
- | `dev-plan` | Plan a feature from a written description — intake → feature create → AC generation → feature check gate → decomposition → batch-create |
98
+ | `dev-idea` | Turn a vague idea into a feature with AC and a decomposed task batch — discovery, idea-eval, feature-create, AC, system-design, decompose, batch-create, handoff |
99
+ | `dev-plan` | Plan a feature from a written description — intake → feature create → AC generation → decomposition → batch-create |
100
100
  | `dev-brainstorm` | Interactive solution design — heuristic discovery interview followed by structured ideation with trade-offs and confidence scoring |
101
101
  | `dev-find-way` | Wayfinder — chart a multi-session investigation map, or resolve one map ticket; always in an isolated git worktree + branch (`--worktree`, `--wrap`) |
102
102
  | `dev-refine` | Refine task requirements via structured Q&A — clarify scope, elicit missing details, tighten acceptance criteria; `--depth ready` audits and corrects existing claims, freezes the spec, promotes to todo |
@@ -348,7 +348,7 @@ surface or run one workflow. All skills target the same five core platforms: `cl
348
348
  | `conflict-finding` | 1.0 | Authority-aware semantic audit — four-pillar (source/task/feature/authority) conflict discovery, claim-specific authority resolution, reproducible evidence, confirmed owner-routed remediation; backs `/sp:dev-find-conflict` |
349
349
  | `fleet-join` | 1.0 | Guest fleet occupancy — join as a live guest occupant, loop `spur agent wait --inbox` to pull work, reply through `spur message reply`, then leave; G73 (task 1081) |
350
350
  | `next-feature` | 1.0 | Prompt-first feature frontier prioritizer — sync-first precondition, B3 actionability gate (cited, never restated), tiered rubric over measured signals, D1–D4 defect proposals conforming to the restructure map schema; backs `/sp:dev-find-next` |
351
- | `pr-reviewing` | 1.0 | GitHub Codex PR review — PR prepare/reuse, `@codex review` request with per-HEAD dedupe, bounded polling, findings normalization, validated fix + re-review; spine SSOT `pr-review.yaml` + `spur builder pr`; backs `/sp:dev-pr-review` |
351
+ | `pr-reviewing` | 1.0 | GitHub Codex PR review — PR prepare/reuse, `@codex review` request with per-HEAD dedupe, bounded polling, findings normalization, validated fix + re-review; direct `spur builder pr-status`/`pr-check`/`pr-open`/`pr-review` sequence; backs `/sp:dev-pr-review` |
352
352
  | `indexed-context` | 1.0 | Cross-agent project context — anatomy/learnings/pitfalls/buglog/memory in `.spur/context/`; hook-tracked token-ledger; graceful degradation on agents without hooks |
353
353
  | `history-anatomy` | 1.0 | Diagnostic interpretation owner over already-imported history — daily/ad-hoc mode contract, closed finding taxonomy, twelve-section report contract, `enrich`/`validate` rubrics; no workflow launch, no JSONL fallback, no corpus mutation |
354
354
  | `session-review` | 1.1 | Inline review of the active coding-agent session — compact outcomes, evidence-backed resolved/open issue classification, proposal-only improvements, and next actions; no workflow, import, delegation, or mutation |
@@ -695,7 +695,11 @@ pipeline owns one lifecycle phase:
695
695
  | `task-pipeline.yaml` | Single-task execution | `/sp:dev-run` |
696
696
  | `idea-pipeline.yaml` | Idea/planning → feature + tasks | `/sp:dev-idea`, `/sp:dev-plan` |
697
697
  | `wrapup-pipeline.yaml` | Post-execution wrap-up | `/sp:dev-wrap`, `/sp:dev-wrapall` |
698
- | `wayfinder-resolution.yaml` | Wayfinder ticket resolution loop | `spur workflow run` (free-form) |
698
+
699
+ > 1251 (D66): the PR-review and wayfinder-investigate spines were retired — the PR sequencing
700
+ > lives in the `spur builder pr-status | pr-check | pr-open | pr-review` verbs, and the wayfinder
701
+ > investigate hop is a skill procedure in `sp:wayfinder`. 1246 retired the feature-verification
702
+ > wrapper the same way: `feature-lifecycle`'s `verifying` entry calls `spur feature verify`.
699
703
 
700
704
  > 0946 (D64): the `decision-routing-example.yaml` authoring sample was retired from the catalogue
701
705
  > (zero runs, example-only — `docs/design/workflow-catalogue-refactor.md` §10); the pattern lives on
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Turn a vague idea into a feature with AC and a decomposed task batch — discovery, idea-eval, feature-create, AC, feature-check, system-design, decompose, batch-create (Design by default), handoff
2
+ description: Turn a vague idea into a feature with AC and a decomposed task batch — discovery, idea-eval, feature-create, AC, system-design, decompose, batch-create (Design by default), handoff
3
3
  role: planner
4
4
  argument-hint: "\"<idea>\" | --from-file <path> [--skip-design] [--agent <inline|auto|name>] [--auto]"
5
5
  allowed-tools: ["Bash", "Read", "Skill", "AskUserQuestion"]
@@ -45,9 +45,12 @@ trace records the answering source. `--auto` takes the recommended option at eac
45
45
  | Gate | Pauses when | Recommendation comes from | `--auto` takes |
46
46
  | --- | --- | --- | --- |
47
47
  | idea-eval | after discovery | eval report `## Recommendation` (`proceed` / `reshape` / `drop`) | `proceed`/`reshape` → continue; `drop` → cancel; missing → still asks |
48
- | feature-check | after AC | recorded AC check + requirement coverage | PASS → continue; FAIL → revise AC (cap 3) |
49
48
  | design-approval | after system-design (skipped by `--skip-design`) | recorded design check | PASS → approve; FAIL → still asks |
50
- | batch-create | after decompose | batch schema + task-order sidecar validation | create the batch |
49
+
50
+ The AC check and the batch create are deterministic pipeline steps, not gates: `ac-generate`
51
+ runs `spur feature check` and routes on the recorded result (a FAIL retries the AC loop, cap 3),
52
+ and `batch-create-run` runs `spur task batch-create` and routes on its own recorded result.
53
+ Neither ever pauses, under any profile.
51
54
 
52
55
  Flag → vars: `--auto` sets `profile=auto`, `idea_approved=true`, `design_approved=true`;
53
56
  `--skip-design` sets `design=skip`.
@@ -78,5 +81,6 @@ Flag → vars: `--auto` sets `profile=auto`, `idea_approved=true`, `design_appro
78
81
  - The `start` state persists the operator's idea argument (or the `--from-file` contents)
79
82
  **unmodified** to `.spur/run/<run-id>-idea-input.md` (0887 R1); every model-bearing stage prompt
80
83
  treats that file as the authoritative ask, and the precheck fails the run when it is empty or missing.
81
- - Stage contract (discovery → idea-eval → feature-create → AC → feature-check → system-design →
82
- decompose → batch-create → ready-prepare → handoff): `plugins/sp/skills/spur-dev/references/dev-operations.md` § idea.
84
+ - Stage contract (discovery → idea-eval → feature-create → ac-generate → system-design
85
+ (conditional) → design-approval → decompose → batch-create-run → ready-prepare →
86
+ handoff-finalize → handoff): `plugins/sp/skills/spur-dev/references/dev-operations.md` § idea.
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Plan a feature from a description — intake → feature create → AC generation → feature check gate → decomposition → batch-create (Design by default)
2
+ description: Plan a feature from a description — intake → feature create → AC generation → decomposition → batch-create (Design by default)
3
3
  role: planner
4
4
  argument-hint: "\"<description>\" [--feature <id>] [--parent <feature-id>] [--skip-design] [--agent <inline|auto|name>] [--auto]"
5
5
  allowed-tools: ["Bash", "Read", "Skill", "AskUserQuestion"]
@@ -17,9 +17,9 @@ and commits the verified ones. The invoking agent orchestrates.
17
17
  | Flag | Description | Default |
18
18
  | --- | --- | --- |
19
19
  | `<mode>` | `status\|review\|fix` — status: read-only branch + PR state; review: local branch-diff review (or the GitHub Codex spine with `--codex`); fix: validate, fix and commit findings from either reviewer. | status |
20
- | `--codex` | `review` only — route through the GitHub Codex spine (`pr-review.yaml`) instead of the local `/sp:dev-review`. | off |
21
- | `--force` | `review --codex` only — request a fresh review of the current HEAD even when it was already reviewed (`mode=rerun`). | off |
22
- | `--no-wait` | `review --codex` only — post the request and return `pending` without polling (`mode=submit`). | off |
20
+ | `--codex` | `review` only — route through the GitHub Codex spine (`spur builder pr-check` → `pr-open` → `pr-review`) instead of the local `/sp:dev-review`. | off |
21
+ | `--force` | `review --codex` only — request a fresh review of the current HEAD even when it was already reviewed (`pr-review --force`). | off |
22
+ | `--no-wait` | `review --codex` only — post the request and return `pending` without polling (`pr-review --no-wait`). | off |
23
23
  | `--base <branch>` | Base branch for the branch report, the `pr-check` diff and a newly created PR. | existing PR base, else repo default |
24
24
  | `--agent <inline\|auto\|name>` | Who performs the model-bearing triage/fix. Omit uses the current agent, `inline` forbids dispatch, `auto` resolves the declared role, and a name pins that executor. This does not change the deterministic route. | omit |
25
25
  | `<focus>` | Remaining free text — extra review focus for the local review or the Codex request (e.g. `security boundaries and transaction idempotency`). | none |
@@ -36,9 +36,9 @@ For shared semantics, see the [flag glossary](../skills/spur-dev/references/flag
36
36
 
37
37
  - Apply the [inline-default execution-surface contract](../skills/spur-dev/references/cross-cutting.md#inline-default-execution-surface).
38
38
  - Delegate everything: `Skill(skill="sp:pr-reviewing", args="$ARGUMENTS")`. The skill owns mode
39
- routing, the local `/sp:dev-review` dispatch, the Codex spine and finding triage/fix. On the
40
- Codex path `pr-review.yaml` is the SSOT for state order and guards, and the four flat
41
- `spur builder pr-<verb>` verbs are the deterministic git/gh core every spine step shells out to.
39
+ routing, the local `/sp:dev-review` dispatch, the Codex spine and finding triage/fix. The Codex
40
+ path is a direct sequence of the four flat `spur builder pr-<verb>` verbs — `pr-check` →
41
+ `pr-open` → `pr-review` → `pr-status` — each verb's verdict gating the next.
42
42
  - Retired modes print one notice line and run their replacement: `full` → `review --codex`;
43
43
  `submit` → `review --codex --no-wait`; `rerun` → `review --codex --force`; `collect` → `status`.
44
44
  `rules` prints that `AGENTS.md` review-rule authoring belongs to `cc:cc-magents` (the `cc` plugin)
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Wrap up a single completed task — learnings, metrics, doc-sync, optional feature transition and branch cleanup
2
+ description: Wrap up a single completed task — learnings, metrics, doc-sync, optional branch cleanup
3
3
  role: coder
4
4
  argument-hint: "<wbs> [--agent <inline|auto|name>] [--auto] [--merge] [--dry-run]"
5
5
  allowed-tools: ["Bash", "Read", "AskUserQuestion"]
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Wrap up a batch of completed tasks — learnings, metrics, doc-sync, feature transition, optional branch cleanup
2
+ description: Wrap up a batch of completed tasks — learnings, metrics, doc-sync, optional branch cleanup
3
3
  role: coder
4
4
  argument-hint: "[--since <iso-date>] [--feature <id>] [--status <s>] [--agent <inline|auto|name>] [--auto] [--merge] [--dry-run]"
5
5
  allowed-tools: ["Bash", "Read", "AskUserQuestion"]
@@ -14,7 +14,7 @@ Wraps the **wrapup-pipeline.yaml** workflow.
14
14
  | Flag | Description | Default |
15
15
  | --- | --- | --- |
16
16
  | `--since` `<iso-date>` | Wrap tasks completed since a date. | configured |
17
- | `--feature` `<id>` | Wrap tasks in a feature. | omitted |
17
+ | `--feature` `<id>` | Select tasks in a feature (selection only — it no longer advances the feature; follow up with `spur feature sync <id>`). | omitted |
18
18
  | `--status` `<s>` | Only wrap tasks in a status. | done |
19
19
  | `--agent` `<inline\|auto\|name>` | Who runs the wrap's model-bearing steps. Wrap is workflow-backed (headless): `omit` and `--agent inline` resolve identically per task 0687 — tier substitution under objective trigger 3 (durable auditable run record required) with a warning naming the substituted executor; `auto` tier-resolves an executor; a name pins that executor into `vars.agent`. | inline |
20
20
  | `--auto` | Skip objective HITL gates. | off |
@@ -42,8 +42,8 @@ For shared semantics, see the [flag glossary](../skills/spur-dev/references/flag
42
42
  ```bash
43
43
  AGENT=… # omit≡inline → tier-substituted executor + warning; auto tier-resolved; <name> unchanged
44
44
  echo "execution surface: subprocess; reason: trigger 3 — durable auditable run record required; requested agent: <inline|auto|name>; executor: $AGENT"
45
- VARS=$(jq -nc --arg tasks "$TASKS" --arg feature "$FEATURE" --arg agent "$AGENT" --arg profile "$PROFILE" --arg merge "$MERGE" \
46
- '{tasks:$tasks, feature:$feature, agent:$agent, profile:$profile, merge:$merge}')
45
+ VARS=$(jq -nc --arg tasks "$TASKS" --arg agent "$AGENT" --arg profile "$PROFILE" --arg merge "$MERGE" \
46
+ '{tasks:$tasks, agent:$agent, profile:$profile, merge:$merge}')
47
47
  spur workflow run wrapup-pipeline.yaml --vars "$VARS" [--dry-run]
48
48
  ```
49
49
 
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "sp",
3
- "version": "0.4.14",
3
+ "version": "0.4.15",
4
4
  "description": "Spur — a local-first harness engineering toolkit that wraps mainstream coding agents with constraint checking, workflow orchestration, and history analytics."
5
5
  }
@@ -39,19 +39,19 @@ invoking coding agent is the local orchestrator and implementer.
39
39
  ▼
40
40
  sp:pr-reviewing (this skill — mode routing, local-review dispatch, triage, fix)
41
41
  │ local route: pr-check --scan-only → git diff --name-only → /sp:dev-review
42
- │ Codex route: pr-review.yaml (check → precheck → open → review)
42
+ │ Codex route: pr-check → pr-open → pr-review → pr-status
43
43
  ▼
44
44
  `spur builder pr-<verb>` + `spur builder show-branches` ← tested git/gh core
45
45
  ```
46
46
 
47
- - **State order and guards** for the Codex route are defined once, in `pr-review.yaml`. Do not
48
- re-derive them in prose; when the YAML and this file disagree, the YAML wins and this file gets
49
- fixed.
50
47
  - **Deterministic work** (every git/gh call) goes through the four flat `spur builder pr-<verb>`
51
48
  commands and `spur builder show-branches` — never hand-rolled `gh` invocations that drift from
52
49
  the tested core.
50
+ - **Sequencing inside a verb** (preflight→hygiene, push→ensure, request→wait→collect) lives in
51
+ `runPrVerb`, not in this file. This skill orders the *verbs*; each verb owns its own ops, its
52
+ one-word verdict and its exit code.
53
53
  - **Model-bearing work** (the local review dispatch, finding triage, `fix` edits) is this skill's
54
- job and never enters the workflow machine.
54
+ job and never enters a deterministic verb.
55
55
 
56
56
  ## Modes and routing
57
57
 
@@ -61,7 +61,7 @@ Parse the first positional argument as the mode; default `status`.
61
61
  | --- | --- | --- |
62
62
  | `status` | direct CLI | `spur builder show-branches --base <b> --pr --format markdown`, with the report filtered to the current branch and its base, plus `spur builder pr-status --json` (PR number, CI, local changes, latest current-head review in `.review`). Read-only. |
63
63
  | `review` | local stack | `spur builder pr-check --base <b> --scan-only --json`; `BLOCK` stops, `WARN` is reported. Then `git diff --name-only <b>...HEAD` → `/sp:dev-review --scope <comma-joined paths> --focus <focus>`. An empty diff ends with "nothing to review". Posts nothing to GitHub. |
64
- | `review --codex` | `pr-review.yaml` | Run the spine with `mode=full`; `--force` → `mode=rerun`; `--no-wait` → `mode=submit`; `focus`/`baseBranch` passed as vars. States: check → precheck → open → review. `pending` is reported as pending, never failure. |
64
+ | `review --codex` | direct verb sequence | `spur builder pr-check --base <b> --json` → `pr-open --base <b> --json` → `pr-review --focus <focus> [--force] [--no-wait] --json` → `pr-status --json`. Each verb's verdict gates the next; `PENDING` is reported as pending, never failure. |
65
65
  | `fix` | direct verb + model work | Findings from the latest local review verdict, or from `spur builder pr-status --json` `.review` when the last review was Codex. Validate each finding against the code before editing; rejected findings are listed with a reason. Fix the legitimate ones, run the project gate, make one focused commit and push without force (`spur builder pr-open` handles a first push). |
66
66
 
67
67
  Retired modes print one notice line and run their replacement: `full` → `review --codex`;
@@ -125,17 +125,37 @@ Codex: clean | findings | pending | not requested | unavailable
125
125
 
126
126
  ## Review mode (--codex)
127
127
 
128
- Run `pr-review.yaml` as the single source of truth for the Codex spine — check → precheck → open →
129
- review. Never call `spur builder pr-review` in a way that bypasses the workflow's state order and
130
- guards.
128
+ Four verbs in order. Each one's `--status-file` verdict gates the next — stop at the first red
129
+ verdict and report it with the stage's JSON record; never run a later verb to "retry" an earlier
130
+ one, and never force-push around a rejected push.
131
131
 
132
132
  ```sh
133
- spur workflow run pr-review.yaml --vars '{"mode":"full","baseBranch":"<base>","focus":"<focus>"}'
133
+ spur builder pr-check --base "$base" --json --status-file .spur/run/pr-check.status
134
+ # PASS|WARN → continue (a WARN rides into the report). BLOCK|FAIL → stop.
135
+ # BLOCK = secrets/.env/conflict markers/private keys. FAIL = preflight (detached HEAD, dirty
136
+ # tree, gh unauthenticated, no GitHub remote, or the current branch *is* the base).
137
+
138
+ # Optional project fast check before spending a review request. Run it only when the project
139
+ # defines one; a red check stops here rather than reviewing code that fails its own gate.
140
+
141
+ spur builder pr-open --base "$base" --json --status-file .spur/run/pr-open.status
142
+ # FOUND|CREATED → continue. FAIL → stop (push rejected, no reviewable delta, PR create failed).
143
+
144
+ spur builder pr-review --focus "$focus" [--force] [--no-wait] \
145
+ --timeout 600 --interval 30 --json --status-file .spur/run/pr-review.status
146
+ # CLEAN|FINDINGS → collected. PENDING → pending, not failed. FAIL → stop.
147
+
148
+ spur builder pr-status --json # composite PR/CI/Codex report; read-only, never gates
134
149
  ```
135
150
 
136
- - `mode=full` by default; `--force` → `mode=rerun`; `--no-wait` → `mode=submit`.
137
- - The workflow's `pending` terminal state is **pending, not failed**: report the requested or
138
- in-flight state and collect later with `status`.
151
+ - **Flags from the invocation:** `--force` requests a fresh review of an already-reviewed HEAD;
152
+ `--no-wait` posts the request and returns `PENDING` without polling. Both are optional and
153
+ independent — pass each as its own argument. The default is request → wait → collect, with
154
+ per-HEAD dedupe.
155
+ - **`PENDING` is pending, not failed** (a wait timeout included): report the requested or in-flight
156
+ state and collect later with `status`.
157
+ - **`pr-review` owns request → wait → collect in one runtime** (`runPrVerb`), so there is no
158
+ separate request record to thread between steps.
139
159
  - When `--codex` is passed, the external review goes through the GitHub PR and an `@codex review`
140
160
  request only — never a local Codex CLI review. If GitHub Codex Code Review is unavailable, stop
141
161
  and report the setup/access problem; never silently fall back to a local review.
@@ -219,20 +239,18 @@ and nothing else.
219
239
 
220
240
  ## Platform notes
221
241
 
222
- - The skill, command and workflow YAML are platform-neutral; per-platform emission is owned by
223
- superskill. On platforms without a Skill tool, read this file and follow the protocol manually.
242
+ - The skill and command are platform-neutral; per-platform emission is owned by superskill. On
243
+ platforms without a Skill tool, read this file and follow the protocol manually.
224
244
  - Prerequisites: `git` for every path. `gh` authenticated with a GitHub remote wherever PR state is
225
245
  touched: `status` (its `pr-status` half — the `show-branches` topology runs on `git` alone, and
226
246
  its `--pr` enrichment only degrades with a warning), `review` (step 1's `pr-check --scan-only`),
227
- `review --codex` (the workflow's check/open/review states), and `fix` when it reads a Codex
247
+ `review --codex` (its `pr-check`/`pr-open`/`pr-review` verbs), and `fix` when it reads a Codex
228
248
  verdict or pushes through `pr-open`. The `git diff --name-only` scope, `/sp:dev-review`, and
229
249
  `fix`'s local-verdict edit/commit path need only `git`. The `--codex` path additionally needs
230
250
  GitHub Codex Code Review enabled for the repository.
231
251
 
232
252
  ## Additional Resources
233
253
 
234
- - **Workflow SSOT (Codex path):** `pr-review.yaml` (seeded by `spur init`; tune per project — wait
235
- budget, `preReviewCmd`, dedupe policy)
236
254
  - **Command source:** the four flat verbs `pr-status` / `pr-check` / `pr-open` / `pr-review` plus the
237
255
  read-only `show-branches` report — see `sp:spur-cli` `references/builder.md`
238
256
  - **Local review:** `/sp:dev-review` — its dimensions live in `sp:code-verification` and
@@ -563,7 +563,8 @@ schema before emitting.
563
563
  **Pass the deterministic task check.** Each `acceptance_criteria` bullet must be an exact feature
564
564
  scenario title (L4.uncovered-task-scenario); add task-local checks as prose after the bullets, not
565
565
  as extra bullets. No section body may use `HITL`, `merge event`, `content-gate`, `GATED`, or
566
- `capstone` as standalone words (L4.gate-language); ambiguous words (`approval`, `approved`,
566
+ `capstone` as standalone words in any case (L4.gate-language) — dotted action kinds such as
567
+ `hitl.confirm` are fine, a bare `hitl` count is not; ambiguous words (`approval`, `approved`,
567
568
  `merged`) warn only with a gating cue (`until`, `after`, `once`, `before`, `pending`, `blocked`,
568
569
  `wait for`, `requires`) in the same sentence, so plain composition prose stays silent — say
569
570
  "operator answer" / "accepted" instead, and keep enum values out of that list too.
@@ -80,7 +80,7 @@ spur builder pr-review --force --json # force a fresh review of
80
80
  ```
81
81
 
82
82
  Four flat verbs over the ops absorbed from `plugins/sp/scripts/pr-reviewing.ts` (script-absorption
83
- PR-7); driven by the `pr-review` workflow. Each composes existing ops and embeds their records in
83
+ PR-7); sequenced directly by `sp:pr-reviewing`. Each composes existing ops and embeds their records in
84
84
  one JSON object per verb: `pr-status` → the status object plus `review` (collect, or `null` with no
85
85
  PR); `pr-check` → `{ ok, verdict, preflight, hygiene }`; `pr-open` → `{ ok, verdict, push, pr }`;
86
86
  `pr-review` → `{ ok, verdict, request, findings }`.
@@ -94,7 +94,7 @@ PR); `pr-check` → `{ ok, verdict, preflight, hygiene }`; `pr-open` → `{ ok,
94
94
  explicit pin that no longer matches the PR head fails loud instead of reporting a moved commit.
95
95
  - **Deduping:** `pr-review` collects without a new request when the exact HEAD is already reviewed,
96
96
  and returns `PENDING` when a marked request is still in flight; `--force` requests a fresh review
97
- (`mode=rerun`). Never force-pushes.
97
+ of the current HEAD regardless. Never force-pushes.
98
98
 
99
99
  ```bash
100
100
  # deprecated alias, one release: maps the old op names to the verbs above
@@ -272,6 +272,13 @@ spur feature sync H2 --folder docs/custom-tasks --json # non-default tasks fol
272
272
  task statuses, verdict mtimes); an identical next call replays the prior result
273
273
  (`suppressed: true`) instead of re-deriving. A changed input, `--force`, or a non-blocked
274
274
  outcome re-derives/clears. Dry-run never reads or writes the state.
275
+ - **Single-call completion after the verifying entry (1247):** entering `verifying` runs the
276
+ feature-verification pass (1246), but the hop list was derived before that pass recorded its
277
+ receipt. Sync re-derives **once** after a `verifying` hop lands, so a PASS receipt applies
278
+ `verifying → done` in the same call (`appliedHops: ['verifying','done']`). A FAIL or still-missing
279
+ receipt leaves the feature in `verifying` and still exits `0`; `proposal.reason` then names the
280
+ receipt finding and the rework edge (`spur feature update <id> active`). The re-derive
281
+ never runs when the verifying hop was not applied (guard denied, goal conflict, `--dry-run`).
275
282
  - **`--json`** single-feature emits `{ proposal, applied, appliedHops[], suppressed? }`; `--all` emits
276
283
  `{ totalFeatures, evaluated, updatedCount, results[] }` where each result is the single-feature
277
284
  shape. `proposal` is
@@ -51,7 +51,7 @@ re-reading or re-tokenizing the task.
51
51
  | `batch-create` | Create many tasks from a validated JSON array | `--file <path>` `--skip-ready` `--agent <selector>` `--folder` `--json` |
52
52
  | `batch-plan <mode>` | Batch plan projection: `waves` / `order` / `task-children` (JSON on stdout; the batch driver's visible plan) | `--tasks <file>` `--concurrency <n>` `--letter <A-Z>` `--plan <file>` |
53
53
  | `preflight [wbs]` | Read-only batch admission: TABLE A STOP check, quick readiness (`--operation`), one-shot recovery hop; status/deps read from the corpus unless given | `--status <s>` `--deps <list>` `--dep-status <map>` `--operation <op>` `--filtered-count <n>` `--required-sections <list>` `--present-sections <list>` `--force` `--recovery` `--classify-feature <json>` `--folder` `--json` |
54
- | `wrapup <op>` | One deterministic `wrapup-pipeline` step (`resolve`, `route-reason`, `metrics`, `feature-transition`, `doc-sync-snapshot`, `doc-sync-scope`, `doc-supersession`, `drift-probe`) — workflow-internal | `--run-id <id>` `--tasks <json>` `--mode <m>` `--feature <id>` `--feature-gate-cmd <cmd>` `--spur-bin <cmd>` |
54
+ | `wrapup <op>` | One deterministic `wrapup-pipeline` step (`resolve`, `route-reason`, `metrics`, `doc-sync-snapshot`, `doc-sync-scope`, `doc-supersession`, `drift-probe`) — workflow-internal | `--run-id <id>` `--tasks <json>` `--mode <m>` `--spur-bin <cmd>` |
55
55
  | `record <wbs>` | Write `Testing` from a verify verdict (deterministic); bare-`## Review` fallback only; optional Solution + transition | `--verdict-file <path>` `--solution-from-diff` `--transition <status>` `--folder` `--json` |
56
56
  | `verdict <wbs>` | Derive PASS/PARTIAL/FAIL/UNKNOWN from verify answer text → verdict JSON; see [answer-file shape](tasks/verbs.md#answer-file-shape-what---from-answer-parses) | `--from-answer <path>` `--folder` `--json` |
57
57
  | `check [wbs]` | Four-layer validation; `--fix` repairs structure; `--corpus` is the explicit unsuppressed audit; `--precheck` adds the pipeline precheck gate (size + evidence channel, WBS required) | `--strict` `--as <status>` `--strict-core` `--precheck` `--fix` `--folder` `--corpus` `--since <ref>` `--json` |
@@ -127,7 +127,8 @@ scenario, it matches by title. Rules:
127
127
  - **No synonyms in cross-references.** The title in the feature file and the title in the
128
128
  task's AC reference must be byte-identical.
129
129
  - **Avoid gate vocabulary in titles.** `spur task check` (L4.gate-language) rejects task sections
130
- containing `HITL`, `merge event`, `content-gate`, `GATED`, or `capstone` as standalone words;
130
+ containing `HITL`, `merge event`, `content-gate`, `GATED`, or `capstone` as standalone words in any
131
+ case (dotted action kinds such as `hitl.confirm` are fine);
131
132
  ambiguous words (`approval`, `approved`, `merged`) warn only when the same sentence also carries
132
133
  a gating cue (`until`, `after`, `once`, `before`, `pending`, `blocked`, `wait for`, `requires`)
133
134
  — plain composition prose like "the stages merged back into one step" stays silent. Task AC
@@ -366,13 +366,13 @@ check receipt (`receipt.runId === runId`), otherwise `n/a (<reason>)`. Any value
366
366
 
367
367
  ### 6. plan
368
368
 
369
- - **Purpose:** Plan a feature from a description — intake → feature create → AC generation → feature check gate → decomposition → batch-create (with **Design by default**).
369
+ - **Purpose:** Plan a feature from a description — intake → feature create → AC generation → decomposition → batch-create (with **Design by default**).
370
370
  - **Inputs:** `"<description>"` (required). `--feature <id>` links to an existing feature. `--parent <feature-id>` nests under a parent. Omitted/`inline` drives `idea-pipeline.yaml` in the host session; `auto` or a name launches the async workflow worker. **Design package flags (unified with `/sp:dev-idea`):**
371
371
  - **Default:** author task `design` on every batch item + feature satellite when the seam heuristic fires (**ties lean design**). There is **no** `--design` force flag.
372
372
  - `--skip-design` — skip feature satellite **and** omit task `design` fields (scaffold only; refine fills later). Sole design opt-out.
373
373
  - `--auto` — accept the recommendation at every operator gate, taste gates included (same contract as § 16 idea).
374
374
  - **Backing:** `sp:spur-dev` skill, `plan` operation. Stage floor: the `planner` role per [`roles.md`](../../../references/roles.md) — this file names roles, never tiers (0538 R4).
375
- - **Behavior:** Clarify scope → `spur feature create` → author BDD AC → `spur feature check` gate → decompose into task-batch JSON **including `design` (unless `--skip-design`)** → `spur task batch-create` gate. Design package details: [planning-workflow.md](planning-workflow.md) Step 5.5.
375
+ - **Behavior:** Clarify scope → `spur feature create` → author BDD AC → decompose into task-batch JSON **including `design` (unless `--skip-design`)**. The pipeline then runs `spur feature check` on the recorded AC (`ac-generate`) and `spur task batch-create` (`batch-create-run`) as deterministic steps that route on their recorded result — neither is an operator gate. Design package details: [planning-workflow.md](planning-workflow.md) Step 5.5.
376
376
  - **Delegation:** `Skill(skill="sp:spur-dev", args="plan $ARGUMENTS")`
377
377
 
378
378
  ### 7. docs
@@ -489,10 +489,10 @@ gate commands, pending questions and approval requirements.>
489
489
 
490
490
  ### 14. wrap
491
491
 
492
- - **Purpose:** Wrap up a single completed task — capture learnings, record metrics, sync docs, and optionally advance the feature / clean up the branch.
492
+ - **Purpose:** Wrap up a single completed task — capture learnings, record metrics, sync docs, and optionally clean up the branch.
493
493
  - **Inputs:** `<wbs>` (required, positional). `--agent <inline|auto|name>` names the wrap's model-bearing executor (default: `inline`, resolving identically when omitted; task 0687); wrap is workflow-backed (headless), so omit ≡ explicit `inline` (task 0687): tier substitution under objective trigger 3 (durable auditable run record required) plus one warning naming the substituted executor. `auto` tier-resolves an executor, and a name pins that executor into `vars.agent` (see [SSOT](cross-cutting.md#inline-default-execution-surface)). `--auto` skips objective confirmations (the branch-cleanup HITL gate still pauses — irreversible). `--merge` triggers branch cleanup (irreversible HITL gate).
494
494
  - **Backing:** `spur workflow run wrapup-pipeline.yaml` — direct workflow invocation (no backing skill; the pipeline IS the procedure).
495
- - **Behavior:** Resolves the executor (`agent.default` for omit/`inline`, tier-resolved for `auto`, unchanged for a name), emits a pre-dispatch notice naming the subprocess override — `execution surface: subprocess`, `reason: trigger 3 — durable auditable run record required`, `requested agent: <selector>`, `executor: <resolved>` — then builds `--vars '{"tasks":"[\"<wbs>\"]","agent":"<resolved>","profile":"interactive|auto","merge":"true|false"}'` and invokes the wrapup pipeline. The pipeline runs: task-resolve → doc-sync → learnings-validate → learnings-append → doc-tripwire → metrics-record → (feature-transition) → (branch-cleanup) → done. `doc-tripwire` (1037) runs the TRUSTED CONFIG ONLY `docTripwireCmd` over the still-uncommitted wrap diff after the doc-sync exits converge — the default runs `bun run test-repo-wide` only when `package.json` declares the script (a no-op in other projects); override it via `--vars`, and an empty string disables the check while still recording PASS; a FAIL fails the wrap with already-written learnings/docs preserved. Task statuses are NOT mutated. Branch cleanup is an irreversible HITL gate that always pauses, even under `--auto`.
495
+ - **Behavior:** Resolves the executor (`agent.default` for omit/`inline`, tier-resolved for `auto`, unchanged for a name), emits a pre-dispatch notice naming the subprocess override — `execution surface: subprocess`, `reason: trigger 3 — durable auditable run record required`, `requested agent: <selector>`, `executor: <resolved>` — then builds `--vars '{"tasks":"[\"<wbs>\"]","agent":"<resolved>","profile":"interactive|auto","merge":"true|false"}'` and invokes the wrapup pipeline. The pipeline runs: task-resolve → doc-sync → learnings-validate → learnings-append → doc-tripwire → metrics-record → (branch-cleanup) → done. `doc-tripwire` (1037) runs the TRUSTED CONFIG ONLY `docTripwireCmd` over the still-uncommitted wrap diff after the doc-sync exits converge — the default runs `bun run test-repo-wide` only when `package.json` declares the script (a no-op in other projects); override it via `--vars`, and an empty string disables the check while still recording PASS; a FAIL fails the wrap with already-written learnings/docs preserved. Task and feature statuses are NOT mutated. Branch cleanup is an irreversible HITL gate that always pauses, even under `--auto`.
496
496
  - **Vars string typing:** `tasks` is a JSON-encoded **string**, not a JSON array — `spur workflow run --vars` accepts only string values (`--vars values must be strings`); the pipeline's guards parse the string with `jq length`. `jq -nc` guarantees the shape:
497
497
 
498
498
  ```bash
@@ -505,15 +505,15 @@ gate commands, pending questions and approval requirements.>
505
505
 
506
506
  ### 15. wrapall
507
507
 
508
- - **Purpose:** Wrap up a batch of completed tasks — capture learnings, record metrics, sync docs, advance a feature through legal lifecycle edges, and optionally clean up branches.
509
- - **Inputs:** `--since <iso-date>` filters done tasks by frontmatter `updated_at >= date` (v1 approximation). `--feature <id>` selects all tasks under a feature AND advances the feature through legal lifecycle edges (`backlog → active → verifying → done`, guards honored). `--status <s>` (default: `done`) filters by task status. `--agent <inline|auto|name>` names the wrap's model-bearing executor (default: `inline`, resolving identically when omitted; task 0687); wrap is workflow-backed (headless), so omit ≡ explicit `inline` (task 0687): tier substitution under objective trigger 3 (durable auditable run record required) plus one warning naming the substituted executor. `auto` tier-resolves an executor, and a name pins that executor into `vars.agent` (see [SSOT](cross-cutting.md#inline-default-execution-surface)). `--auto` skips objective confirmations. `--merge` triggers branch cleanup (irreversible HITL gate).
508
+ - **Purpose:** Wrap up a batch of completed tasks — capture learnings, record metrics, sync docs, and optionally clean up branches.
509
+ - **Inputs:** `--since <iso-date>` filters done tasks by frontmatter `updated_at >= date` (v1 approximation). `--feature <id>` selects all tasks under a feature; it no longer advances the feature — wrap-up owns post-task knowledge work only (1249 R1/R5), so follow the batch with `spur feature sync <id>` if a transition is wanted. `--status <s>` (default: `done`) filters by task status. `--agent <inline|auto|name>` names the wrap's model-bearing executor (default: `inline`, resolving identically when omitted; task 0687); wrap is workflow-backed (headless), so omit ≡ explicit `inline` (task 0687): tier substitution under objective trigger 3 (durable auditable run record required) plus one warning naming the substituted executor. `auto` tier-resolves an executor, and a name pins that executor into `vars.agent` (see [SSOT](cross-cutting.md#inline-default-execution-surface)). `--auto` skips objective confirmations. `--merge` triggers branch cleanup (irreversible HITL gate).
510
510
  - **Backing:** `spur workflow run wrapup-pipeline.yaml` — direct workflow invocation.
511
- - **Behavior:** Resolves the task list via `spur task list --json` (filtered by `--feature`, `--since`, `--status`), resolves the executor (omission and explicit `--agent inline` tier-substitute identically with a warning per task 0687, `auto` tier-resolves, a name pins), emits a pre-dispatch notice naming the subprocess override — `execution surface: subprocess`, `reason: trigger 3 — durable auditable run record required`, `requested agent: <selector>`, `executor: <resolved>` — then builds `--vars '{"tasks":"[...]","feature":"<id>","agent":"<resolved>","profile":"interactive|auto","merge":"true|false"}'` and invokes the wrapup pipeline. The pipeline runs the same states as `wrap` — including the `doc-tripwire` hop (1037), whose TRUSTED CONFIG ONLY `docTripwireCmd` runs the repo-wide tripwires over the still-uncommitted wrap diff and can be overridden or disabled (empty string) via `--vars` — but with the full task list and optional feature transition. Task statuses are NOT mutated. Feature transitions go through `spur feature update` so lifecycle guards apply. Branch cleanup is an irreversible HITL gate.
511
+ - **Behavior:** Resolves the task list via `spur task list --json` (filtered by `--feature`, `--since`, `--status`), resolves the executor (omission and explicit `--agent inline` tier-substitute identically with a warning per task 0687, `auto` tier-resolves, a name pins), emits a pre-dispatch notice naming the subprocess override — `execution surface: subprocess`, `reason: trigger 3 — durable auditable run record required`, `requested agent: <selector>`, `executor: <resolved>` — then builds `--vars '{"tasks":"[...]","agent":"<resolved>","profile":"interactive|auto","merge":"true|false"}'` and invokes the wrapup pipeline. The pipeline runs the same states as `wrap` — including the `doc-tripwire` hop (1037), whose TRUSTED CONFIG ONLY `docTripwireCmd` runs the repo-wide tripwires over the still-uncommitted wrap diff and can be overridden or disabled (empty string) via `--vars` — but with the full task list. Task and feature statuses are NOT mutated. Branch cleanup is an irreversible HITL gate.
512
512
  - **Vars string typing:** `tasks` is a JSON-encoded **string**, not a JSON array — `--vars` values must be strings (the CLI rejects raw arrays); `jq -nc` passes the array text through as a string value:
513
513
 
514
514
  ```bash
515
- VARS=$(jq -nc --arg tasks "$TASKS" --arg feature "$FEATURE" --arg agent "$AGENT" --arg profile "$PROFILE" --arg merge "$MERGE" \
516
- '{tasks:$tasks, feature:$feature, agent:$agent, profile:$profile, merge:$merge}')
515
+ VARS=$(jq -nc --arg tasks "$TASKS" --arg agent "$AGENT" --arg profile "$PROFILE" --arg merge "$MERGE" \
516
+ '{tasks:$tasks, agent:$agent, profile:$profile, merge:$merge}')
517
517
  spur workflow run wrapup-pipeline.yaml --vars "$VARS"
518
518
  ```
519
519
 
@@ -538,7 +538,7 @@ gate commands, pending questions and approval requirements.>
538
538
  fails the run when that file is empty or missing, and every model-bearing stage prompt treats
539
539
  it as the authoritative ask (R2).
540
540
  - **Backing:** `idea-pipeline.yaml` on the host executor (`vars.executor: host`, advanced by `spur workflow continue`) for omitted/`inline`, or `spur workflow run idea-pipeline.yaml --async` for `auto`/name.
541
- - **Behavior:** Builds vars from the table above and drives the idea pipeline. Flow: discovery → **idea-eval** (taste; reject → cancelled) → feature-create → ac-generate → feature-check → system-design (conditional) → design-approval (taste) → decompose → batch-create (`--skip-ready`) → ready-prepare (ready checklist per created task + ready-evidence sidecar, 0788) → handoff. STOPS at handoff — no task execution, no pipeline nesting. Headless runs use one `trace --follow`; cancellation is reported stopped only when `workflow cancel --json` returns `killed: true`.
541
+ - **Behavior:** Builds vars from the table above and drives the idea pipeline. Flow: discovery → **idea-eval** (taste; reject → cancelled) → feature-create → ac-generate → system-design (conditional) → design-approval (taste) → decompose → batch-create-run (`--skip-ready`) → ready-prepare (ready checklist per created task + ready-evidence sidecar, 0788) → handoff-finalize → handoff. STOPS at handoff — no task execution, no pipeline nesting. Headless runs use one `trace --follow`; cancellation is reported stopped only when `workflow cancel --json` returns `killed: true`.
542
542
  - **Delegation:** Host executor (this session answers each work item) by default; explicit executor selection uses the async workflow worker.
543
543
  - **Idea-evaluation gate:** After discovery, operator reviews `.spur/run/idea-eval-report.md` ([idea-evaluation.md](idea-evaluation.md)). Approve continues; reject/cancel → no feature. Under `--auto`, follows the report's recommendation (`drop` → cancelled; missing recommendation → pauses). Enhanced idea is a sidecar — `vars.idea` is not overwritten.
544
544
  - **Design package (`--skip-design` only):**
@@ -112,7 +112,8 @@ bun run apps/cli/src/index.ts feature check <id> --strict --json
112
112
  live tasks (a warning in `FeatureCheckService` until `done`); `--strict` elevates both to error,
113
113
  so they are reported verbatim but do **not** abort the batch; every other error finding (L1–L3
114
114
  structural, `L4.malformed-verdict-artifact`, `L4.uncovered-feature-scenario`) still aborts. The
115
- terminal feature-transition gate enforces scenario verification after the batch runs, so pre-run
115
+ feature-lifecycle done-boundary gate (reached via `spur feature sync <id>`, the forward hop from
116
+ task-pipeline's done close) enforces scenario verification after the batch runs, so pre-run
116
117
  unverified scenarios and incomplete tasks are transient, not defects (dogfood 2026-08-11,
117
118
  feature I2; 2026-10-08, feature H1). Extend the set by editing that constant, never this prose.
118
119
  - **Auto-repairs are reported.** Structural repairs applied by `--fix` are listed under
@@ -461,14 +462,12 @@ in next-router; this only maps status → primary TABLE A hop for recovery.
461
462
 
462
463
  ### 3.3c Feature-sync retry suppression (task 0411; 1004 R3)
463
464
 
464
- During a batch, the per-task `record` step and the wrap-up `feature-transition` step each invoke
465
- feature status sync. When a feature is L4-gate-blocked (e.g. not all linked tasks are `done`), the
466
- identical blocked proposal repeats on every call with no intervening input change. The service fixes
467
- this, not the engine.
465
+ During a batch, the per-task `record` step invokes feature status sync. When a feature is
466
+ L4-gate-blocked (e.g. not all linked tasks are `done`), the identical blocked proposal repeats on
467
+ every call with no intervening input change. The service fixes this, not the engine.
468
468
 
469
- Both `task-pipeline.yaml` (`record` step) and `wrapup-pipeline.yaml` (`feature-transition` step)
470
- invoke `spur feature sync <feature-id> --json` directly — retry suppression lives inside the
471
- `FeatureService.syncFeature` implementation:
469
+ `task-pipeline.yaml`'s `record` step invokes `spur feature sync <feature-id> --json` directly —
470
+ retry suppression lives inside the `FeatureService.syncFeature` implementation:
472
471
 
473
472
  1. On a **blocked** result (`gateBlocked` checked first — a partial hop can have `applied: true`
474
473
  while still gate-blocked — then an unapplied from≠to deferral), the service persists
@@ -483,7 +482,7 @@ invoke `spur feature sync <feature-id> --json` directly — retry suppression li
483
482
  never reads or writes the state.
484
483
 
485
484
  **Batch driver contract:** the orchestrator does **nothing extra** — the suppression lives inside
486
- the pipeline's `record` step and the wrap-up's `feature-transition` step. The driver still
485
+ the pipeline's `record` step. The driver still
487
486
  launches `task-pipeline.yaml` verbatim (R4.1). Suppression is transparent: the sync emits the same
488
487
  `FeatureSyncResult` JSON shape (`suppressed: true` added on replay), so downstream report logic is
489
488
  unchanged. The only observable difference is fewer redundant `feature sync` derivations.
@@ -650,7 +649,7 @@ Before launching a full `spur-check:full`:
650
649
  | R4.3 (`--agent` merged into per-task vars) | Step 3.2 |
651
650
  | R5.1 (orchestrator boundary) | "Zero engine code" preamble + Step 3 |
652
651
  | R5.2 (structured batch report) | Step 5 |
653
- | 0411 (bounded feature-sync retry suppression) | Step 3.3c — wrapper lives in pipeline `record` + wrap-up `feature-transition`; driver unchanged |
652
+ | 0411 (bounded feature-sync retry suppression) | Step 3.3c — wrapper lives in pipeline `record`; driver unchanged |
654
653
  | 0510 R2 (feature-derived strict preflight) | Step 1 — "Feature-derived strict preflight (R2, task 0510)" |
655
654
  | 0510 R5 (metadata-only host controller) | Step 3.4 + projected `task show` / trace snippets in Step 1, 2.3, 3.1 |
656
655
 
@@ -361,8 +361,7 @@ skipped persist-out (or the pre-removal assertion fired too late), verdicts rema
361
361
  from the task records — `spur task show <wbs> --json` carries the recorded verdict and its
362
362
  proof block, so a lost `<wbs>-verdict.json` is regenerated by re-recording or copying from
363
363
  the task store. Feature run/latest receipts are re-runnable, not recovered: rerun the
364
- feature-verification workflow in the invoking tree —
365
- `spur workflow run feature-verification.yaml --vars '{"featureId":"<id>"}'` — which rewrites
364
+ feature-verification pass in the invoking tree — `spur feature verify <id>` — which rewrites
366
365
  the receipt files it owns. Divergent files are never auto-overwritten; reconcile by hand.
367
366
 
368
367
  ```
@@ -208,10 +208,10 @@ the worktree directory before launching the loop.
208
208
  #### Evidence staging before the first task (create and reuse mode)
209
209
 
210
210
  `.spur/memory/evidence/` and `.spur/run/` are **untracked per-tree** planes, so a fresh worktree
211
- starts with neither while the invoking tree holds every earlier task's verdict. Both
212
- `preflightFeature` (`spur task wrapup resolve` → `spur feature check --strict --as done`
213
- with cwd = the worktree) and `runMetrics` resolve verdict artifacts relative to the tree they run
214
- in, so without this step an already-`done` linked task reads as missing evidence
211
+ starts with neither while the invoking tree holds every earlier task's verdict. Feature completion
212
+ checks (`spur feature check --strict --as done`) and wrap-up metrics resolve verdict artifacts relative
213
+ to the tree they run in; wrap-up task resolution itself checks task identity and terminal status only.
214
+ Without this step an already-`done` linked task reads as missing evidence
215
215
  (`L4.evidence-not-recoverable`, `L4.scenario-unverified`) and its metrics row degrades to
216
216
  `UNKNOWN`.
217
217
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: planning-workflow
3
- description: "Extracted section: the planning half — intake → feature create → AC generation → feature check gate → decomposition → batch-create gate → refine. The full step-by-step procedure for turning a vague description into a validated, decomposed feature."
3
+ description: "Extracted section: the planning half — intake → feature create → AC generation → decomposition → batch-create → refine. The full step-by-step procedure for turning a vague description into a validated, decomposed feature."
4
4
  see_also:
5
5
  - spur-dev
6
6
  - decomposition
@@ -13,18 +13,19 @@ see_also:
13
13
  vague description
14
14
  → intake (clarify scope, constraints — prompt work)
15
15
  → spur feature create … ; AC authored/generated (spur agent run, bdd templates)
16
- → GATE: spur feature check (BDD validator; loop until clean)
16
+ → ac-generate runs `spur feature check` (BDD validator; loop until clean)
17
17
  → decomposition (prompt work) → task-batch JSON
18
- → GATE: task-batch.schema.json + spur task batch-create (atomic: all-or-nothing)
18
+ → batch-create-run runs `task-batch.schema.json` + `spur task batch-create` (atomic: all-or-nothing)
19
19
  → design doc (conditional: seam heuristic under default; skip with --skip-design) → docs/design/<slug>.md + 04 index
20
20
  ```
21
21
 
22
22
 
23
23
  The planning half transforms a description into a validated feature file with acceptance
24
- criteria, then decomposes it into a batch of tasks. Two CLI gates make LLM regressions
25
- unable to corrupt the corpus: `spur feature check` validates the AC, and
24
+ criteria, then decomposes it into a batch of tasks. Two deterministic validation steps make LLM
25
+ regressions unable to corrupt the corpus: `spur feature check` validates the AC, and
26
26
  `task-batch.schema.json` validates the decomposition shape before `spur task batch-create`
27
- writes anything.
27
+ writes anything. Both run inside the pipeline (`ac-generate`, `batch-create-run`) and route on
28
+ their recorded result — neither is an operator gate.
28
29
 
29
30
  ## Step 1: Intake
30
31
 
@@ -128,7 +128,7 @@ Invoked when the operator has a loose idea and the destination itself is foggy.
128
128
 
129
129
  ### Work Through the Map
130
130
 
131
- Invoked when a map already exists (operator provides the feature ID). A ticket is **optional** — without one, pick the next frontier ticket, not the operator's preference. Pipeline runs: `references/pipeline-resolution.md`.
131
+ Invoked when a map already exists (operator provides the feature ID). A ticket is **optional** — without one, pick the next frontier ticket, not the operator's preference.
132
132
 
133
133
  0. **Worktree first.** Same rule as charting (`sp/wayfind-<wbs>-<short-id>`): steps 6 and 7 remove fog from the map, and the gate that checks the removal measures the branch, not the commit. The `wip` claim lands only in the worktree until the merge, so `/sp:dev-find-way` excludes tickets held by other active `dev-find-way` worktree markers when picking the frontier.
134
134
  1. **Load the map** — the feature description (the low-res view), not every task body. Read the destination and Decisions-so-far to orient.
@@ -146,6 +146,16 @@ Invoked when a map already exists (operator provides the feature ID). A ticket i
146
146
  conversation (`AskUserQuestion` where available) and recorded straight into **## Decisions so far**
147
147
  — no task is claimed, and it does not consume the session's one ticket.
148
148
 
149
+ **Boundary.** A resolution authors research/specification sections — it never implements, so
150
+ never invoke `/sp:dev-run` or `/sp:dev-runall` from one. Work from local reads and the `spur`
151
+ CLI; no network search, no MCP tools. Write Solution/Testing/Review with
152
+ `spur task update <wbs> --section <name> --from-file <path>`, keeping the existing Requirements
153
+ and AC.
154
+
155
+ **Evidence anchors.** Resolve every cited line for real — `grep -n "^## <wbs> " <evidence-file>`
156
+ — and never copy a line number from another ticket's example. Re-read each cited line before
157
+ finishing and confirm it names the requirement's subject.
158
+
149
159
  5. **Record the resolution.** Post the answer in the task body, then `spur task update <wbs> done`. Append one line to the map's **## Decisions so far**: `- [<WBS> <title>](path) — <one-line gist of the answer>`.
150
160
  6. **Graduate fog into new tickets.** Any fog the answer has made specifiable becomes fresh child tasks (create-then-wire). Clear each graduated patch from **### Not yet specified** so it lives only as its new ticket. Doing half of this — deleting the fog without creating the ticket — destroys the only record the work was ever identified; the `corpus.ungraduated-fog` sweep retired in 0775 — enforcement is review-time discipline.
151
161
  7. **Rule out mis-scoped tickets.** If the answer reveals a ticket sits beyond the destination, close it and add one line to **### Out of scope** (the gist + why it's out of scope, linking the closed ticket). A scope boundary is not a step on the route — it stays out of **## Decisions so far**.