@gobing-ai/spur 0.3.58 → 0.3.61

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 (93) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/config.example.yaml +2 -2
  3. package/config/config.global.yaml +137 -0
  4. package/config/corpus-baseline.json +96 -8
  5. package/config/workflow-composition-baseline.json +1 -1
  6. package/config/workflows/wayfinder-resolution.yaml +4 -6
  7. package/package.json +9 -9
  8. package/plugins/README.md +5 -4
  9. package/plugins/sp/README.md +3 -3
  10. package/plugins/sp/agents/super-planner.md +1 -1
  11. package/plugins/sp/commands/dev-pr-review.md +1 -1
  12. package/plugins/sp/commands/dev-wrap.md +2 -2
  13. package/plugins/sp/commands/dev-wrapall.md +2 -2
  14. package/plugins/sp/plugin.json +1 -1
  15. package/plugins/sp/references/roles.md +12 -9
  16. package/plugins/sp/scripts/surface-drift-inventory.ts +51 -17
  17. package/plugins/sp/scripts/validate-commands.ts +11 -4
  18. package/plugins/sp/skills/code-verification/SKILL.md +1 -1
  19. package/plugins/sp/skills/code-verification/references/verdict-schema.md +1 -1
  20. package/plugins/sp/skills/issue-finding/SKILL.md +1 -1
  21. package/plugins/sp/skills/next-router/references/routing-table.md +1 -1
  22. package/plugins/sp/skills/pr-reviewing/SKILL.md +2 -2
  23. package/plugins/sp/skills/spec-decomposition/references/decomposition.md +1 -1
  24. package/plugins/sp/skills/spur-cli/references/init.md +1 -1
  25. package/plugins/sp/skills/spur-cli/references/tasks/l3-guard-cheatsheet.md +1 -1
  26. package/plugins/sp/skills/spur-cli/references/workflows/operations.md +1 -1
  27. package/plugins/sp/skills/spur-cli/references/workflows.md +4 -4
  28. package/plugins/sp/skills/spur-dev/SKILL.md +4 -4
  29. package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +4 -4
  30. package/plugins/sp/skills/spur-dev/references/dev-operations.md +9 -9
  31. package/plugins/sp/skills/spur-dev/references/done-housekeeping.md +1 -1
  32. package/plugins/sp/skills/spur-dev/references/execution-batch.md +3 -3
  33. package/plugins/sp/skills/spur-dev/references/execution-workflow.md +1 -1
  34. package/plugins/sp/skills/spur-dev/references/feature-link-helper.md +3 -2
  35. package/plugins/sp/skills/spur-dev/references/gate-checklists.md +1 -1
  36. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +5 -2
  37. package/plugins/sp/skills/spur-dev/references/planning-workflow.md +2 -2
  38. package/schemas/spur-config.schema.json +1 -2
  39. package/spur.js +1381 -527
  40. package/web/_astro/BoardApp.BtRfVADq.js +1 -0
  41. package/web/_astro/BoardApp.CBIzcvqi.js +178 -0
  42. package/web/_astro/{TaskDetail.DHjEt5vl.js → TaskDetail.D2N60cfE.js} +1 -1
  43. package/web/_astro/{arc.XgRC1Ij_.js → arc.D-EfJJwf.js} +1 -1
  44. package/web/_astro/{architectureDiagram-3BPJPVTR.DOZWrfHc.js → architectureDiagram-3BPJPVTR.CgvTTzqp.js} +1 -1
  45. package/web/_astro/{blockDiagram-GPEHLZMM.ClQnCGf_.js → blockDiagram-GPEHLZMM.CnOohvvn.js} +1 -1
  46. package/web/_astro/{c4Diagram-AAUBKEIU.CKCtUD0Y.js → c4Diagram-AAUBKEIU.BFcBGUy_.js} +1 -1
  47. package/web/_astro/channel.kIu33Gui.js +1 -0
  48. package/web/_astro/{chunk-2J33WTMH.c7J8OHq5.js → chunk-2J33WTMH.C-6MR-XY.js} +1 -1
  49. package/web/_astro/{chunk-4BX2VUAB.C-A1Dv27.js → chunk-4BX2VUAB.BN4LP5AR.js} +1 -1
  50. package/web/_astro/{chunk-55IACEB6.C4kGGALB.js → chunk-55IACEB6.tUJ_CTtZ.js} +1 -1
  51. package/web/_astro/{chunk-727SXJPM.Bith2NHt.js → chunk-727SXJPM.BNCk-sxv.js} +1 -1
  52. package/web/_astro/{chunk-AQP2D5EJ.qeSXhsVY.js → chunk-AQP2D5EJ.BkpFIbNa.js} +1 -1
  53. package/web/_astro/{chunk-FMBD7UC4.DdQQnTAy.js → chunk-FMBD7UC4.DcSZ87PN.js} +1 -1
  54. package/web/_astro/{chunk-ND2GUHAM.BZYQVeZd.js → chunk-ND2GUHAM.BkLvHFFf.js} +1 -1
  55. package/web/_astro/{chunk-QZHKN3VN.BiUWwHaV.js → chunk-QZHKN3VN.HOdgszax.js} +1 -1
  56. package/web/_astro/{classDiagram-4FO5ZUOK.OGRhcldh.js → classDiagram-4FO5ZUOK.CWfRiY6b.js} +1 -1
  57. package/web/_astro/{classDiagram-v2-Q7XG4LA2.OGRhcldh.js → classDiagram-v2-Q7XG4LA2.CWfRiY6b.js} +1 -1
  58. package/web/_astro/{cose-bilkent-S5V4N54A.DxYklM_j.js → cose-bilkent-S5V4N54A.C6j4PxoN.js} +1 -1
  59. package/web/_astro/{dagre-BM42HDAG.BSb2dEbo.js → dagre-BM42HDAG.BATZG1II.js} +1 -1
  60. package/web/_astro/{diagram-2AECGRRQ.mOrItPK2.js → diagram-2AECGRRQ.B0f5yY6x.js} +1 -1
  61. package/web/_astro/{diagram-5GNKFQAL.C99r7J3C.js → diagram-5GNKFQAL.Bewupqv0.js} +1 -1
  62. package/web/_astro/{diagram-KO2AKTUF.BKbnisJO.js → diagram-KO2AKTUF.C23r1tB2.js} +1 -1
  63. package/web/_astro/{diagram-LMA3HP47.D8FJGo4V.js → diagram-LMA3HP47.DUErdBOR.js} +1 -1
  64. package/web/_astro/{diagram-OG6HWLK6.DVBs7n4Q.js → diagram-OG6HWLK6.BubmwAjb.js} +1 -1
  65. package/web/_astro/{erDiagram-TEJ5UH35.CddMTl4l.js → erDiagram-TEJ5UH35.BE_Qs5mv.js} +1 -1
  66. package/web/_astro/{flowDiagram-I6XJVG4X.B7WclnjQ.js → flowDiagram-I6XJVG4X.COWdbzon.js} +1 -1
  67. package/web/_astro/{ganttDiagram-6RSMTGT7.CRh7ggvz.js → ganttDiagram-6RSMTGT7.5EGYC4MK.js} +1 -1
  68. package/web/_astro/{gitGraphDiagram-PVQCEYII.du-L7V8A.js → gitGraphDiagram-PVQCEYII.bUUV7iEw.js} +1 -1
  69. package/web/_astro/index.Cestp9nh.css +1 -0
  70. package/web/_astro/{infoDiagram-5YYISTIA.CClGl9Px.js → infoDiagram-5YYISTIA.CbiInFvz.js} +1 -1
  71. package/web/_astro/{ishikawaDiagram-YF4QCWOH.BuTETIPz.js → ishikawaDiagram-YF4QCWOH.CB7lrSsJ.js} +1 -1
  72. package/web/_astro/{journeyDiagram-JHISSGLW.Cyg1zCiE.js → journeyDiagram-JHISSGLW.rhGZgWt8.js} +1 -1
  73. package/web/_astro/{kanban-definition-UN3LZRKU.aH8eDXwX.js → kanban-definition-UN3LZRKU.DPeZD_lP.js} +1 -1
  74. package/web/_astro/{linear.paE_RY_i.js → linear.l60Nyp5b.js} +1 -1
  75. package/web/_astro/{mermaid.core.BhdKkI85.js → mermaid.core.DmtMJcmL.js} +4 -4
  76. package/web/_astro/{mindmap-definition-RKZ34NQL.aZNfv8Nx.js → mindmap-definition-RKZ34NQL.Cc47AcCn.js} +1 -1
  77. package/web/_astro/{pieDiagram-4H26LBE5.5psni0hC.js → pieDiagram-4H26LBE5.B8cN-S9g.js} +1 -1
  78. package/web/_astro/{quadrantDiagram-W4KKPZXB.DcGPGrFd.js → quadrantDiagram-W4KKPZXB.DY1FN89L.js} +1 -1
  79. package/web/_astro/{requirementDiagram-4Y6WPE33.BwPD2lzi.js → requirementDiagram-4Y6WPE33.CnM6VbV8.js} +1 -1
  80. package/web/_astro/{sankeyDiagram-5OEKKPKP.B3jGVk0l.js → sankeyDiagram-5OEKKPKP.B9DcGfNV.js} +1 -1
  81. package/web/_astro/{sequenceDiagram-3UESZ5HK.BdWsx3LW.js → sequenceDiagram-3UESZ5HK.ymxaNlAY.js} +1 -1
  82. package/web/_astro/{stateDiagram-AJRCARHV.DRpG4ClZ.js → stateDiagram-AJRCARHV.DDOJ3d8P.js} +1 -1
  83. package/web/_astro/{stateDiagram-v2-BHNVJYJU.Su0ML9Wo.js → stateDiagram-v2-BHNVJYJU.Ck0Nb_KY.js} +1 -1
  84. package/web/_astro/{timeline-definition-PNZ67QCA.Cg_Uv8A5.js → timeline-definition-PNZ67QCA.BNOvZwAN.js} +1 -1
  85. package/web/_astro/{vennDiagram-CIIHVFJN.DVouKT4b.js → vennDiagram-CIIHVFJN.D3k5ivSF.js} +1 -1
  86. package/web/_astro/{wardley-L42UT6IY.CexIVQli.js → wardley-L42UT6IY.BylaxVqn.js} +1 -1
  87. package/web/_astro/{wardleyDiagram-YWT4CUSO.ChSAzcvx.js → wardleyDiagram-YWT4CUSO.HLNtFUm9.js} +1 -1
  88. package/web/_astro/{xychartDiagram-2RQKCTM6.CN-Yp50J.js → xychartDiagram-2RQKCTM6.B-WooKSU.js} +1 -1
  89. package/web/index.html +2 -2
  90. package/web/_astro/BoardApp.CrusGeQ4.js +0 -1
  91. package/web/_astro/BoardApp.P8SRAD5Q.js +0 -179
  92. package/web/_astro/channel.CmK546kO.js +0 -1
  93. package/web/_astro/index.B4x8fe52.css +0 -1
@@ -1,9 +1,8 @@
1
1
  #!/usr/bin/env bun
2
2
  /**
3
3
  * surface-drift-inventory — re-runnable drift inventory for `plugins/sp` and
4
- * the tracked workflow-YAML SSOT tree (`.spur/workflows` symlink target)
5
- * against the live source-local `spur` CLI
6
- * (feature I3, task 0539 R1/R2).
4
+ * the tracked workflow-YAML SSOT tree against the live
5
+ * source-local `spur` CLI (feature I3, task 0539 R1/R2).
7
6
  *
8
7
  * Method (mechanical first, prose second — task 0539 Design):
9
8
  * A. Plugin assertions: extract every `spur`-invocation (backticked spans, fenced
@@ -19,7 +18,7 @@
19
18
  * in this repo — recorded unverified.
20
19
  * D. Workflows: `spur workflow validate` (live engine, schema + semantic) and a
21
20
  * `--dry-run` transition walk for all ten definitions; `.spur/workflows`
22
- * symlink realpath vs its tracked SSOT target tree.
21
+ * symlink is asserted absent (two-tier project→bundled model, task 0650).
23
22
  *
24
23
  * Output: markdown inventory on stdout-path via `--out`; exit 1 when any
25
24
  * CONFIRMED mismatch remains (ok after repair); unverified entries never fail.
@@ -35,7 +34,6 @@ import {
35
34
  mkdtempSync,
36
35
  readdirSync,
37
36
  readFileSync,
38
- realpathSync,
39
37
  rmSync,
40
38
  writeFileSync,
41
39
  } from 'node:fs';
@@ -130,6 +128,7 @@ interface Parsed {
130
128
  }
131
129
 
132
130
  const PLACEHOLDER = /^([<[{($#"'`]|…|\.\.\.)/;
131
+ const LEGACY_SELF_ALIASES = new Set(['init', 'migrate', 'serve', 'status']);
133
132
 
134
133
  /** Normalize an asserted invocation span into nouns / verbs / flags. Null when it asserts nothing. */
135
134
  export function parseInvocation(spanRaw: string): Parsed | null {
@@ -259,6 +258,32 @@ export function checkNounVerbFlags(
259
258
  const root = surface([]);
260
259
  for (const noun of nouns) {
261
260
  if (noun === 'help' || /^(foo|bar|baz)$/.test(noun)) continue;
261
+ if (LEGACY_SELF_ALIASES.has(noun)) {
262
+ const self = surface(['self']);
263
+ const command = surface(['self', noun]);
264
+ const present = self.commands.includes(noun);
265
+ record(
266
+ `spur ${noun}`,
267
+ 'help-capture(spur self; hidden root alias)',
268
+ present ? 'ok' : 'mismatch',
269
+ present
270
+ ? `legacy alias; canonical self/${noun} command is present`
271
+ : `canonical self/${noun} command is absent`,
272
+ occ,
273
+ );
274
+ for (const f of flags) {
275
+ record(
276
+ `spur ${noun} ${f}`,
277
+ `help-capture(spur self ${noun}; hidden root alias)`,
278
+ present && command.flags.includes(f) ? 'ok' : 'mismatch',
279
+ command.flags.includes(f)
280
+ ? 'flag present on canonical command'
281
+ : `flag absent (${command.flags.join(', ')})`,
282
+ occ,
283
+ );
284
+ }
285
+ continue;
286
+ }
262
287
  if (!root.commands.includes(noun)) {
263
288
  record(
264
289
  `spur ${noun}`,
@@ -315,6 +340,10 @@ export function checkNounVerbFlags(
315
340
  }
316
341
  }
317
342
  for (const verb of verbs) {
343
+ if (nouns.length === 0 && LEGACY_SELF_ALIASES.has(verb)) {
344
+ checkNounVerbFlags([verb], [], flags, occ);
345
+ continue;
346
+ }
318
347
  if (nouns.length === 0 && root.commands.includes(verb)) {
319
348
  record(`spur ${verb}`, 'help-capture(root)', 'ok', 'root verb present', occ);
320
349
  }
@@ -383,7 +412,10 @@ export function sweepPluginTrees(root: string = PLUGIN_ROOT): void {
383
412
  const cells = line.split('|').map((c) => c.trim());
384
413
  const lastCell = cells[cells.length - 2] ?? line;
385
414
  if (first && first !== 'Verb' && first !== 'spur') {
386
- if (rootCommands.includes(first)) {
415
+ const nounCommands = surface([refNoun]).commands;
416
+ if (nounCommands.includes(first)) {
417
+ checkNounVerbFlags([refNoun], [first], flagNames(lastCell), occ);
418
+ } else if (rootCommands.includes(first) || LEGACY_SELF_ALIASES.has(first)) {
387
419
  checkNounVerbFlags([], [first], flagNames(lastCell), occ);
388
420
  } else {
389
421
  checkNounVerbFlags([refNoun], [first], flagNames(lastCell), occ);
@@ -611,7 +643,7 @@ const JSON_PROBES: string[][] = [
611
643
  ['agent', 'list', '--json'],
612
644
  ['agent', 'doctor', 'omp', '--json'],
613
645
  ['workflow', 'list', '--json'],
614
- ['workflow', 'validate', '.spur/workflows/basic.yaml', '--json'],
646
+ ['workflow', 'validate', 'basic.yaml', '--json'],
615
647
  ['status', '--json'],
616
648
  ['team', 'status', '--json'],
617
649
  ['projects', 'list', '--json'],
@@ -758,18 +790,20 @@ export function sweepWorkflows(opts: { run?: CliRunner; wfDir?: string; link?: s
758
790
  }
759
791
  });
760
792
  }
793
+ // Two-tier model (task 0648/0650): `.spur/workflows` is no longer a symlink and
794
+ // must not exist — the CLI resolves an explicit project path, then bundled fallback. Assert absence.
761
795
  const link = opts.link ?? join(REPO_ROOT, '.spur', 'workflows');
762
- let status: Row['status'] = 'mismatch';
763
- let actual = '';
796
+ let status: Row['status'] = 'ok';
797
+ let actual = 'absent (two-tier project→bundled model, task 0650)';
764
798
  try {
765
- const rp = realpathSync(link);
766
- const tracked = realpathSync(wfDir);
767
- status = rp === tracked ? 'ok' : 'mismatch';
768
- actual = `resolves to ${rel(rp)}${status === 'ok' ? ' (== tracked SSOT target)' : ` — expected ${rel(tracked)}`}`;
799
+ if (existsSync(link)) {
800
+ status = 'mismatch';
801
+ actual = 'still present should be removed (task 0650 R3)';
802
+ }
769
803
  } catch (e) {
770
- actual = `cannot resolve .spur/workflows: ${String(e).slice(0, 120)}`;
804
+ actual = `exists-check failed: ${String(e).slice(0, 120)}`;
771
805
  }
772
- record('.spur/workflows symlink', 'symlink-realpath', status, actual, { file: '.spur/workflows', line: 1 });
806
+ record('.spur/workflows symlink', 'symlink-absent', status, actual, { file: '.spur/workflows', line: 1 });
773
807
  }
774
808
 
775
809
  // ─── Report ─────────────────────────────────────────────────────────────────
@@ -805,7 +839,7 @@ export function render(): string {
805
839
  L.push(
806
840
  "R1 scope: `plugins/sp/{commands,skills,scripts,hooks}` (tests/ and evals/ are the harness itself; agents/ prose is outside R1's named surface list).",
807
841
  );
808
- L.push('R2 scope: all workflow YAML via the `.spur/workflows` runtime symlink (tracked SSOT tree).');
842
+ L.push('R2 scope: all workflow YAML via the bundled tree (two-tier project→bundled model since task 0648/0650).');
809
843
  L.push('');
810
844
  L.push('## Check methods');
811
845
  L.push('');
@@ -825,7 +859,7 @@ export function render(): string {
825
859
  L.push(
826
860
  '| `workflow-validate` / `workflow-dry-run` | Live engine: schema+semantic validate; `--dry-run` transition walk |',
827
861
  );
828
- L.push('| `symlink-realpath` | realpath of `.spur/workflows` vs its tracked SSOT target |');
862
+ L.push('| `symlink-absent` | `.spur/workflows` is asserted absent (two-tier project→bundled model, task 0650) |');
829
863
  L.push('');
830
864
  for (const [method, list] of byMethod) {
831
865
  const mm = list.filter((r) => r.status === 'mismatch').length;
@@ -281,15 +281,22 @@ function checkTargetResolution(cmd: ParsedCommand, skillsDir: string, root: stri
281
281
  }
282
282
  }
283
283
 
284
- // Workflow file refs — patterns like .spur/workflows/<name>.yaml
285
- const workflowRefs = [...implSection.matchAll(/\.spur\/workflows\/([^\s)"\]]+\.yaml)/g)];
284
+ // Workflow file refs — bare names (`spur workflow run <name>.yaml`, two-tier model
285
+ // 0648/0650) resolve against the bundled workflow-definition asset tree; legacy
286
+ // `.spur/workflows/<name>.yaml` paths are also caught as unification drift.
287
+ const wfDir = join(root, 'config', 'workflows');
288
+ const workflowRefs = [
289
+ ...[...implSection.matchAll(/\.spur\/workflows\/([^\s)"\]]+\.yaml)/g)],
290
+ ...[...implSection.matchAll(/spur workflow (?:run|validate) ([a-z0-9-]+\.yaml)/g)],
291
+ ];
286
292
  for (const ref of workflowRefs) {
287
- const wfPath = join(root, '.spur', 'workflows', ref[1]);
293
+ const bare = (ref[1] ?? '').split('/').pop() ?? '';
294
+ const wfPath = join(wfDir, bare);
288
295
  if (!existsSync(wfPath)) {
289
296
  violations.push({
290
297
  command: cmd.name,
291
298
  gate: 'c',
292
- message: `unresolved workflow file ${ref[0]} (missing ${wfPath})`,
299
+ message: `unresolved workflow file ${bare} (missing ${wfPath})`,
293
300
  });
294
301
  }
295
302
  }
@@ -521,7 +521,7 @@ Do **not** use this skill for:
521
521
  rubric.
522
522
  - `plugins/sp/references/roles.md` — the Layer-1 role→tier table; this skill's verify/review modes
523
523
  run under the `reviewer` role (0538 R4: this file points at roles.md, it does not restate tiers).
524
- - `.spur/workflows/task-pipeline.yaml` — the `verify → record` gate that consumes the verdict.
524
+ - `task-pipeline.yaml` — the `verify → record` gate that consumes the verdict.
525
525
  - **`sp:spur-dev`** — the execution-half umbrella that drives the pipeline this skill gates.
526
526
  - [references/code-improvement.md](references/code-improvement.md) — architecture-improvement lens
527
527
  for module depth, seam placement, locality, coupling, and testability.
@@ -120,7 +120,7 @@ Wave C verification can emit the following additive `checks[]` rows:
120
120
 
121
121
  ## How the gate reads it
122
122
 
123
- `.spur/workflows/task-pipeline.yaml`, transition `verify → record`:
123
+ `task-pipeline.yaml`, transition `verify → record`:
124
124
 
125
125
  ```yaml
126
126
  guard:
@@ -233,7 +233,7 @@ Also note **what worked well** so efficient patterns are preserved.
233
233
  1. **Fix description** — documentation, skill reference, pipeline comment, hook, etc.
234
234
  2. **Target location** — exact file path
235
235
  - Skill guidance: `skills/<skill-name>/SKILL.md` or `references/<name>.md`
236
- - Pipeline comments: runtime workflow under `.spur/workflows/` (e.g. `task-pipeline.yaml`)
236
+ - Pipeline comments: runtime workflow resolves by bare name (`task-pipeline.yaml`)
237
237
  - CLI reference: `skills/spur-cli/references/tasks/…`
238
238
  3. **Proposed content** — text or code block to add
239
239
  4. **Expected impact** — estimated time saved per future run
@@ -212,5 +212,5 @@ A6 → C5/C3 → unit first if verdict says so; else verify.
212
212
  - Dev operations map: `plugins/sp/skills/spur-dev/references/dev-operations.md:37-56`
213
213
  - `--next` chain on run: `plugins/sp/commands/dev-run.md:104-127`
214
214
  - Refine skip-gate sections: `plugins/sp/skills/spur-dev/references/dev-operations.md:114-128`
215
- - Task lifecycle wip→testing guard: `.spur/workflows/task-lifecycle.yaml:48-54`
215
+ - Task lifecycle wip→testing guard: `task-lifecycle.yaml:48-54`
216
216
  - Decision-brief format: `plugins/sp/skills/spur-dev/references/decision-brief.md`
@@ -39,7 +39,7 @@ that review effort happens on the PR, through Codex — not in the local session
39
39
  sp:pr-reviewing (this skill — mode routing, triage, fix, rules)
40
40
  │ deterministic spine, state order + guards:
41
41
 
42
- .spur/workflows/pr-review.yaml ← workflow SSOT (seeded by spur init; project-tunable)
42
+ pr-review.yaml ← workflow SSOT (seeded by spur init; project-tunable)
43
43
  │ every state resolves with `superskill script path` and shells out to:
44
44
 
45
45
  staged pr-reviewing.ts ← Bun entrypoint for the tested git/gh core
@@ -280,7 +280,7 @@ or review requests.
280
280
 
281
281
  ## Additional Resources
282
282
 
283
- - **Workflow SSOT:** `.spur/workflows/pr-review.yaml` (seeded by `spur init`; tune per project —
283
+ - **Workflow SSOT:** `pr-review.yaml` (seeded by `spur init`; tune per project —
284
284
  wait budget, `preReviewCmd`, dedupe policy)
285
285
  - **Script source:** [scripts/pr-reviewing.ts](../../scripts/pr-reviewing.ts) — subcommand CLI
286
286
  - **Tests:** [tests/pr-reviewing.test.ts](../../tests/pr-reviewing.test.ts) — stubbed git/gh suites
@@ -146,7 +146,7 @@ blank Design before implement (`/sp:dev-refine` / `dev-refineall`).
146
146
 
147
147
  The `template` field is the **single variant axis** (TASK_VARIANTS): it selects the section
148
148
  layout (the `section-matrix.yaml` variant), the scaffold body file
149
- (`.spur/templates/task/<variant>.md`), and is written to the task's `template:` frontmatter.
149
+ (`.spur/tasks/templates/<variant>.md`), and is written to the task's `template:` frontmatter.
150
150
  Which sections actually appear is **status-driven** by the runtime matrix — query
151
151
  `spur task sections <wbs> list --json` rather than assuming a fixed list — pick the variant by
152
152
  *purpose*, not by a section checklist.
@@ -64,7 +64,7 @@ fails, STOP and report the gap rather than papering over it with customization:
64
64
  ```
65
65
  spur status
66
66
  spur task create "__probe__" --template standard # confirms task template + matrix resolve
67
- spur workflow validate .spur/workflows/task-pipeline.yaml
67
+ spur workflow validate task-pipeline.yaml
68
68
  ```
69
69
 
70
70
  ### Phase 1.6 — Rule glob adaptation (LLM-as-judge)
@@ -12,7 +12,7 @@ Read this before writing `Solution`, `Testing`, or `Review`. Section **writers**
12
12
  evidence section (F92 0593 R1): implement owns `Solution`, the review coordinator
13
13
  (`/sp:dev-review`) owns `Review`, and `spur task record` owns `Testing` (bare-`## Review`
14
14
  backfill is fallback-only). The owning implementations are
15
- `.spur/workflows/task-lifecycle.yaml`, `hasPopulatedPriorityTable()` in
15
+ `task-lifecycle.yaml`, `hasPopulatedPriorityTable()` in
16
16
  `packages/app/src/services/task-check.ts`, the verdict normalizers in
17
17
  `packages/app/src/services/task-record.ts`, and `TASK_CANONICAL_SECTIONS` in
18
18
  `packages/domain/src/planning/markdown-document.ts`.
@@ -29,7 +29,7 @@ Authored workflows default to a project-local directory, grouped by purpose:
29
29
 
30
30
  A `--file <path>` argument overrides the default. Keep one workflow per file, named for what it does
31
31
  (`approval.yaml`, `import-file.yaml`), not for its mode. The canonical example
32
- (`.spur/workflows/basic.yaml`) lives here; copy real schema shapes from it rather than from a
32
+ (`basic.yaml`) lives here; copy real schema shapes from it rather than from a
33
33
  half-remembered snippet.
34
34
 
35
35
  ## Sub-procedure: mode-selection gate
@@ -50,7 +50,7 @@ ordered guards, and may loop back on itself (e.g. implement → check → fix
50
50
  | Node typing | none (states are untyped) | `type: action\|gate\|parallel\|decision` |
51
51
  | Required keys | `name, initialState, states, transitions` | `kind, name, initialNode, nodes, edges` |
52
52
  | `kind` field | optional (defaults to state-machine) | **required** — `kind: transition-flow` |
53
- | Canonical example | implement→check→fix loop (`.spur/workflows/basic.yaml` — soft status-file probe + bounded fixall) | read→validate→transform→write pipeline |
53
+ | Canonical example | implement→check→fix loop (`basic.yaml` — soft status-file probe + bounded fixall) | read→validate→transform→write pipeline |
54
54
 
55
55
  **Heuristic:** loops / retries / one-active-state → **state-machine**; pipeline / fan-out / action-per-node → **transition-flow**.
56
56
 
@@ -335,7 +335,7 @@ the workflow's actions (`shell`, custom runners) do that; this skill builds and
335
335
  (transition-flow) wins. Order the specific case before the fallback. Simple shell checks can use
336
336
  `action-ok` before an unconditional retry edge; multi-condition gates prefer a **soft status-file
337
337
  probe** (always exit 0) with ordered shell guards for PASS / FAIL / exhausted (see
338
- `.spur/workflows/basic.yaml` and `task-pipeline.yaml` quality-gate hop).
338
+ `basic.yaml` and `task-pipeline.yaml` quality-gate hop).
339
339
  4. **`env.allow` is an allowlist.** `${env.X}` resolves only if `X` is listed under `env.allow`;
340
340
  otherwise it resolves empty. A workflow that "loses" an environment value usually forgot to allow it.
341
341
  5. **Extensions are fail-closed.** The CLI loads YAML-declared extension modules itself
@@ -372,9 +372,9 @@ the workflow's actions (`shell`, custom runners) do that; this skill builds and
372
372
  custom action/guard runners, the trust-gated extension loader, and CLI-vs-library capability gaps.
373
373
  - `@gobing-ai/ts-dual-workflow-engine` README — authoritative library reference (both drivers,
374
374
  RunLifecycle, persistence, the full event map, every built-in capability).
375
- - `.spur/workflows/basic.yaml` — the canonical state-machine implement→soft-check→fixall loop
375
+ - `basic.yaml` — the canonical state-machine implement→soft-check→fixall loop
376
376
  (status-file branching + `qualityGateMaxFixAttempts`); copy real shapes from here.
377
- - `.spur/workflows/task-pipeline.yaml` — full production pipeline (precheck, quality gate, HITL,
377
+ - `task-pipeline.yaml` — full production pipeline (precheck, quality gate, HITL,
378
378
  verify, record) when you need the complete reliability pattern set.
379
379
 
380
380
  ## Platform Notes
@@ -85,7 +85,7 @@ Full procedure: **[references/planning-workflow.md](references/planning-workflow
85
85
  ```
86
86
  pick task (spur task list --json)
87
87
  → interactive omit/inline: read task-pipeline.yaml → drive actions + guards in host session
88
- → explicit/headless executor: spur workflow run .spur/workflows/task-pipeline.yaml --vars '{"wbs":"<wbs>"}'
88
+ → explicit/headless executor: spur workflow run task-pipeline.yaml --vars '{"wbs":"<wbs>"}'
89
89
  → on HITL pause: surface to operator; resume the selected driver from the paused state
90
90
  ```
91
91
 
@@ -218,10 +218,10 @@ for "what's actually in file Y" or for resources that sit outside the step seque
218
218
 
219
219
  - `plugins/sp/references/roles.md` — the Layer-1 role→tier table (`scribe` / `coder` / `reviewer` /
220
220
  `planner`, one per tier); the role vocabulary this spine's operations route under.
221
- - `.spur/workflows/task-pipeline.yaml` — the execution pipeline definition.
222
- - `.spur/workflows/idea-pipeline.yaml` — the front-half state machine (absorbed the retired
221
+ - `task-pipeline.yaml` — the execution pipeline definition.
222
+ - `idea-pipeline.yaml` — the front-half state machine (absorbed the retired
223
223
  planning-pipeline in D5-K; `/sp:dev-plan` routes here, ADR-072).
224
- - `.spur/templates/bdd/gherkin.md` — the BDD scenario template.
224
+ - `templates/bdd/gherkin.md` — the BDD scenario template.
225
225
 
226
226
  ## Platform Notes
227
227
 
@@ -148,9 +148,9 @@ contract exists to prevent.
148
148
  > Before 0398 R7 this row was unusable: the tag was required to keep `MET`, but the tag also broke
149
149
  > title matching, so the scenario read as unverified. Both halves now hold at once.
150
150
 
151
- ### What `--strict` advance requires
151
+ ### What a done advance requires
152
152
 
153
- `spur feature advance <id> --to done --strict` treats a scenario as verified only when **all** of
153
+ `spur feature advance <id> --to done` treats a scenario as verified only when **all** of
154
154
  these hold:
155
155
 
156
156
  1. A task links to the feature (`feature_id`) and is status `done`.
@@ -169,7 +169,7 @@ Author one row per scenario declared in the task's own `### Acceptance Criteria`
169
169
 
170
170
  ## Gherkin template
171
171
 
172
- Use the canonical BDD template at `.spur/templates/bdd/gherkin.md`. Key rules:
172
+ Use the canonical BDD template at `templates/bdd/gherkin.md`. Key rules:
173
173
 
174
174
  - **Given** establishes preconditions (state, setup).
175
175
  - **When** describes the single action under test.
@@ -210,7 +210,7 @@ Each resolved decision from a grilling interview (Phase 1) becomes one or more G
210
210
  | A **deferred branch** ("out of scope for now") | A `## Scope` **Out** bullet — *not* a scenario |
211
211
 
212
212
  Number scenarios `R1, R2, …` sequentially, stable forever (the title is the traceability identity
213
- key). Use the Gherkin template at `.spur/templates/bdd/gherkin.md`.
213
+ key). Use the Gherkin template at `templates/bdd/gherkin.md`.
214
214
 
215
215
  ## AC altitude (task 0584 / ADR-062)
216
216
 
@@ -143,7 +143,7 @@ must not be changed without updating the backing skill.
143
143
  - **Inputs:** `<wbs>` (required). `--mode <full|implement>` selects the execution mode. `implement` invokes `sp:code-implementation` inline by default. Interactive `full` with omitted `--agent` or explicit `--agent inline` reads `task-pipeline.yaml` and drives its actions/guards in the host session — host-controlled and non-subprocess; **omitted** `--agent` keeps 0508 eligibility (eligible `agent.run` stages dispatch once to a native subagent, host fallback), while explicit `--agent inline` is the zero-dispatch carve-out (every stage executes in the invoking 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. `--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.
144
144
  - **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).
145
145
  - **Modes:**
146
- - **`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.
146
+ - **`full`** (default): Drive the full pipeline — precheck → implement → test → review → approve(HITL) → verify → record → done. Interactive omit/inline uses [inline-pipeline-driver.md](inline-pipeline-driver.md) (host-controlled; eligible stages may use a native subagent); explicit/headless executor selection invokes `spur workflow run task-pipeline.yaml --vars '{"wbs":"<wbs>"}'` (with `profile: auto` when `--auto`). Both monitor/surface HITL and preserve the YAML gates. `--next` never changes this mode.
147
147
  - **`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".
148
148
  - **Delegation:** `Skill(skill="sp:spur-dev", args="run-inline $ARGUMENTS")` for interactive full omit/inline (explicit `inline` = zero-dispatch carve-out); `Skill(skill="sp:spur-dev", args="run $ARGUMENTS")` for explicit/headless full mode; `Skill(skill="sp:code-implementation", args="$ARGUMENTS")` for implement mode.
149
149
 
@@ -301,33 +301,33 @@ must not be changed without updating the backing skill.
301
301
 
302
302
  - **Purpose:** Wrap up a single completed task — capture learnings, record metrics, sync docs, and optionally advance the feature / clean up the branch.
303
303
  - **Inputs:** `<wbs>` (required, positional). `--agent <inline|auto|name>` names the wrap's model-bearing executor (default: `agent.default`); wrap is workflow-backed, so omitted `--agent` resolves to `agent.default` under objective trigger 3 (durable auditable run record required) while explicit `--agent inline` is rejected with the stable special error (headless workflow surface), `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).
304
- - **Backing:** `spur workflow run .spur/workflows/wrapup-pipeline.yaml` — direct workflow invocation (no backing skill; the pipeline IS the procedure).
304
+ - **Backing:** `spur workflow run wrapup-pipeline.yaml` — direct workflow invocation (no backing skill; the pipeline IS the procedure).
305
305
  - **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`.
306
306
  - **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:
307
307
 
308
308
  ```bash
309
309
  VARS=$(jq -nc --arg tasks "[\"$WBS\"]" --arg agent "$AGENT" --arg profile "$PROFILE" --arg merge "$MERGE" \
310
310
  '{tasks:$tasks, agent:$agent, profile:$profile, merge:$merge}')
311
- spur workflow run .spur/workflows/wrapup-pipeline.yaml --vars "$VARS"
311
+ spur workflow run wrapup-pipeline.yaml --vars "$VARS"
312
312
  ```
313
313
 
314
- - **Delegation:** Direct `spur workflow run .spur/workflows/wrapup-pipeline.yaml` (no `Skill()` call — the command builds the vars JSON and invokes the workflow directly via `Bash`).
314
+ - **Delegation:** Direct `spur workflow run wrapup-pipeline.yaml` (no `Skill()` call — the command builds the vars JSON and invokes the workflow directly via `Bash`).
315
315
 
316
316
  ### 15. wrapall
317
317
 
318
318
  - **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.
319
319
  - **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 omitted `--agent` resolves to `agent.default` under objective trigger 3 (durable auditable run record required) while explicit `--agent inline` is rejected with the stable special error (headless workflow surface), `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).
320
- - **Backing:** `spur workflow run .spur/workflows/wrapup-pipeline.yaml` — direct workflow invocation.
320
+ - **Backing:** `spur workflow run wrapup-pipeline.yaml` — direct workflow invocation.
321
321
  - **Behavior:** Resolves the task list via `spur task list --json` (filtered by `--feature`, `--since`, `--status`), resolves the executor (`agent.default` for omitted `--agent`, tier-resolved for `auto`, unchanged for a name; explicit `--agent inline` is rejected with the stable special error), 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.
322
322
  - **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:
323
323
 
324
324
  ```bash
325
325
  VARS=$(jq -nc --arg tasks "$TASKS" --arg feature "$FEATURE" --arg agent "$AGENT" --arg profile "$PROFILE" --arg merge "$MERGE" \
326
326
  '{tasks:$tasks, feature:$feature, agent:$agent, profile:$profile, merge:$merge}')
327
- spur workflow run .spur/workflows/wrapup-pipeline.yaml --vars "$VARS"
327
+ spur workflow run wrapup-pipeline.yaml --vars "$VARS"
328
328
  ```
329
329
 
330
- - **Delegation:** Direct `spur workflow run .spur/workflows/wrapup-pipeline.yaml` (no `Skill()` call — the command resolves tasks and invokes the workflow directly via `Bash`).
330
+ - **Delegation:** Direct `spur workflow run wrapup-pipeline.yaml` (no `Skill()` call — the command resolves tasks and invokes the workflow directly via `Bash`).
331
331
 
332
332
  ### 16. idea
333
333
 
@@ -337,9 +337,9 @@ must not be changed without updating the backing skill.
337
337
  - `--skip-design` — design package off (system-design + task Design).
338
338
  - `--approve-taste` — with `--auto`, skip **all** remaining taste pauses this run (idea-eval + design-approval). Sets `idea_approved=true` and `design_approved=true`.
339
339
  Aliases (prefer `--approve-taste`): `--idea-approved` → `idea_approved`; `--design-approved` → `design_approved`. There is **no** `--design` force flag.
340
- - **Backing:** `spur workflow run .spur/workflows/idea-pipeline.yaml` — direct workflow invocation.
340
+ - **Backing:** `spur workflow run idea-pipeline.yaml` — direct workflow invocation.
341
341
  - **Behavior:** Builds vars from the table above and invokes 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 → handoff. STOPS at handoff — no task execution, no pipeline nesting.
342
- - **Delegation:** Direct `spur workflow run .spur/workflows/idea-pipeline.yaml` (command maps flags → vars, then `Bash`).
342
+ - **Delegation:** Direct `spur workflow run idea-pipeline.yaml` (command maps flags → vars, then `Bash`).
343
343
  - **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`, still pauses unless taste pre-cleared (`--approve-taste` / alias). Enhanced idea is a sidecar — `vars.idea` is not overwritten.
344
344
  - **Design package (`--skip-design` only):**
345
345
 
@@ -27,7 +27,7 @@ Invariant: zero `- [ ]` entries (real or placeholder) anywhere in a `done` task
27
27
  Drive the real `task-pipeline.yaml` FSM where applicable:
28
28
 
29
29
  ```
30
- spur workflow run .spur/workflows/task-pipeline.yaml --vars '{"wbs":"<wbs>"}'
30
+ spur workflow run task-pipeline.yaml --vars '{"wbs":"<wbs>"}'
31
31
  ```
32
32
 
33
33
  If you hand-walk lifecycle statuses (manual `spur task update <wbs> <status>` without the
@@ -16,7 +16,7 @@ report shape.
16
16
 
17
17
  Single-task execution is documented in **[execution-workflow.md](execution-workflow.md)** — this file
18
18
  extends that procedure to the batch case. Read that file first for the single-task pipeline contract;
19
- everything here assumes a task runs through `.spur/workflows/task-pipeline.yaml` unchanged.
19
+ everything here assumes a task runs through `task-pipeline.yaml` unchanged.
20
20
 
21
21
  **Zero engine code, zero schema changes (ADR-022).** The batch is orchestration over existing seams —
22
22
  the status vocabulary (`packages/domain/src/planning/schema.ts`), the `dependencies[]` frontmatter
@@ -232,7 +232,7 @@ invocation in its own subagent/worktree-safe context; synthesize outcomes; recov
232
232
 
233
233
  ### 3.1 Per-task execution reuses the pipeline verbatim (R4)
234
234
 
235
- Each task runs through the **standard single-task pipeline** — `.spur/workflows/task-pipeline.yaml`
235
+ Each task runs through the **standard single-task pipeline** — `task-pipeline.yaml`
236
236
  — with no new FSM and no step edits. Interactive sequential omit/inline invokes the host
237
237
  [inline pipeline driver](inline-pipeline-driver.md), which interprets that file; explicit/parallel
238
238
  execution invokes the workflow engine. The batch loop inspects the result and never redefines a
@@ -242,7 +242,7 @@ step.
242
242
  `agent.run` stages runs for many minutes. Always use `--async` + `spur workflow trace` polling:
243
243
 
244
244
  ```bash
245
- RUN=$(spur workflow run .spur/workflows/task-pipeline.yaml \
245
+ RUN=$(spur workflow run task-pipeline.yaml \
246
246
  --vars '{"wbs":"<wbs>","profile":"auto","agent":"claude"}' --async --json | jq -r '.runId')
247
247
  spur workflow trace "$RUN" --json | jq '{runId, status, terminalState}' # poll until status is terminal (done/failed)
248
248
  ```
@@ -120,7 +120,7 @@ VARS=$(jq -nc --arg wbs "$WBS" --arg profile auto '{wbs:$wbs, profile:$profile}'
120
120
  # optional, when --agent <name|auto> is set:
121
121
  # VARS=$(jq -nc --arg wbs "$WBS" --arg profile auto --arg agent "$AGENT" \
122
122
  # '{wbs:$wbs, profile:$profile, agent:$agent}')
123
- RUN=$(spur workflow run .spur/workflows/task-pipeline.yaml --vars "$VARS" --async --json | jq -r '.runId')
123
+ RUN=$(spur workflow run task-pipeline.yaml --vars "$VARS" --async --json | jq -r '.runId')
124
124
  spur workflow trace "$RUN" --follow --output # streams the run; --output shows the agent log surface
125
125
  ```
126
126
 
@@ -44,10 +44,11 @@ When `/sp:dev-verify <wbs>` produces a `PASS` verdict:
44
44
  - Confirm with the operator before executing `spur feature sync <id>` (in `--auto` mode, apply forward-only proposals).
45
45
 
46
46
  2. **Task missing `feature_id`**:
47
- - Check if `feature_link_declined: true` is set on the task frontmatter (`spur task show <wbs> --json`).
47
+ - Check the task content for the Q&A marker `feature_link_declined: true` (`spur task show <wbs> --json`).
48
48
  - If `feature_link_declined: true`: skip linking prompt — explicit operator deferral is preserved.
49
49
  - If unlinked and not declined: propose candidate existing features via single-task mode.
50
- - If operator explicitly declines: record `feature_link_declined: true` via `spur task update <wbs> --field feature_link_declined --value true`.
50
+ - If operator explicitly declines: preserve the existing Q&A body, append the marker, and write it through
51
+ `spur task update <wbs> --section Q&A --from-file <body-file>`.
51
52
 
52
53
  ## Single-task mode
53
54
 
@@ -23,7 +23,7 @@ Run each checklist as an actual command sequence, not a mental confirmation:
23
23
 
24
24
  ## Task lifecycle transitions
25
25
 
26
- SSOT: `.spur/workflows/task-lifecycle.yaml`.
26
+ SSOT: `task-lifecycle.yaml`.
27
27
 
28
28
  backlog → todo → wip → testing → done
29
29
 
@@ -14,8 +14,9 @@ interactive `/sp:dev-run --mode full` or sequential `/sp:dev-runall` invocation
14
14
  passes `--agent inline`. A named executor, `--agent auto`, parallel batch mode, `spur workflow run`,
15
15
  and `spur agent run` keep the existing subprocess path.
16
16
 
17
- The project runtime definition at `.spur/workflows/task-pipeline.yaml` remains the sole FSM definition.
18
- The driver MUST read that file
17
+ The project runtime definition `task-pipeline.yaml`, resolved through the two-tier
18
+ project→bundled model (task 0648/0650, never an unbundled runtime path) — remains the sole
19
+ FSM definition. The driver MUST read that file
19
20
  at invocation time. It must not copy the state list, actions, guards, or transition order into a
20
21
  command, skill, script, or second workflow.
21
22
 
@@ -134,12 +135,14 @@ tasks (0617, 0619) because the sections were hand-written **before** the verdict
134
135
  **overwrites** a hand-authored `## Testing` with an auto-generated "No requirements recorded" table,
135
136
  plus replaces `## Solution` with a bare auto change-map. Creating the artifact first (PASS, with
136
137
  requirement rows keyed by scenario title) makes `task record` the compliant path.
138
+
137
139
  ```bash
138
140
  # verdict artifact first (shape: {wbs, verdict, requirements:[{id,status,evidence}], checks:[], source})
139
141
  # then the record hop; then re-write Testing/Solution if record's backfill is thinner than intended.
140
142
  spur task update <wbs> wip --no-lifecycle
141
143
  spur task record <wbs> --solution-from-diff --transition testing
142
144
  ```
145
+
143
146
  The engine now preserves an already-authored Testing when the verdict is UNKNOWN (task-service
144
147
  `record` fallback-only, mirroring the Review 0593 precedent) — but the order above is still the
145
148
  contract for the standard pipeline.
@@ -57,7 +57,7 @@ Gherkin template. Conventions:
57
57
  - **Scenario-title mapping:** the scenario title is the identity key for traceability edges
58
58
  to task files — keep them stable and unique.
59
59
  - Generate AC inline (current session) with the BDD template
60
- (`.spur/templates/bdd/gherkin.md`), or escalate to `spur agent run` only when a
60
+ (`templates/bdd/gherkin.md`), or escalate to `spur agent run` only when a
61
61
  [subprocess trigger](cross-cutting.md#inline-default-execution-surface) applies. **Thread
62
62
  `--agent` through** when the command forwarded one. See the
63
63
  [inline-default execution-surface contract](cross-cutting.md#inline-default-execution-surface).
@@ -220,7 +220,7 @@ skip and why. Do not pause to ask; the operator reviews the satellite afterward.
220
220
  ## Step 5.6: Idea pipeline (sp:dev-idea) — planning handoff contracts
221
221
 
222
222
  `/sp:dev-idea` runs the same planning half through the idea-pipeline workflow definition
223
- (`.spur/workflows/idea-pipeline.yaml` — symlinked to the tracked SSOT). Two
223
+ (`idea-pipeline.yaml` — resolved from an explicit project path or the bundled tree). Two
224
224
  artifacts are the contract between the workflow states and the operator:
225
225
 
226
226
  **Goal/Scope intent (feature-create).** The agent writes body-only intent files
@@ -4,7 +4,6 @@
4
4
  "title": "Spur Project Configuration",
5
5
  "description": "Schema for .spur/config.yaml \u2014 the single project configuration surface consumed by spur and (future) spur-server.",
6
6
  "type": "object",
7
- "required": ["version", "name"],
8
7
  "properties": {
9
8
  "$schema": {
10
9
  "type": "string",
@@ -12,7 +11,7 @@
12
11
  },
13
12
  "version": {
14
13
  "type": "string",
15
- "description": "Config schema version (string, not integer). Current recommended: '1.1' (ADR-033 executor tiers + planning blocks). '1' remains accepted for backward compatibility."
14
+ "description": "Config schema version (string, not integer). Current recommended: '1.2' (config-1.2 layered global/project merge, ADR-033 executor tiers + planning blocks). '1' remains accepted for backward compatibility."
16
15
  },
17
16
  "name": {
18
17
  "type": "string",