@gobing-ai/spur 0.3.42 → 0.3.44

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 (79) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/templates/AGENTS.md +5 -0
  3. package/config/workflows/idea-pipeline.yaml +205 -33
  4. package/config/workflows/task-pipeline.yaml +11 -1
  5. package/package.json +9 -9
  6. package/plugins/sp/commands/dev-run.md +3 -3
  7. package/plugins/sp/commands/dev-runall.md +9 -4
  8. package/plugins/sp/commands/dev-wrap.md +21 -3
  9. package/plugins/sp/commands/dev-wrapall.md +21 -3
  10. package/plugins/sp/plugin.json +1 -1
  11. package/plugins/sp/skills/code-implementation/SKILL.md +27 -0
  12. package/plugins/sp/skills/issue-finding/SKILL.md +21 -3
  13. package/plugins/sp/skills/issue-finding/references/session-formats.md +30 -0
  14. package/plugins/sp/skills/next-router/references/routing-table.md +2 -2
  15. package/plugins/sp/skills/parallel-execution/references/dispatch-surface.md +9 -0
  16. package/plugins/sp/skills/spur-cli/SKILL.md +3 -0
  17. package/plugins/sp/skills/spur-cli/references/features/hierarchy-mece.md +8 -3
  18. package/plugins/sp/skills/spur-cli/references/tasks.md +25 -1
  19. package/plugins/sp/skills/spur-dev/SKILL.md +12 -4
  20. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +22 -6
  21. package/plugins/sp/skills/spur-dev/references/dev-operations.md +13 -13
  22. package/plugins/sp/skills/spur-dev/references/execution-batch.md +76 -4
  23. package/plugins/sp/skills/spur-dev/references/execution-workflow.md +4 -2
  24. package/plugins/sp/skills/spur-dev/references/flag-glossary.md +2 -2
  25. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +43 -11
  26. package/plugins/sp/skills/spur-dev/references/planning-workflow.md +53 -0
  27. package/spur.js +529 -290
  28. package/web/_astro/{BoardApp.8sc6Rb1t.js → BoardApp.BnhkWEOG.js} +99 -99
  29. package/web/_astro/BoardApp.CxOS4HSo.js +1 -0
  30. package/web/_astro/{TaskDetail.DOKJP-Kp.js → TaskDetail.B6yiT-U7.js} +1 -1
  31. package/web/_astro/{arc.CTqmqspU.js → arc.PbmgYm3_.js} +1 -1
  32. package/web/_astro/{architectureDiagram-3BPJPVTR.B0iBdehs.js → architectureDiagram-3BPJPVTR.BkOMSdmD.js} +1 -1
  33. package/web/_astro/{blockDiagram-GPEHLZMM.BHE5UFj5.js → blockDiagram-GPEHLZMM.BtomoUdy.js} +1 -1
  34. package/web/_astro/{c4Diagram-AAUBKEIU.DQApkgGM.js → c4Diagram-AAUBKEIU.UzrYwJnF.js} +1 -1
  35. package/web/_astro/channel.DRRsElzO.js +1 -0
  36. package/web/_astro/{chunk-2J33WTMH.fPrKLM8v.js → chunk-2J33WTMH.nNChLHkw.js} +1 -1
  37. package/web/_astro/{chunk-4BX2VUAB.DExvwBhn.js → chunk-4BX2VUAB.DLYAecPo.js} +1 -1
  38. package/web/_astro/{chunk-55IACEB6.EjNkjuD6.js → chunk-55IACEB6.t5bzuj1J.js} +1 -1
  39. package/web/_astro/{chunk-727SXJPM.Dl8xy5rT.js → chunk-727SXJPM.DBV62bIy.js} +1 -1
  40. package/web/_astro/{chunk-AQP2D5EJ.BSzMBgBs.js → chunk-AQP2D5EJ.BdaPhTPs.js} +1 -1
  41. package/web/_astro/{chunk-FMBD7UC4.CAeOHpbb.js → chunk-FMBD7UC4.eoZ88KMf.js} +1 -1
  42. package/web/_astro/{chunk-ND2GUHAM.Bbeu_ZOH.js → chunk-ND2GUHAM.QdNDyeSh.js} +1 -1
  43. package/web/_astro/{chunk-QZHKN3VN.Da_GaIAZ.js → chunk-QZHKN3VN.DPaToLfx.js} +1 -1
  44. package/web/_astro/{classDiagram-4FO5ZUOK.sMfFYSQp.js → classDiagram-4FO5ZUOK.By9BMe7b.js} +1 -1
  45. package/web/_astro/{classDiagram-v2-Q7XG4LA2.sMfFYSQp.js → classDiagram-v2-Q7XG4LA2.By9BMe7b.js} +1 -1
  46. package/web/_astro/{cose-bilkent-S5V4N54A.PS3IwOH4.js → cose-bilkent-S5V4N54A.DMypl6q_.js} +1 -1
  47. package/web/_astro/{dagre-BM42HDAG.EQCx8a6D.js → dagre-BM42HDAG.p26m9grg.js} +1 -1
  48. package/web/_astro/{diagram-2AECGRRQ.aEFQl8v8.js → diagram-2AECGRRQ.C6m3oTIu.js} +1 -1
  49. package/web/_astro/{diagram-5GNKFQAL.jyN4tqFX.js → diagram-5GNKFQAL.JCxivn2z.js} +1 -1
  50. package/web/_astro/{diagram-KO2AKTUF.DaTVHnme.js → diagram-KO2AKTUF.DifbEd3P.js} +1 -1
  51. package/web/_astro/{diagram-LMA3HP47.DWsGRLAN.js → diagram-LMA3HP47.Bffga-cf.js} +1 -1
  52. package/web/_astro/{diagram-OG6HWLK6.DYxmOViu.js → diagram-OG6HWLK6.CUKx5_Cx.js} +1 -1
  53. package/web/_astro/{erDiagram-TEJ5UH35.BKM75coa.js → erDiagram-TEJ5UH35.DaJ9KZR0.js} +1 -1
  54. package/web/_astro/{flowDiagram-I6XJVG4X.B7qpqDW5.js → flowDiagram-I6XJVG4X.Do6l5pzg.js} +1 -1
  55. package/web/_astro/{ganttDiagram-6RSMTGT7.CYBL-hwy.js → ganttDiagram-6RSMTGT7.DqBKgB-3.js} +1 -1
  56. package/web/_astro/{gitGraphDiagram-PVQCEYII.Cz4ly3Kq.js → gitGraphDiagram-PVQCEYII.B3VLSQ22.js} +1 -1
  57. package/web/_astro/{index.yAse9IaO.css → index.QfZ9SC3X.css} +1 -1
  58. package/web/_astro/{infoDiagram-5YYISTIA.CIS7cXrJ.js → infoDiagram-5YYISTIA.hK8ZC1oa.js} +1 -1
  59. package/web/_astro/{ishikawaDiagram-YF4QCWOH.DQ-pmaOG.js → ishikawaDiagram-YF4QCWOH.C8DXwh9Z.js} +1 -1
  60. package/web/_astro/{journeyDiagram-JHISSGLW.DVS6F7UM.js → journeyDiagram-JHISSGLW.mj5aHQDb.js} +1 -1
  61. package/web/_astro/{kanban-definition-UN3LZRKU.D8t-G-U6.js → kanban-definition-UN3LZRKU.Dwlhi49r.js} +1 -1
  62. package/web/_astro/{linear.IO8rTd_n.js → linear.7zBHdlwl.js} +1 -1
  63. package/web/_astro/{mermaid.core.CjVnvFNf.js → mermaid.core.AYdA0EJr.js} +4 -4
  64. package/web/_astro/{mindmap-definition-RKZ34NQL.DZoEMTnU.js → mindmap-definition-RKZ34NQL.BSnniHEq.js} +1 -1
  65. package/web/_astro/{pieDiagram-4H26LBE5.86r1QWnX.js → pieDiagram-4H26LBE5.B7EVpA3J.js} +1 -1
  66. package/web/_astro/{quadrantDiagram-W4KKPZXB.DzFA9uPk.js → quadrantDiagram-W4KKPZXB.rg4i8YOY.js} +1 -1
  67. package/web/_astro/{requirementDiagram-4Y6WPE33.dQvdvfMr.js → requirementDiagram-4Y6WPE33.C4pr792x.js} +1 -1
  68. package/web/_astro/{sankeyDiagram-5OEKKPKP.BvC-VNI8.js → sankeyDiagram-5OEKKPKP.DTXU996O.js} +1 -1
  69. package/web/_astro/{sequenceDiagram-3UESZ5HK.DDQNwHs8.js → sequenceDiagram-3UESZ5HK.D73DOjzi.js} +1 -1
  70. package/web/_astro/{stateDiagram-AJRCARHV.BKwYy-tQ.js → stateDiagram-AJRCARHV.CVkeSaEH.js} +1 -1
  71. package/web/_astro/{stateDiagram-v2-BHNVJYJU.KxCiRYYf.js → stateDiagram-v2-BHNVJYJU.DkBGwoN5.js} +1 -1
  72. package/web/_astro/{timeline-definition-PNZ67QCA.Bh9NDjOx.js → timeline-definition-PNZ67QCA.DnJ9Yh7G.js} +1 -1
  73. package/web/_astro/{vennDiagram-CIIHVFJN.DIM-n6us.js → vennDiagram-CIIHVFJN.DN1tvR4Y.js} +1 -1
  74. package/web/_astro/{wardley-L42UT6IY.DW15qOFQ.js → wardley-L42UT6IY.D54jJ-7-.js} +1 -1
  75. package/web/_astro/{wardleyDiagram-YWT4CUSO.UTIvjsG9.js → wardleyDiagram-YWT4CUSO.C2grUBbE.js} +1 -1
  76. package/web/_astro/{xychartDiagram-2RQKCTM6.CQYhkVDS.js → xychartDiagram-2RQKCTM6.D05okBnV.js} +1 -1
  77. package/web/index.html +2 -2
  78. package/web/_astro/BoardApp.ChKnY1JD.js +0 -1
  79. package/web/_astro/channel.B0q9WGrH.js +0 -1
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  description: Wrap up a batch of completed tasks — learnings, metrics, doc-sync, feature transition, optional branch cleanup
3
- argument-hint: "[--since <iso-date>] [--feature <id>] [--status <s>] [--auto] [--merge] [--dry-run]"
3
+ argument-hint: "[--since <iso-date>] [--feature <id>] [--status <s>] [--agent <inline|auto|name>] [--auto] [--merge] [--dry-run]"
4
4
  allowed-tools: ["Bash", "Read", "AskUserQuestion"]
5
5
  ---
6
6
 
@@ -15,6 +15,7 @@ Wraps the **wrapup-pipeline.yaml** workflow.
15
15
  | `--since` `<iso-date>` | Wrap tasks completed since a date. | configured |
16
16
  | `--feature` `<id>` | Wrap tasks in a feature. | omitted |
17
17
  | `--status` `<s>` | Only wrap tasks in a status. | done |
18
+ | `--agent` `<inline\|auto\|name>` | Who runs the wrap's model-bearing steps. Wrap is workflow-backed: omit/`inline` resolves to `agent.default` (objective trigger 3 — durable auditable run record required); `auto` tier-resolves an executor; a name pins that executor into `vars.agent`. | agent.default |
18
19
  | `--auto` | Skip objective HITL gates. | off |
19
20
  | `--merge` | Merge wrap branches. | off |
20
21
  | `--dry-run` | Render wraps without writing. | off |
@@ -23,11 +24,28 @@ For shared semantics, see the [flag glossary](../skills/spur-dev/references/flag
23
24
 
24
25
  ## Usage
25
26
 
26
- /sp:dev-wrapall [--since <iso-date>] [--feature <id>] [--status <s>] [--auto] [--merge] [--dry-run]
27
+ /sp:dev-wrapall [--since <iso-date>] [--feature <id>] [--status <s>] [--agent <inline|auto|name>] [--auto] [--merge] [--dry-run]
27
28
 
28
29
  ## Implementation
29
30
 
31
+ - Apply the [inline-default execution-surface contract](../skills/spur-dev/references/cross-cutting.md#inline-default-execution-surface).
32
+ - Wrap stays **workflow-backed** — `spur workflow run .spur/workflows/wrapup-pipeline.yaml` is the only implementation; there is no inline wrap driver.
33
+ - Resolve the executor **before** launching the workflow:
34
+ - `--agent <name>` → pass the name unchanged into `vars.agent`.
35
+ - `--agent auto` → tier-resolve a concrete executor first, then merge it into `vars.agent`.
36
+ - omit/`--agent inline` → resolve to `agent.default` (the documented headless resolution of `inline`).
37
+ - Emit a pre-dispatch notice naming the override before `spur workflow run`, exactly:
38
+ `execution surface: subprocess`, `reason: trigger 3 — durable auditable run record required`, `requested agent: inline|auto|<name>`, `executor: agent.default|<resolved-name>`.
39
+ - The wrap workflow still creates its durable run record — the notice reports the override, it does not change the workflow.
40
+
30
41
  ```bash
31
- spur workflow run .spur/workflows/wrapup-pipeline.yaml --vars '{"tasks":"<json-encoded-wbs-list>","feature":"<id|>","profile":"interactive|auto","merge":"true|false"}' [--dry-run]
42
+ AGENT=… # resolved above: agent.default for inline/omitted, tier-resolved for auto, unchanged for <name>
43
+ echo "execution surface: subprocess; reason: trigger 3 — durable auditable run record required; requested agent: <inline|auto|name>; executor: $AGENT"
44
+ VARS=$(jq -nc --arg tasks "$TASKS" --arg feature "$FEATURE" --arg agent "$AGENT" --arg profile "$PROFILE" --arg merge "$MERGE" \
45
+ '{tasks:$tasks, feature:$feature, agent:$agent, profile:$profile, merge:$merge}')
46
+ spur workflow run .spur/workflows/wrapup-pipeline.yaml --vars "$VARS" [--dry-run]
32
47
  ```
33
48
 
49
+ The executor resolution is described in the bullets above; the snippet's `AGENT` variable carries
50
+ the resolved name (`agent.default` for omit/`inline`).
51
+
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sp",
3
- "version": "0.3.42",
3
+ "version": "0.3.44",
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
  "extensions": {
6
6
  "pi": ["./hooks/pi/guard-extension.ts"]
@@ -92,6 +92,33 @@ only burns wall clock and context budget.
92
92
  - If a targeted probe reveals a failure you cannot fix within implement scope, note it in
93
93
  `## Solution` and let the `test` hop's fixall handle it — do not pre-empt the gate.
94
94
 
95
+ ## Changed-path targeted checks (dependency-aware verification, task 0510 R3)
96
+
97
+ "Run the narrow test first" needs a second half: *which* narrow tests, when a change to a shared
98
+ surface can break a downstream consumer. The matrix below is dependency-aware — domain → app →
99
+ CLI — so a shared change verifies its consumers without recreating the full project check inside
100
+ implement. **It augments narrow behavior tests; it never authorizes `bun run spur-check`,
101
+ `bun run test`, or another full project check inside implement** — the pipeline's `test` hop owns
102
+ that single full gate.
103
+
104
+ | Changed surface | Required targeted tests | Required typechecks |
105
+ | --- | --- | --- |
106
+ | `packages/domain/src/**` public type/query | affected domain test; affected app service test; affected CLI command test | `@gobing-ai/spur-domain`, `@gobing-ai/spur-app`, `@gobing-ai/spur` |
107
+ | `packages/app/src/**` public service/type | affected app test; affected CLI command test | `@gobing-ai/spur-app`, `@gobing-ai/spur` |
108
+ | `apps/cli/src/**` | affected `apps/cli/tests/**` file | `@gobing-ai/spur` |
109
+ | shared plugin flag/command/reference | affected plugin structure/contract test; add `flag-contract-parity.test.ts` **only** when the shared flag surface changes | no package typecheck unless TypeScript also changed |
110
+
111
+ - Apply **only the matching rows** for the surfaces actually changed; a multi-surface change applies
112
+ the union.
113
+ - Workspace typechecks run as `bun run --filter <workspace> typecheck` for each listed workspace
114
+ (monorepo; installed/other projects substitute their package-manager surface).
115
+ - The shared plugin row is deliberately conditional: a pure reference/prose change needs only the
116
+ affected structure test; `flag-contract-parity.test.ts` is added only when the shared flag surface
117
+ (a flag the plugin layer consumes) actually changes, so an unconditional parity suite does not
118
+ creep into every plugin edit.
119
+ - Run the applicable rows, then stop: the single full project check belongs to the pipeline's
120
+ `test` hop (`task-pipeline.yaml` `${vars.qualityGateCmd}`).
121
+
95
122
  ## Behavior
96
123
 
97
124
  This skill behaves as a **technique**: given a task (read its Background, AC, Design, Plan), it maps
@@ -169,9 +169,27 @@ count identical commands for loop detection.
169
169
  Produce per-session metrics (duration, tools, compactions, test runs, spur calls, guard failures,
170
170
  key finding). Aggregate totals across sessions.
171
171
 
172
- When `--use-history` is set, optionally run `spur history import --source <mapped> …` and
173
- `spur history analyze` for token/cost aggregates. Do **not** treat history ETL as a substitute
174
- for raw tool-loop evidence.
172
+ When `--use-history` is set, the **selected-file history bridge** supplies ETL aggregates for the
173
+ frozen session set (task 0507 R3):
174
+
175
+ 1. **Freeze Phase 1's selected OMP JSONL files once** — the same inventory the raw analysis reads.
176
+ Discovery roots: the normal OMP session root (`~/.omp/agent/sessions/`) **and**
177
+ `.spur/run/<run-id>/agent-sessions/<omp-executor>/*.jsonl` for workflow subprocess sessions.
178
+ Never import a broad `.spur/run` scan and never run a full/source-root reconciliation here.
179
+ 2. **Import each frozen file once, through the source-local CLI**, with single-file `force-file`
180
+ mode:
181
+ `bun run apps/cli/src/index.ts history import --source omp --file <absolute-file> --mode force-file --json`.
182
+ The importer derives the session key from the filename; use the same stem for analysis.
183
+ 3. **Analyze scoped to that key**: `history analyze --session <filename-stem> --json`.
184
+ 4. Use the artifact for the aggregates ETL can represent — tokens, cost, messages, tool calls,
185
+ loops, and assistant response duration. **Continue parsing the same raw files** for command text,
186
+ compactions, test/guard retries, tool execution duration/status/errors, and every other signal
187
+ the ETL does not carry.
188
+
189
+ ETL supplies normalized aggregates; it is **not** a substitute for raw tool-loop evidence. If an
190
+ import fails or the DB is empty, continue with raw logs and note that cost data is unavailable.
191
+ Before any ad-hoc verification SQL against `history_*` tables, follow the schema-first rule in
192
+ [references/session-formats.md](references/session-formats.md) — inspect the live schema once.
175
193
 
176
194
  ### Phase 3: IDENTIFY — Root Cause Ranking
177
195
 
@@ -104,6 +104,36 @@ For issue-finding:
104
104
  Import does not invent bottleneck categories. If import fails or the DB is empty, continue with
105
105
  raw logs and note that cost data is unavailable.
106
106
 
107
+ **Schema-first rule (task 0506 R3):** history tables are owned by
108
+ `@gobing-ai/ts-llm-jsonl-importer` and can change between versions — never copy column lists
109
+ into this skill. Before any ad-hoc verification SQL that references importer-owned `history_*`
110
+ tables, run **one** schema-introspection query for every table you need and compose the data
111
+ queries from that result:
112
+
113
+ ```bash
114
+ # one introspection pass, then write data queries against what it reports
115
+ sqlite3 <db> "SELECT name, sql FROM sqlite_schema WHERE type='table' AND name LIKE 'history_%';"
116
+ # (or a single `.schema history_<table>` invocation per referenced table)
117
+ ```
118
+
119
+ If a column you expected is absent, trust the live schema — do not guess. The importer's
120
+ `HISTORY_IMPORT_SCHEMA_SQL` is the authority; this skill holds no duplicate column contract.
121
+
122
+ **Selected-file bridge (task 0507 R3):** `--use-history` imports the frozen Phase-1 file set one
123
+ file at a time — never a root scan, never a full reconciliation. Ambient discovery covers the
124
+ normal OMP root (`~/.omp/agent/sessions/`) **and** workflow subprocess sessions under
125
+ `.spur/run/<run-id>/agent-sessions/<omp-executor>/*.jsonl` (same `type: "message"` envelope). For
126
+ each file, the session key is the JSONL filename stem (importer `sessionIdFromContext`); import and
127
+ analyze per key:
128
+
129
+ ```bash
130
+ bun run apps/cli/src/index.ts history import --source omp --file <absolute-file> --mode force-file --json
131
+ bun run apps/cli/src/index.ts history analyze --session <filename-stem> --json
132
+ ```
133
+
134
+ ETL owns token/cost/message/tool/loop/assistant-duration aggregates; raw JSONL stays authoritative
135
+ for command text, compactions, test/guard retries, and tool execution duration/status/errors.
136
+
107
137
  ## Edge cases
108
138
 
109
139
  | Scenario | Handling |
@@ -65,7 +65,7 @@ Statuses from `TASK_STATUSES`: backlog | todo | wip | testing | blocked | done |
65
65
  | A5 | `status == wip` (no usable checkpoint) | `task.show.status` | `/sp:dev-run <wbs> --mode implement --auto --next` | yes | **yes** → verify | Completes implement step then chain |
66
66
  | A6 | `status == testing` | `task.show.status` | `/sp:dev-verify <wbs> --auto --next` | yes | **yes** — verify `--next` → done (FSM + provenance guards) | On PARTIAL/FAIL → stop review-pending (do not force done) |
67
67
  | A7 | `status == blocked` | `task.show` + Notes/History for blocker text | `/sp:dev-handover "<blocker summary from Notes or status>"` | no | no | **STOP** after handover doc; human unblocks |
68
- | A8 | `status == done` | `task.show.status` | `/sp:dev-wrap <wbs>` | no | **no** (wrap's `--merge` is irreversible HITL; never auto) | Operator may re-invoke with `--merge` explicitly later |
68
+ | A8 | `status == done` | `task.show.status` | `/sp:dev-wrap <wbs>` (`--agent <value>` preserved when the originating command supplied it; omission remains omission) | no | **no** (wrap's `--merge` is irreversible HITL; never auto) | Operator may re-invoke with `--merge` explicitly later |
69
69
  | A9 | `status == cancelled` | `task.show.status` | *(none)* | no | no | **STOP** — no-op; print "cancelled — nothing to advance" |
70
70
 
71
71
  **Priority when multiple TABLE A rows could match:** lowest row number wins (A1–A9 are mutually
@@ -83,7 +83,7 @@ When input is a feature id:
83
83
  | B3 | Feature has ≥1 frontier task | task list under feature | **Recurse TABLE A** on chosen task | per A | per A | Frontier = open (`backlog`\|`todo`\|`wip`\|`testing`\|`blocked`), unblocked (all `dependencies[]` done), prefer WBS-ascending among `todo` then `backlog` then `wip` then `testing` then `blocked` |
84
84
  | B4 | No frontier tasks AND `feature.status == backlog` AND AC placeholder/invalid | `feature check` / AC body | `/sp:dev-plan` continuation is not WBS-shaped — **STOP** with: run `/sp:dev-plan --feature <id>` or fix AC then `spur feature check <id>` | no | no | Planning-half entry needs a description; do not invent idea text |
85
85
  | B5 | No frontier tasks AND feature has valid AC but zero tasks | feature + empty task roster | **STOP** with: `/sp:dev-plan --feature <id>` (decompose/batch-create) | no | no | Avoid auto-running plan without operator description confirmation in v1 |
86
- | B6 | No frontier tasks AND all child tasks `done` AND feature in `active`\|`verifying` | task list all done | `/sp:dev-wrapall --feature <id>` | no | no | Advances feature via wrapall guards; `--merge` never auto |
86
+ | B6 | No frontier tasks AND all child tasks `done` AND feature in `active`\|`verifying` | task list all done | `/sp:dev-wrapall --feature <id>` (`--agent <value>` preserved when the originating command supplied it; omission remains omission) | no | no | Advances feature via wrapall guards; `--merge` never auto |
87
87
  | B7 | No frontier tasks AND mixed cancelled/done only | statuses | **STOP** — print summary; suggest feature status update manually | no | no | |
88
88
  | B8 | `feature.status == blocked` | feature status | **STOP** — print feature blocked; do not pick tasks | no | no | |
89
89
 
@@ -89,6 +89,15 @@ non-sandboxed shell, or a workspace that owns the storage path). The upstream fi
89
89
  pi-coding-agent's storage-path resolution; it is out of scope here and recorded as motivating
90
90
  evidence only.
91
91
 
92
+ ## Composition with the inline pipeline driver (task 0508)
93
+
94
+ The interactive inline driver (`cross-cutting.md` § Inline-default execution surface) applies this
95
+ reference's default — native subagent first — to sequential `task-pipeline.yaml` `agent.run`
96
+ stages. Eligibility there is the same observable test (pure-slash action, non-interactive state,
97
+ native subagent with shared-worktree capability); the inline driver remains the authority for
98
+ provenance, artifact validation, and no-replay guarantees. This reference stays the authority for
99
+ the native-subagent versus `spur agent run` choice everywhere else.
100
+
92
101
  ## See also
93
102
 
94
103
  - **`parallel-execution`** SKILL.md - the dispatch disciplines this rule sits beside.
@@ -101,6 +101,9 @@ the whole point of this facade is that the CLI surface has a single, scalable ho
101
101
  prose checks here.
102
102
  - **Not a competency.** Design, implementation, testing, and review are competency skills, not CLI
103
103
  verbs — they are not documented here.
104
+ - **Not the orchestration owner.** The ADR-054 boundary: this facade owns CLI noun/verb/flag
105
+ semantics — including task and feature status-transition verbs — while multi-step lifecycle
106
+ orchestration belongs to `sp:spur-dev`.
104
107
 
105
108
  ## See also
106
109
 
@@ -117,15 +117,15 @@ If any check fails → **child of best parent** or **task under existing feature
117
117
  | --- | --- | --- |
118
118
  | Board module + UX slice as child | `F8` Features board → `F81` detail action group | **Good** — same product surface, finer grain |
119
119
  | Restructure tooling under Feature CLI | `F3` Feature management CLI → `F31` restructure kit | **Good** — not a new root letter (was root `S`, moved) |
120
- | Runner vs plugin integration | `B` Agent execution (`spur agent`) vs `H` Agent integration (`plugins/sp`) | **Keep separate** — Goals at `B_agent-execution.md:14` vs `H_agent-integration.md:14`; **reject merge on “agent” alone** |
120
+ | Runtime vs plugin harness | `B` Agent execution (`spur agent`) vs `I` sp plugin (`plugins/sp`) | **Keep separate** — B owns runner/process/session/executor behavior; I owns skills, commands, subagents, hooks, and orchestration guidance. H is frozen history. |
121
121
  | Observability UX as sibling roots | `J` board + `K` System Events table redesign + `L` payload enrichment | **Reparent K,L under J** — not body-merge; not peer roots (audit 0356) |
122
- | Plugin epics as new letters | `N` dev-next UX, `O` token architecture as roots beside `H` | **Reparent under H** — same plugin plane, finer grain |
122
+ | Plugin epics as new letters | Historical `N` dev-next UX and `O` token architecture | **Historical move under H is retained; new plugin work goes under I** — do not extend frozen H |
123
123
  | Workflow observability as board root | `P` workflow run observability as peer of `D` Workflows | **Reparent under D** — object is `spur workflow run`, not board J |
124
124
  | Planning validation as root | `Q` AC-verifiable gates as peer of `F` Planning | **Reparent under F** |
125
125
  | Status feedback as root | `R` feature status loop as peer of `F` | **Reparent under F** (corpus status is planning) |
126
126
  | CLI backbone vs board product | `G` Collaboration (message/team CLI) vs `M` Teams board | **Keep both** — different surfaces; do not merge |
127
127
  | One-off polish as root | New letter for “fix button loading” | **Bad** — task under existing feature |
128
- | Done historical epic as root | `I` sp plugin hands-off ready (`done`) | **Keep** as historical root OK; optional later neatness under H |
128
+ | Done plugin epic as root | `I1` sp plugin hands-off ready (`done`) | **Good after reparent** completed delivery under durable plugin root I |
129
129
 
130
130
  ### Audit 0356 snapshot (A–R dispositions)
131
131
 
@@ -144,6 +144,11 @@ Authoritative seed for restructure mapping lives in task 0356 Solution and
144
144
 
145
145
  **Rejected merges:** B∪H (name-only overlap); J∪K body-merge (use reparent for K/L).
146
146
 
147
+ **Ownership amendment (2026-08-11):** audit 0356 is a historical snapshot. The current active
148
+ boundary is B = runtime agent execution, I = `sp` plugin harness, H = frozen mixed history. The
149
+ applied map is `docs/plans/2026-08-11-sp-plugin-feature-tree-restructure-map.md`; do not place new
150
+ work under H.
151
+
147
152
  ---
148
153
 
149
154
  ## Checklist: before `spur feature create`
@@ -18,6 +18,21 @@ pipeline run) lives in **`sp:spur-dev`** — do not reimplement that loop here.
18
18
  *drive* a task through its lifecycle, reach for `sp:spur-dev`; when you need to know *which verb
19
19
  does what*, this skill.
20
20
 
21
+ ## WBS lookup fast path
22
+
23
+ Start from the WBS, not the corpus layout:
24
+
25
+ ```bash
26
+ spur task show <wbs> --json # metadata + full content + filePath
27
+ spur task path <wbs> --json # absolute path only
28
+ ```
29
+
30
+ Do not search `docs/tasks*` or guess `--folder` to locate a known WBS. `show` is the default when an
31
+ agent needs to read a task; use `path` only when another filesystem command needs the absolute path.
32
+ Both commands resolve across configured task folders. Add `--folder` only when deliberately limiting
33
+ the lookup to one non-default corpus. Capture `show` once per run and reuse its response instead of
34
+ re-reading or re-tokenizing the task.
35
+
21
36
  ## Verb map
22
37
 
23
38
  | Verb | Purpose | Key flags |
@@ -68,7 +83,9 @@ See [tasks/verbs.md](tasks/verbs.md) for the variant detail.
68
83
  exit code `3` and names the existing WBS — reuse it.
69
84
  - **`--dedupe-within <seconds>`** overrides the default window (e.g. `--dedupe-within 60`).
70
85
  - **`--allow-duplicate-name`** disables the guard entirely (explicit override).
71
- - Tasks created without `--feature` are never guarded (no collision scope).
86
+ - The guard applies to every create: feature-scoped tasks collide within the same
87
+ feature; unscoped tasks (no `--feature`) collide with other unscoped tasks of the
88
+ same title (their own collision scope).
72
89
  - With `--json`, a duplicate emits `{ ok: false, error: { code, message, existingWbs,
73
90
  existingName, attemptedName } }` for deterministic reuse.
74
91
 
@@ -217,6 +234,13 @@ spur task check --strict --json # elevate ALL warnings to failures
217
234
  spur task check 0040 --strict-core # the testing→done gate variant
218
235
  ```
219
236
 
237
+ **Folder resolution (task 0522):** a WBS-targeted check (`<wbs>` present, no `--folder`) resolves
238
+ the task across **all configured task folders** — the same resolution as `task show` / `task path` /
239
+ `task update` — so a task in an inactive configured folder is checked, not reported missing. An
240
+ explicit `--folder <path>` is normalized to an absolute path and restricts lookup to that single
241
+ directory (relative and absolute spellings are equivalent). Unscoped checks (no WBS) and
242
+ `task list` remain active-folder-only.
243
+
220
244
  `--json` emits the structured matrix — per-task findings (missing sections, broken feature edges,
221
245
  AC-coverage orphans via L4 traceability) keyed by WBS, plus a per-task `pass` verdict. **Query this,
222
246
  do not re-derive it**: parse the JSON to answer "which tasks are ready?", "what's blocking 0040?",
@@ -45,6 +45,10 @@ the lifecycle*; the competency skills know *how to do each job*; the CLI knows *
45
45
  The skill was decomposed **by function** (ADR-028): design, decomposition, implementation, testing,
46
46
  and verification each became a standalone competency skill, leaving this spine to orchestrate them.
47
47
 
48
+ **Ownership (ADR-054).** This spine owns multi-step lifecycle orchestration — intake, gates,
49
+ decomposition, pipeline runs, HITL pauses. CLI noun/verb/flag semantics, including
50
+ status-transition verbs, are the facade's (`sp:spur-cli`), never this skill's.
51
+
48
52
  **The competencies the spine dispatches:**
49
53
 
50
54
  | Unit of work | Competency skill |
@@ -154,13 +158,17 @@ CLI does.
154
158
  you ship corrupted corpus.
155
159
  2. **The pipeline, not you, writes results.** `## Testing` and `## Review` sections are
156
160
  filled by the pipeline's `record` step. Do not edit them directly during execution.
157
- 3. **Check before every write.** Run `spur task check <wbs> --json` to know what sections
161
+ 3. **Resolve task IDs through the CLI.** Read a known WBS with `spur task show <wbs> --json`; it
162
+ returns metadata, full content, and `filePath` across configured task folders. Use `spur task
163
+ path <wbs> --json` only when another tool needs the absolute path. Never search `docs/tasks*` or
164
+ guess `--folder`; reuse the first `show` response throughout the run.
165
+ 4. **Check before every write.** Run `spur task check <wbs> --json` to know what sections
158
166
  the task needs at its current status. Guessing produces matrix violations.
159
- 4. **AC titles are identity keys.** Renaming a scenario after tasks are created breaks
167
+ 5. **AC titles are identity keys.** Renaming a scenario after tasks are created breaks
160
168
  traceability edges. If you must rename, update the task's scenario references too.
161
- 5. **Batch-create is atomic.** A single schema violation rejects the entire batch. Validate
169
+ 6. **Batch-create is atomic.** A single schema violation rejects the entire batch. Validate
162
170
  locally against `task-batch.schema.json` before invoking the CLI.
163
- 6. **Two-halves seam.** The planning and execution halves share this skill today but are
171
+ 7. **Two-halves seam.** The planning and execution halves share this skill today but are
164
172
  designed to split cleanly. Keep new logic in one half or the other — never straddle the
165
173
  seam with cross-half dependencies.
166
174
 
@@ -41,7 +41,7 @@ in scripts and audit output but does not change the default.
41
41
 
42
42
  | Value | Who does the work | Derived surface |
43
43
  |---|---|---|
44
- | `inline` (default when omitted) | Whoever is running this session (interactive) or `agent.default` (headless) | Interactive: inline (host session); headless: subprocess of `agent.default` |
44
+ | `inline` (default when omitted) | Whoever is running this session (interactive) or `agent.default` (headless) | Interactive: inline host-controlled, eligible model stages may use a native subagent (0508); headless: subprocess of `agent.default` |
45
45
  | `auto` | Tier-resolved from the stage's `min_tier` + `fallback` | Subprocess — a tier-resolved executor pins a specific agent/model, which the host session cannot supply |
46
46
  | `<name>` (coding agent or configured executor) | That executor | Inline when it resolves to the current session's agent; subprocess otherwise |
47
47
 
@@ -105,11 +105,18 @@ meaningless.
105
105
  **Interactive task pipelines invert control into the host session (ADR-047 amendment).**
106
106
  `dev-run --mode full` and sequential `dev-runall` with omit/`inline` interpret the existing
107
107
  `task-pipeline.yaml` in the host session; they do not launch `spur workflow run` and never redirect
108
- silently to `agent.default`. Each inline model stage appends
109
- `stage <id> executed inline in session <session-id>` to its run log. `dev-plan` remains a workflow
110
- subprocess, as do `dev-run`/`dev-runall` with `--agent auto` or a name, parallel batches, and every
111
- headless `spur workflow run` / `spur agent run`. `dev-run --mode implement` continues to run its
112
- single competency in-session under omit/`inline`.
108
+ silently to `agent.default`. Interactive inline is **host-controlled and non-subprocess**, but no
109
+ longer guarantees host-context execution for every model stage (task 0508): an eligible `agent.run`
110
+ stage pure-slash input, non-interactive state, native subagent with shared-worktree
111
+ read/write/shell capability dispatches **once** to that native subagent and joins before the
112
+ driver continues; any pre-dispatch eligibility failure falls back to one host execution, and a
113
+ failure after dispatch follows the stage's error policy with no automatic host replay. Operator
114
+ confirmation actions, `pause: true`, and approve/taste/ask decisions stay host-owned. Each inline
115
+ model stage appends `stage <id> executed inline in session <session-id>` to its run log; a
116
+ subagent-dispatched stage appends `stage <id> executed via subagent <agent-id> (host session
117
+ <session-id>)` instead. `dev-plan` remains a workflow subprocess, as do `dev-run`/`dev-runall` with
118
+ `--agent auto` or a name, parallel batches, and every headless `spur workflow run` / `spur agent
119
+ run`. `dev-run --mode implement` continues to run its single competency in-session under omit/`inline`.
113
120
 
114
121
  ### Executor precedence chain (R7)
115
122
 
@@ -429,6 +436,15 @@ iterating one task).
429
436
  2. Loop on that narrow target until green.
430
437
  3. **Then** run the single full `spur-check` (or `bun run check`) as the final gate.
431
438
 
439
+ **Dependency-aware selection (task 0510 R3).** "Narrow" is not "whatever file I touched" — a change
440
+ to a shared surface must also verify its downstream consumers. Pick the targeted tests and
441
+ typechecks from the **changed-path matrix** in `code-implementation/SKILL.md` (§ Changed-path
442
+ targeted checks): domain changes run affected domain + app/CLI consumer tests and
443
+ domain/app/CLI typechecks; app changes run affected app + CLI tests and app/CLI typechecks; CLI
444
+ changes run affected CLI tests and the CLI typecheck; shared plugin flag/command contract changes
445
+ run their focused structure/parity tests. Run only the applicable rows, then stop — the full
446
+ project check is still the pipeline's single final gate, never a per-iteration re-run.
447
+
432
448
  Do not re-run the full suite per iteration, and do not `spur-check` before you have a green narrow
433
449
  target. **Target:** full `spur-check` runs ≤2 per task (one during iteration, one final) instead of
434
450
  4 across a chain.
@@ -77,8 +77,8 @@ each would be scope creep for one-liner procedures.
77
77
  | 12 | brainstorm | `dev-brainstorm` | `Skill()` | `sp:brainstorm` (`dev-brainstorm`) | `<topic> [--depth <basic\|detailed\|comprehensive>] [--options <n>] [--agent <inline\|auto\|name>] [--skip-discovery] [--wayfind] [--task [<feature-id>]] [--feature [<parent-id>]] [--next]` |
78
78
  | 13 | runall | `dev-runall` | `Skill()` → agent | `sp:spur-dev` (`runall`) → `sp:super-planner` | `--tasks <selector> [--feature <id>] [--mode <sequential\|parallel>] [--keep-going] [--auto] [--agent <inline\|auto\|name>] [--json] [--wrap] [--next] [--continue] [--worktree [<name>]]` |
79
79
  | 13a | parallel | `dev-parallel` | `Skill()` | `sp:parallel-execution` | `--tasks <selector> [--feature <id>] [--mode <fan-out\|review-panel\|investigation>] [--agent <inline\|auto\|name>] [--json]` |
80
- | 14 | wrap | `dev-wrap` | `Skill()` | `spur workflow run` (wrapup-pipeline) | `<wbs> [--auto] [--merge] [--dry-run]` |
81
- | 15 | wrapall | `dev-wrapall` | `Skill()` | `spur workflow run` (wrapup-pipeline) | `[--since <iso>] [--feature <id>] [--status <s>] [--auto] [--merge] [--dry-run]` |
80
+ | 14 | wrap | `dev-wrap` | `Skill()` | `spur workflow run` (wrapup-pipeline) | `<wbs> [--agent <inline\|auto\|name>] [--auto] [--merge] [--dry-run]` |
81
+ | 15 | wrapall | `dev-wrapall` | `Skill()` | `spur workflow run` (wrapup-pipeline) | `[--since <iso>] [--feature <id>] [--status <s>] [--agent <inline\|auto\|name>] [--auto] [--merge] [--dry-run]` |
82
82
  | 16 | idea | `dev-idea` | `Skill()` | `spur workflow run` (idea-pipeline) | `"<idea>" [--auto] [--skip-design] [--approve-taste]` |
83
83
 
84
84
  ## Skill-backed operations
@@ -137,10 +137,10 @@ must not be changed without updating the backing skill.
137
137
  ### 4. run
138
138
 
139
139
  - **Purpose:** Run a task through the execution pipeline (full) or execute a single pipeline step (implement).
140
- - **Inputs:** `<wbs>` (required). `--mode <full|implement>` selects the execution mode. `implement` invokes `sp:code-implementation` inline by default. Interactive `full` with omit/`--agent inline` reads `task-pipeline.yaml` and drives its actions/guards in the host session; `--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.
140
+ - **Inputs:** `<wbs>` (required). `--mode <full|implement>` selects the execution mode. `implement` invokes `sp:code-implementation` inline by default. Interactive `full` with omit/`--agent inline` reads `task-pipeline.yaml` and drives its actions/guards in the host session — host-controlled and non-subprocess, with eligible `agent.run` stages dispatching once to a native subagent and host fallback (task 0508); `--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. `--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.
141
141
  - **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).
142
142
  - **Modes:**
143
- - **`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); explicit/headless executor selection invokes `spur workflow run .spur/workflows/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.
143
+ - **`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 .spur/workflows/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.
144
144
  - **`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".
145
145
  - **Delegation:** `Skill(skill="sp:spur-dev", args="run-inline $ARGUMENTS")` for interactive full omit/inline; `Skill(skill="sp:spur-dev", args="run $ARGUMENTS")` for explicit/headless full mode; `Skill(skill="sp:code-implementation", args="$ARGUMENTS")` for implement mode.
146
146
 
@@ -288,7 +288,7 @@ must not be changed without updating the backing skill.
288
288
  ### 13. runall
289
289
 
290
290
  - **Purpose:** Run a batch of tasks through their pipelines in dependency-correct order — resolve a set, topo-sort, run each via `task-pipeline.yaml`, inspect verdicts, apply the failure policy, emit a batch report.
291
- - **Inputs:** `--tasks <selector>` (required — explicit WBS list, status pseudo-list, `feature:<id>`, or `ready`). `--mode <sequential|parallel>` (default `sequential`; `parallel` fans out a proven-independent subset per `execution-batch.md` § Parallel Execution). `--keep-going` skips a failed task's in-batch dependents and continues independents (default halts on first failure). `--auto` sets `profile=auto` on each per-task run (skips the HITL approve gate). The orchestrator loop itself continues in this session; each full per-task workflow's `agent.run` stages always dispatch a subprocess. `--agent <inline|auto|name>` pins the executor for the per-task stages (see [SSOT](cross-cutting.md#inline-default-execution-surface)); the value crosses into per-task `vars.agent`, not the orchestrator. `--json` emits the report as JSON. `--wrap` triggers `wrapup-pipeline.yaml` after the batch completes, `--next` chains each task to terminal status then runs the wrap hop **once for the batch**, `--continue` resumes from checkpoint.
291
+ - **Inputs:** `--tasks <selector>` (required — explicit WBS list, status pseudo-list, `feature:<id>`, or `ready`). `--mode <sequential|parallel>` (default `sequential`; `parallel` fans out a proven-independent subset per `execution-batch.md` § Parallel Execution). `--keep-going` skips a failed task's in-batch dependents and continues independents (default halts on first failure). `--auto` sets `profile=auto` on each per-task run (skips the HITL approve gate). `--feature <id>` is sugar for `--tasks feature:<id>`; when the effective selector is feature-derived, the batch runs `spur feature check <id> --strict --json` **once** before task resolution — a non-zero strict check aborts with verdict `aborted`, zero attempted tasks, and the structured findings (task 0510 R2); scoped: `L4.scenario-unverified` (expected pre-run state of any not-yet-run feature) is reported verbatim but does not abort — any other strict error aborts. Explicit WBS/status/`ready` selectors add no feature check. The orchestrator loop itself continues in this session; interactive sequential omit/`inline` uses the host driver — host-controlled with eligible `agent.run` stages dispatching once to a native subagent and host fallback (task 0508) — while `--agent auto`/a name, parallel mode, and headless invocation keep the isolated per-task workflow subprocess boundary. `--agent <inline|auto|name>` pins the executor for the per-task stages (see [SSOT](cross-cutting.md#inline-default-execution-surface)); the value crosses into per-task `vars.agent`, not the orchestrator. `--json` emits the report as JSON. `--wrap` triggers `wrapup-pipeline.yaml` after the batch completes, `--next` chains each task to terminal status then runs the wrap hop **once for the batch**, `--continue` resumes from checkpoint.
292
292
  - **Three orthogonal axes (do not confuse):** `--keep-going` = batch failure policy (halt vs skip dependents); `--continue` = resume from checkpoint (pick up an interrupted batch); `--next` = per-task lifecycle chaining (advance status on a verdict — `dev-verify`/`dev-verifyall` only). `routing-table.md` offers `--continue` and `--next` as competing options for the same situation only when the batch was interrupted mid-run; otherwise they address different problems.
293
293
  - **Backing:** `sp:spur-dev` skill, `runall` operation → delegates the driver loop to the **`sp:super-planner`** agent (the batch orchestrator).
294
294
  - **Behavior:** The orchestrator reads [execution-batch.md](execution-batch.md) and drives: resolve selector → freeze set → topo-sort by `dependencies[]` (Kahn, WBS-ascending tie-break; cycle aborts) → resolve out-of-set deps by status (done → allow, else → block subtree) → run each task via `spur workflow run task-pipeline.yaml --async` + `spur workflow trace` polling → inspect terminal state + `.spur/run/<wbs>-verdict.json` → stop-the-batch default or `--keep-going` subtree skip → emit batch report. Per-task pipeline is invoked **verbatim** — no new FSM, no step edits. `--auto`/`--agent` are the only flags that cross the orchestrator→pipeline boundary (both into per-task `--vars`).
@@ -297,14 +297,14 @@ must not be changed without updating the backing skill.
297
297
  ### 14. wrap
298
298
 
299
299
  - **Purpose:** Wrap up a single completed task — capture learnings, record metrics, sync docs, and optionally advance the feature / clean up the branch.
300
- - **Inputs:** `<wbs>` (required, positional). `--auto` skips objective confirmations (the branch-cleanup HITL gate still pauses — irreversible). `--merge` triggers branch cleanup (irreversible HITL gate).
300
+ - **Inputs:** `<wbs>` (required, positional). `--agent <inline|auto|name>` names the wrap's model-bearing executor (default: `agent.default`); wrap is workflow-backed, so omit/`inline` resolves to `agent.default` under objective trigger 3 (durable auditable run record required), `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).
301
301
  - **Backing:** `spur workflow run .spur/workflows/wrapup-pipeline.yaml` — direct workflow invocation (no backing skill; the pipeline IS the procedure).
302
- - **Behavior:** Builds `--vars '{"tasks":"[\"<wbs>\"]","profile":"interactive|auto","merge":"true|false"}'` and invokes the wrapup pipeline. The pipeline runs: task-resolve → doc-sync → learning-capture → metrics-record → (feature-transition) → (branch-cleanup) → done. Task statuses are NOT mutated. Branch cleanup is an irreversible HITL gate that always pauses, even under `--auto`.
302
+ - **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 → learning-capture → metrics-record → (feature-transition) → (branch-cleanup) → done. Task statuses are NOT mutated. Branch cleanup is an irreversible HITL gate that always pauses, even under `--auto`.
303
303
  - **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:
304
304
 
305
305
  ```bash
306
- VARS=$(jq -nc --arg tasks "[\"$WBS\"]" --arg profile "$PROFILE" --arg merge "$MERGE" \
307
- '{tasks:$tasks, profile:$profile, merge:$merge}')
306
+ VARS=$(jq -nc --arg tasks "[\"$WBS\"]" --arg agent "$AGENT" --arg profile "$PROFILE" --arg merge "$MERGE" \
307
+ '{tasks:$tasks, agent:$agent, profile:$profile, merge:$merge}')
308
308
  spur workflow run .spur/workflows/wrapup-pipeline.yaml --vars "$VARS"
309
309
  ```
310
310
 
@@ -313,14 +313,14 @@ must not be changed without updating the backing skill.
313
313
  ### 15. wrapall
314
314
 
315
315
  - **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.
316
- - **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. `--auto` skips objective confirmations. `--merge` triggers branch cleanup (irreversible HITL gate).
316
+ - **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: `agent.default`); wrap is workflow-backed, so omit/`inline` resolves to `agent.default` under objective trigger 3 (durable auditable run record required), `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).
317
317
  - **Backing:** `spur workflow run .spur/workflows/wrapup-pipeline.yaml` — direct workflow invocation.
318
- - **Behavior:** Resolves the task list via `spur task list --json` (filtered by `--feature`, `--since`, `--status`), builds `--vars '{"tasks":"[...]","feature":"<id>","profile":"interactive|auto","merge":"true|false"}'`, and invokes the wrapup pipeline. The pipeline runs the same states as `wrap` 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.
318
+ - **Behavior:** Resolves the task list via `spur task list --json` (filtered by `--feature`, `--since`, `--status`), 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":"[...]","feature":"<id>","agent":"<resolved>","profile":"interactive|auto","merge":"true|false"}'` and invokes the wrapup pipeline. The pipeline runs the same states as `wrap` 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.
319
319
  - **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:
320
320
 
321
321
  ```bash
322
- VARS=$(jq -nc --arg tasks "$TASKS" --arg feature "$FEATURE" --arg profile "$PROFILE" --arg merge "$MERGE" \
323
- '{tasks:$tasks, feature:$feature, profile:$profile, merge:$merge}')
322
+ VARS=$(jq -nc --arg tasks "$TASKS" --arg feature "$FEATURE" --arg agent "$AGENT" --arg profile "$PROFILE" --arg merge "$MERGE" \
323
+ '{tasks:$tasks, feature:$feature, agent:$agent, profile:$profile, merge:$merge}')
324
324
  spur workflow run .spur/workflows/wrapup-pipeline.yaml --vars "$VARS"
325
325
  ```
326
326
 
@@ -64,6 +64,39 @@ function normalizeArgs(raw: Args): Args {
64
64
  - If `--feature FOO` is present and `--tasks` is absent, treat the effective selector as `feature:FOO`.
65
65
  - If both are present, `--tasks` wins (with a one-line note in the batch report).
66
66
 
67
+ **Feature-derived strict preflight (R2, task 0510).** After normalization, if the **effective
68
+ selector** is `feature:<id>` (whether via `--tasks feature:<id>` or the `--feature <id>` sugar),
69
+ run a source-local strict feature check **once** before any task-list resolution, freeze,
70
+ dependency resolution, or worktree task execution:
71
+
72
+ ```bash
73
+ # monorepo source; installed projects use their resolved `spur` binary
74
+ bun run apps/cli/src/index.ts feature check <id> --strict --json
75
+ ```
76
+
77
+ - **Abort shape.** A non-zero check aborts the batch immediately: verdict `aborted`, zero attempted
78
+ tasks, and the structured feature findings (the `--json` finding list) reported verbatim. This is
79
+ the same abort vocabulary as cycle / unknown selector (Step 4/Step 5). **Scoped to structural
80
+ findings:** `L4.scenario-unverified` is the expected state of any not-yet-run feature (its
81
+ covering tasks have no PASS verdicts yet) and `--strict` elevates it to error, so it is reported
82
+ verbatim but does **not** abort the batch; every other error finding (L1–L3 structural,
83
+ `L4.malformed-verdict-artifact`, `L4.uncovered-feature-scenario`) still aborts. The terminal
84
+ feature-transition gate enforces scenario verification after the batch runs, so pre-run unverified
85
+ scenarios are transient, not defects (dogfood 2026-08-11, feature I2).
86
+ - **Exactly once.** The check runs once per batch, before `task list`; it is not re-run per task.
87
+ - **Non-feature exclusion.** Explicit WBS lists, status pseudo-lists, and `ready` selectors add no
88
+ feature check — only an effective `feature:<id>` selector is feature-derived. When explicit
89
+ `--tasks` overrides `--feature`, the effective selector is not feature-derived, so no check runs.
90
+ - **Why.** `FeatureCheckService` emits `L3.scope-delineation` (Scope lacking an In/Out split) as a
91
+ **warning**; feature-scoped batches never ran a strict check before freezing, so the late feature
92
+ transition became the first blocking check. A selector-local strict preflight catches a known
93
+ strict finding before any task pipeline action without changing corpus-wide severity. A not-yet-run
94
+ feature always fails `L4.scenario-unverified` under `--strict`, so that class is
95
+ reported-not-aborting per the Abort shape above — the preflight targets defects, not the
96
+ expected pre-run state.
97
+ - **Scope.** This preflight is advisory to severity policy: it does not alter `FeatureCheckService`,
98
+ `L3.scope-delineation` severity, `feature sync`, or batch-create.
99
+
67
100
  `--tasks <value>` (or the effective value after normalization) resolves to a frozen set of task WBS numbers. Resolution happens **once, at
68
101
  kickoff** — the driver never re-queries `spur task list` to recompute membership mid-batch (R2.1).
69
102
 
@@ -71,7 +104,7 @@ kickoff** — the driver never re-queries `spur task list` to recompute membersh
71
104
  |---|---|---|
72
105
  | Explicit WBS list | `^[0-9, ]+$` | Split on comma; validate each token is a 4-digit WBS; collect the explicit set. (R1.1) |
73
106
  | `feature:<id>` (via `--tasks` or `--feature <id>`) | literal `feature:` prefix or `--feature` flag | `spur task list --feature <id> --json`; collect `wbs` from each row. The `--feature` flag is sugar that becomes `--tasks feature:<id>` at the command layer. (R1.3) |
74
- | `ready` | literal `ready` | Resolve the union of `spur task list --status todo --json` + `spur task list --status backlog --json`, drop tasks with open children (R1.5, umbrella-parent exclusion below), then keep only tasks whose every `dependencies[]` entry resolves to `status == done` (via `spur task show <dep> --json`). Report each excluded task with its unmet dependency. (R1.4) |
107
+ | `ready` | literal `ready` | Resolve the union of `spur task list --status todo --json` + `spur task list --status backlog --json`, drop tasks with open children (R1.5, umbrella-parent exclusion below), then keep only tasks whose every `dependencies[]` entry resolves to `status == done` (via `spur task show <dep> --json | jq '{wbs, status, dependencies, feature_id}'` — R5 metadata-only). Report each excluded task with its unmet dependency. (R1.4) |
75
108
  | Status pseudo-list | `todo` \| `backlog` \| `wip` \| `blocked` \| `testing` | `spur task list --status <value> --json`; collect `wbs` from each row. (R1.2) |
76
109
  | *(else)* | no match | Error: "unknown selector `<value>`" — list the valid forms and halt before running anything. |
77
110
 
@@ -113,7 +146,7 @@ edge `A → B` means "A depends on B" (B must complete before A runs). Only edge
113
146
  ### 2.3 Out-of-set dependency resolution
114
147
 
115
148
  For each dependency edge to a task **outside** the frozen set, resolve its current status via
116
- `spur task show <dep-wbs> --json`:
149
+ `spur task show <dep-wbs> --json | jq '{wbs, status, dependencies, feature_id}'` (R5 metadata-only):
117
150
 
118
151
  - status `done` → edge satisfied, drop it from the graph (R2.5). The dependent is unblocked.
119
152
  - status ≠ `done` → mark the dependent **blocked**. Transitively mark its in-set descendants blocked
@@ -211,7 +244,7 @@ step.
211
244
  ```bash
212
245
  RUN=$(spur workflow run .spur/workflows/task-pipeline.yaml \
213
246
  --vars '{"wbs":"<wbs>","profile":"auto","agent":"claude"}' --async --json | jq -r '.runId')
214
- spur workflow trace "$RUN" --json # poll until status is terminal (done/failed)
247
+ spur workflow trace "$RUN" --json | jq '{runId, status, terminalState}' # poll until status is terminal (done/failed)
215
248
  ```
216
249
 
217
250
  ### 3.2 Flag → `--vars` passthrough (R4.2, R4.3)
@@ -262,6 +295,7 @@ in next-router; this only maps status → primary TABLE A hop for recovery.
262
295
 
263
296
  ### 3.3c Bounded feature-sync retry suppression (task 0411)
264
297
 
298
+
265
299
  During a batch, the per-task `record` step and the wrap-up `feature-transition` step each invoke
266
300
  feature status sync. When a feature is L4-gate-blocked (e.g. not all linked tasks are `done`), the
267
301
  identical blocked proposal repeats on every call with no intervening input change — in the H9
@@ -295,6 +329,42 @@ pipeline's `record` step and the wrap-up's `feature-transition` step. The driver
295
329
  The only observable difference is fewer redundant `feature sync` invocations and a one-line
296
330
  `feature-sync-bounded:` annotation on stderr when a duplicate is suppressed.
297
331
 
332
+ ### 3.4 Metadata-only host controller (R5, task 0510)
333
+
334
+ The batch **orchestrator** reads status, ordering, and terminal state — never task bodies or trace
335
+ output. Task `content`, section bodies (Solution/Testing/Review), and full workflow `output` are
336
+ stage/subagent data and must not enter the host context on the green path; the controller that
337
+ dispatches native subagents must not defeat that isolation by ingesting the very bodies the
338
+ subagents are meant to hold (task 0508's dispatch contract is preserved unchanged).
339
+
340
+ **Green-path projections — every controller-side read is projected to metadata:**
341
+
342
+ - `task show --json` reads pipe to `{wbs, status, dependencies, feature_id}` only:
343
+
344
+ ```bash
345
+ spur task show <wbs> --json | jq '{wbs, status, dependencies, feature_id}'
346
+ ```
347
+
348
+ Use this shape for out-of-set dependency resolution (Step 2.3), the `ready` selector's
349
+ dep-status lookups (Step 1), and any other controller-side `task show`. A status-only lookup may
350
+ narrow further (`| jq '.status'`), but never widen.
351
+
352
+ - Green-path trace observation projects to `{runId, status, terminalState}` only:
353
+
354
+ ```bash
355
+ spur workflow trace "$RUN" --json | jq '{runId, status, terminalState}'
356
+ ```
357
+
358
+ The controller decides continue/halt from `status`/`terminalState` (ADR-044: judge a run by
359
+ `status === 'done'`, never by string-matching a `finalState` name) plus the bounded verdict
360
+ artifact `.spur/run/<wbs>-verdict.json`. It never streams or re-reads a full trace merely to
361
+ summarize status.
362
+
363
+ **Failure-path reads are bounded.** On a failed/blocked task, request only the terminal error and
364
+ the minimal anchor set the batch report needs (e.g. the blocking finding line, the unmet-dep WBS,
365
+ the verdict line) — never the entire trace. If a fuller trace is needed for diagnosis, that read
366
+ belongs to a subagent or the operator, not to the batch controller's report loop.
367
+
298
368
  ## Step 4 — Failure policy (R3)
299
369
 
300
370
  ### 4.1 Stop-the-batch (default) (R3.1)
@@ -611,7 +681,7 @@ Resume, merge, or discard:
611
681
  discard: git worktree remove <worktree-path> && git branch -D <branch>
612
682
  ```
613
683
 
614
- The report reuses the [`--next` chain contract](flag-glossary.md#-next-chain-contract) halt-report
684
+ The report reuses the [`--next` chain contract](flag-glossary.md#--next-chain-contract) halt-report
615
685
  shape (halt cause + where + why), not new vocabulary. Retention is the right default: these batches
616
686
  are long and already resumable via `--continue`; auto-deleting is data loss, auto-merging is a
617
687
  partial result presented as a whole. The answer to "what happens if it fails" is "nothing happens,
@@ -683,6 +753,8 @@ command doc so it does not read as a bug.
683
753
  | R5.1 (orchestrator boundary) | "Zero engine code" preamble + Step 3 |
684
754
  | R5.2 (structured batch report) | Step 5 |
685
755
  | 0411 (bounded feature-sync retry suppression) | Step 3.3c — wrapper lives in pipeline `record` + wrap-up `feature-transition`; driver unchanged |
756
+ | 0510 R2 (feature-derived strict preflight) | Step 1 — "Feature-derived strict preflight (R2, task 0510)" |
757
+ | 0510 R5 (metadata-only host controller) | Step 3.4 + projected `task show` / trace snippets in Step 1, 2.3, 3.1 |
686
758
 
687
759
  ## Parallel Execution
688
760