@gobing-ai/spur 0.4.14 → 0.4.16

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 (121) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/config.global.yaml +21 -8
  3. package/config/decisions/executor.yaml +53 -0
  4. package/config/decisions/task-pipeline.yaml +44 -28
  5. package/config/pipeline-budgets.json +0 -14
  6. package/config/workflow-candidates.json +18 -0
  7. package/config/workflows/feature-lifecycle.yaml +9 -16
  8. package/config/workflows/idea-pipeline.yaml +158 -740
  9. package/config/workflows/task-pipeline.yaml +66 -54
  10. package/config/workflows/wrapup-pipeline.yaml +91 -623
  11. package/package.json +11 -10
  12. package/plugins/sp/README.md +9 -5
  13. package/plugins/sp/agents/expert-spur.md +3 -0
  14. package/plugins/sp/agents/super-planner.md +5 -1
  15. package/plugins/sp/agents/super-reviewer.md +43 -12
  16. package/plugins/sp/commands/dev-fixall.md +7 -2
  17. package/plugins/sp/commands/dev-idea.md +15 -6
  18. package/plugins/sp/commands/dev-plan.md +1 -1
  19. package/plugins/sp/commands/dev-pr-review.md +6 -6
  20. package/plugins/sp/commands/dev-run.md +3 -0
  21. package/plugins/sp/commands/dev-runall.md +6 -3
  22. package/plugins/sp/commands/dev-verify.md +1 -1
  23. package/plugins/sp/commands/dev-wrap.md +1 -1
  24. package/plugins/sp/commands/dev-wrapall.md +4 -4
  25. package/plugins/sp/plugin.json +1 -1
  26. package/plugins/sp/skills/code-implementation/SKILL.md +8 -8
  27. package/plugins/sp/skills/code-improvement/SKILL.md +3 -3
  28. package/plugins/sp/skills/code-review/references/verification-before-completion.md +5 -4
  29. package/plugins/sp/skills/code-verification/SKILL.md +4 -4
  30. package/plugins/sp/skills/code-verification/references/verdict-schema.md +27 -11
  31. package/plugins/sp/skills/functional-review/SKILL.md +1 -1
  32. package/plugins/sp/skills/functional-review/references/verdict-schema.md +1 -1
  33. package/plugins/sp/skills/next-router/references/routing-table.md +1 -1
  34. package/plugins/sp/skills/pr-reviewing/SKILL.md +36 -18
  35. package/plugins/sp/skills/spec-decomposition/references/decomposition.md +2 -1
  36. package/plugins/sp/skills/spur-check/SKILL.md +1 -1
  37. package/plugins/sp/skills/spur-cli/SKILL.md +23 -2
  38. package/plugins/sp/skills/spur-cli/references/agent.md +12 -0
  39. package/plugins/sp/skills/spur-cli/references/builder.md +2 -2
  40. package/plugins/sp/skills/spur-cli/references/decision/lifecycle.md +166 -0
  41. package/plugins/sp/skills/spur-cli/references/decision.md +146 -16
  42. package/plugins/sp/skills/spur-cli/references/features.md +19 -0
  43. package/plugins/sp/skills/spur-cli/references/rules.md +2 -0
  44. package/plugins/sp/skills/spur-cli/references/tasks.md +14 -1
  45. package/plugins/sp/skills/spur-cli/references/workflows/authoring-workflows.md +1 -1
  46. package/plugins/sp/skills/spur-cli/references/workflows/workflow-fit-and-tuning.md +3 -2
  47. package/plugins/sp/skills/spur-cli/references/workflows.md +9 -0
  48. package/plugins/sp/skills/spur-composer/SKILL.md +20 -1
  49. package/plugins/sp/skills/spur-dev/SKILL.md +16 -16
  50. package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +2 -1
  51. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +2 -2
  52. package/plugins/sp/skills/spur-dev/references/decision-points.md +97 -0
  53. package/plugins/sp/skills/spur-dev/references/dev-operations.md +18 -18
  54. package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +1 -1
  55. package/plugins/sp/skills/spur-dev/references/execution-batch.md +19 -10
  56. package/plugins/sp/skills/spur-dev/references/execution-parallel-isolation.md +4 -4
  57. package/plugins/sp/skills/spur-dev/references/execution-workflow.md +41 -29
  58. package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +34 -2
  59. package/plugins/sp/skills/spur-dev/references/execution-worktree-setup.md +4 -4
  60. package/plugins/sp/skills/spur-dev/references/flag-glossary.md +3 -3
  61. package/plugins/sp/skills/spur-dev/references/gate-checklists.md +3 -2
  62. package/plugins/sp/skills/spur-dev/references/glossary.md +12 -3
  63. package/plugins/sp/skills/spur-dev/references/planning-workflow.md +7 -6
  64. package/plugins/sp/skills/spur-doctor/SKILL.md +23 -0
  65. package/plugins/sp/skills/wayfinder/SKILL.md +11 -1
  66. package/spur.js +25617 -24290
  67. package/web/_astro/{BoardApp.Bjdp3XWV.js → BoardApp.CC7Mwswv.js} +65 -65
  68. package/web/_astro/{BoardApp.DCDZxLlQ.js → BoardApp.mWyrWCAR.js} +1 -1
  69. package/web/_astro/{TaskDetail.B3vBxynO.js → TaskDetail.CtAXuiv8.js} +1 -1
  70. package/web/_astro/{arc.DcuHTB_T.js → arc.Duf4u60i.js} +1 -1
  71. package/web/_astro/{architectureDiagram-3BPJPVTR.ClOFWBON.js → architectureDiagram-3BPJPVTR.JoymtqxN.js} +1 -1
  72. package/web/_astro/{blockDiagram-GPEHLZMM.otYyr7Zb.js → blockDiagram-GPEHLZMM.BvmhcioC.js} +1 -1
  73. package/web/_astro/{c4Diagram-AAUBKEIU.BCjpc9LP.js → c4Diagram-AAUBKEIU.CfaWkgoQ.js} +1 -1
  74. package/web/_astro/channel.DtEPXXcH.js +1 -0
  75. package/web/_astro/{chunk-2J33WTMH.D5Wpnpt_.js → chunk-2J33WTMH.B7uR02cD.js} +1 -1
  76. package/web/_astro/{chunk-4BX2VUAB.DkQGtWNz.js → chunk-4BX2VUAB.SwiEauYR.js} +1 -1
  77. package/web/_astro/{chunk-55IACEB6.B7dqjAip.js → chunk-55IACEB6.6ZRCK8PO.js} +1 -1
  78. package/web/_astro/{chunk-727SXJPM.BDQvnKE2.js → chunk-727SXJPM.Cxra_G3N.js} +1 -1
  79. package/web/_astro/{chunk-AQP2D5EJ.D92doBX5.js → chunk-AQP2D5EJ.Cxd7gZeX.js} +1 -1
  80. package/web/_astro/{chunk-FMBD7UC4.C968oI5G.js → chunk-FMBD7UC4.DCLhFkVc.js} +1 -1
  81. package/web/_astro/{chunk-ND2GUHAM.AkkYC1bY.js → chunk-ND2GUHAM.B9AmKcv6.js} +1 -1
  82. package/web/_astro/{chunk-QZHKN3VN.sCc-rKKo.js → chunk-QZHKN3VN.DOjCvl6S.js} +1 -1
  83. package/web/_astro/{classDiagram-4FO5ZUOK.pOQ9u0mY.js → classDiagram-4FO5ZUOK.BMiT_mNf.js} +1 -1
  84. package/web/_astro/{classDiagram-v2-Q7XG4LA2.pOQ9u0mY.js → classDiagram-v2-Q7XG4LA2.BMiT_mNf.js} +1 -1
  85. package/web/_astro/{cose-bilkent-S5V4N54A.CRaeD6FY.js → cose-bilkent-S5V4N54A.mLBCEGjI.js} +1 -1
  86. package/web/_astro/{cynefin-OW5HDTMX.BiCPBu54.js → cynefin-OW5HDTMX.CjDAY0qn.js} +1 -1
  87. package/web/_astro/{dagre-BM42HDAG.C1-q3vkT.js → dagre-BM42HDAG._G_sP8fP.js} +1 -1
  88. package/web/_astro/{diagram-2AECGRRQ.BSVOv30O.js → diagram-2AECGRRQ.39OcfSuz.js} +1 -1
  89. package/web/_astro/{diagram-5GNKFQAL.CHMkjJ-V.js → diagram-5GNKFQAL.D7v_eNC4.js} +1 -1
  90. package/web/_astro/{diagram-KO2AKTUF.CKY9M564.js → diagram-KO2AKTUF.Da1x-lhg.js} +1 -1
  91. package/web/_astro/{diagram-LMA3HP47.B9yNJiTP.js → diagram-LMA3HP47.C8EnzbEb.js} +1 -1
  92. package/web/_astro/{diagram-OG6HWLK6.Dk8AUGsk.js → diagram-OG6HWLK6.DPnBj69F.js} +1 -1
  93. package/web/_astro/{erDiagram-TEJ5UH35.BQHR89TZ.js → erDiagram-TEJ5UH35.CXMu2TIh.js} +1 -1
  94. package/web/_astro/{flowDiagram-I6XJVG4X.oNkWVgoK.js → flowDiagram-I6XJVG4X.ZklePVtQ.js} +1 -1
  95. package/web/_astro/{ganttDiagram-6RSMTGT7.Kz2U2nu9.js → ganttDiagram-6RSMTGT7.BhYOkdqk.js} +1 -1
  96. package/web/_astro/{gitGraphDiagram-PVQCEYII.DHIZ_wPq.js → gitGraphDiagram-PVQCEYII.CADyX8a1.js} +1 -1
  97. package/web/_astro/{infoDiagram-5YYISTIA.D0NJLcv9.js → infoDiagram-5YYISTIA.BsdmwjHt.js} +1 -1
  98. package/web/_astro/{ishikawaDiagram-YF4QCWOH.CFEfTWRG.js → ishikawaDiagram-YF4QCWOH.CzjZ3Crd.js} +1 -1
  99. package/web/_astro/{journeyDiagram-JHISSGLW.Dc7IE7I4.js → journeyDiagram-JHISSGLW.Bxj-ZH_t.js} +1 -1
  100. package/web/_astro/{kanban-definition-UN3LZRKU.D5v0xO9k.js → kanban-definition-UN3LZRKU.Cbg3wVvX.js} +1 -1
  101. package/web/_astro/{linear.wR5KzXnE.js → linear.DJNUrBI9.js} +1 -1
  102. package/web/_astro/{mermaid.core.BDQ2FoPd.js → mermaid.core.DVHQV3SF.js} +4 -4
  103. package/web/_astro/{mindmap-definition-RKZ34NQL.W6_INsia.js → mindmap-definition-RKZ34NQL.DyomosRU.js} +1 -1
  104. package/web/_astro/{pieDiagram-4H26LBE5.61ihbgkK.js → pieDiagram-4H26LBE5.DajtFknN.js} +1 -1
  105. package/web/_astro/{quadrantDiagram-W4KKPZXB.B5lEpFLo.js → quadrantDiagram-W4KKPZXB.Bjq6SjGe.js} +1 -1
  106. package/web/_astro/{requirementDiagram-4Y6WPE33.CmhVBVX0.js → requirementDiagram-4Y6WPE33.ubBVqGLP.js} +1 -1
  107. package/web/_astro/{sankeyDiagram-5OEKKPKP.D7Vvj9Wf.js → sankeyDiagram-5OEKKPKP.I4q46xd-.js} +1 -1
  108. package/web/_astro/{sequenceDiagram-3UESZ5HK.B1vPLixM.js → sequenceDiagram-3UESZ5HK.DsCgZIa8.js} +1 -1
  109. package/web/_astro/{stateDiagram-AJRCARHV.BnACmrGi.js → stateDiagram-AJRCARHV.DcYjYSqT.js} +1 -1
  110. package/web/_astro/{stateDiagram-v2-BHNVJYJU.CW4pnmc3.js → stateDiagram-v2-BHNVJYJU.Dt8yPqiS.js} +1 -1
  111. package/web/_astro/{timeline-definition-PNZ67QCA.CkzMCPnC.js → timeline-definition-PNZ67QCA.0YTkoa5F.js} +1 -1
  112. package/web/_astro/{vennDiagram-CIIHVFJN.CY4aNdAR.js → vennDiagram-CIIHVFJN.BNl0l1hs.js} +1 -1
  113. package/web/_astro/{wardleyDiagram-YWT4CUSO.BtoU9UFk.js → wardleyDiagram-YWT4CUSO.BYSX6cfn.js} +1 -1
  114. package/web/_astro/{xychartDiagram-2RQKCTM6._9ijUUAs.js → xychartDiagram-2RQKCTM6.DzxxpixK.js} +1 -1
  115. package/web/index.html +1 -1
  116. package/worker/laya_worker.py +219 -0
  117. package/config/workflows/feature-verification.yaml +0 -98
  118. package/config/workflows/pr-review.yaml +0 -209
  119. package/config/workflows/wayfinder-resolution.yaml +0 -302
  120. package/plugins/sp/skills/wayfinder/references/pipeline-resolution.md +0 -30
  121. package/web/_astro/channel.qgOLXjI_.js +0 -1
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: spur-cli-decision
3
- description: "spur-cli noun reference: operate `spur decision` as the read/serve surface over the decision catalog - list registered decisions, show one served contract (parameters, criteria vocabulary, effective maker), run a decision to a served outcome (never writes a workflow resultFile), and status the catalog layers with per-maker readiness marks (registered, configured, recorded, probed). Backed by @gobing-ai/ts-ai-decision 0.5.22 hub; offline by default via the built-in `typesafe` maker. Introduced by task 1093; readiness marks and `--probe` by feature P11."
3
+ description: "spur-cli noun reference: operate `spur decision` as the read/serve surface over the decision catalog - list registered decisions, show one served contract (parameters, criteria vocabulary, effective maker), run a decision to a served outcome (never writes a workflow resultFile), and status the catalog layers with per-maker readiness marks (registered, configured, recorded, probed). Backed by the @gobing-ai/ts-ai-decision hub; with no reachable backend every decision serves its declared fallback. Lifecycle (author, evaluate, triage, tune) lives in decision/lifecycle.md."
4
4
  see_also:
5
5
  - spur-cli
6
6
  ---
@@ -25,14 +25,16 @@ Decisions resolve first-wins across three layers:
25
25
  | `registered` | folders listed in config `decisions.paths` | Operator-added catalogs |
26
26
  | `shared` | `decisions/` under the bundled config root | Shipped catalogs (`config/decisions/` in-repo) |
27
27
 
28
- The bundled shared catalog ships `task-triage`, `failure-class`, `review-failure-class`,
28
+ The bundled shared catalog ships `task-semantic-risk`, `failure-class`,
29
29
  `gate-evidence`, `idea-recommendation`, `needs-design`, `anatomy-validation-verdict` and
30
30
  `maker-liveness` (the last served only by `spur decision status --probe`; no workflow references
31
- it). Duplicate ids across layers keep the higher layer; duplicates inside one file are a load error.
31
+ it). Layers win **per file basename** (a project `gates.yaml` replaces the shipped `gates.yaml`
32
+ whole); the same id in two winning files of different basenames is a `duplicateIds` catalog error.
32
33
 
33
34
  **Config keys** (under `decisions:` in the global `~/.config/spur/config.yaml` or project `.spur/config.yaml`):
34
35
  `paths` (extra catalog folders forming the `registered` layer), `maker` (default DecisionMaker
35
- for every decision point), `makers` (per-decision-id overrides). An unregistered maker name is a
36
+ for every decision point), `makers` (per-decision-id overrides), `log` (`full` | `metadata` | `off`,
37
+ how much of each served decision the ledger keeps). An unregistered maker name is a
36
38
  config error, never a silent fallback (design §3.6).
37
39
 
38
40
  ## Verb map
@@ -43,6 +45,7 @@ config error, never a silent fallback (design §3.6).
43
45
  | `show <id>` | One served contract: type, parameters, criteria, fallback, minConfidence, effective maker + source | `--json` |
44
46
  | `run <id>` | Serve one outcome (closed vocabulary; **never writes a resultFile**) | `--param <pairs...>`, `--evidence <files...>`, `--maker <name>`, `--json` |
45
47
  | `status` | Sectioned readiness report: one status line, errors, aligned `Makers` and `Decisions` tables (registration, configuration source, recorded evidence), `Catalog` block | `--probe`, `--reliability [--since <iso>]`, `--json` |
48
+ | `outcome <invocation-id>` | Record what actually happened after a served decision — acceptance is not accuracy (task 1263). First write wins | `<outcome>`, `--review-severity <wbs>`, `--gate-status <run-id>`, `--source <text>`, `--json` |
46
49
 
47
50
  All verbs support `--json` (bare payload) and `--json-envelope` (`{ok, data|error}` envelope,
48
51
  ADR-091). Failure output for `--json` commands uses the canonical error envelope only under
@@ -51,6 +54,62 @@ ADR-091). Failure output for `--json` commands uses the canonical error envelope
51
54
  **Exit codes:** `status` exits 1 on any catalog load error or maker-config error; `show` exits 1 on unknown id; `list` exits 0
52
55
  (design §3.4 — load errors surface via `status`) except for an invalid `--layer` value (exit 1).
53
56
  Error envelopes use `NOT_FOUND` for an unknown id and `VALIDATION_FAILED` for every other caller mistake.
57
+ `outcome` exits 1 for an unknown invocation id (there is no decision to score) and 2 for a malformed
58
+ outcome (it must match `^[a-z0-9-]{1,32}$`) or for naming zero/several outcome sources.
59
+
60
+ ## Acceptance versus accuracy (`outcome` + `--reliability`)
61
+
62
+ `decision.end` says **what was answered**; it never says whether the answer was right. A maker can
63
+ be accepted on every call and still add no information: `review-failure-class` answered its own
64
+ fallback 15/15 times at confidence 1.0 before task 1262 retired it. Tuning `minConfidence`, choosing
65
+ a maker, or judging a shadow maker therefore needs (value, outcome) pairs.
66
+
67
+ **Recording one.** A decide step now writes the invocation's ledger key into its result row
68
+ (`invocationId`, task 1263 R1) — the same id the `decision.*` events and the `decision_logs` row
69
+ carry. Once the outcome is known, record it:
70
+
71
+ ```bash
72
+ # an explicit label (lowercase slug): what a human or a later check observed
73
+ spur decision outcome <invocation-id> fixed --source "quality gate recheck"
74
+ # derived from a task's Review findings: the worst finding, resolved rows included (p1-p4, or none)
75
+ spur decision outcome <invocation-id> --review-severity 1263
76
+ # derived from a gate visit: PASS -> fixed, anything else (or a missing token) -> not-fixed
77
+ spur decision outcome <invocation-id> --gate-status <run-id>
78
+ ```
79
+
80
+ First write wins: a replay exits 0 with `recorded: false` and the outcome that already stands. The
81
+ row lands in `system_events` beside `decision.end`, on the same `run_id`, so the report joins it by
82
+ `invocationId`.
83
+
84
+ **Scoring one.** `decisions.outcomeMap` in config (project or global layer) holds Spur's reading of
85
+ Spur's own artifacts — per decision id, which recorded outcomes count as correct for each served
86
+ value (`String(value)` keys, so a yes/no decision uses `"true"`/`"false"`). The shipped defaults
87
+ score `failure-class` (`fix` → `fixed`, `stop` → `not-fixed`) and `task-semantic-risk`
88
+ (`"false"` → `none`/`p4`/`p3`, `"true"` → `p1`/`p2`).
89
+
90
+ `spur decision status --reliability` then reports, per (decision × maker) group:
91
+
92
+ | Field | Meaning |
93
+ | --- | --- |
94
+ | `samples`, `accepted`, `acceptedRate` | `decision.end` rows; acceptance only |
95
+ | `outcomes` | recorded outcomes by label over those samples |
96
+ | `accuracy` | correct accepted answers ÷ accepted answers **with an outcome**; null when the decision has no map entry or no accepted sample recorded one |
97
+ | `fallbackAccuracy` | the same ratio over fallback (`source: default`) samples |
98
+
99
+ `accuracy: null` is deliberate: an unmapped decision or an unobserved one is unknown, never zero.
100
+ Acceptance and accuracy answer different questions — a high `acceptedRate` with `accuracy: null`
101
+ means "the maker answered and we have not looked at whether it was right yet".
102
+
103
+ **Reading a sampled maker.** Sampled makers report unanimity, not calibration: with two options,
104
+ 2-of-3 agreement is ~0.08 confidence and the 0.8 floor only accepts when every sample agreed
105
+ (ts-libs `docs/design/llm-agent-decision-backend.md` § Confidence granularity). A 1.0 confidence on
106
+ that kind of maker therefore means "the samples agreed", not "the answer is calibrated".
107
+
108
+ **Retuning `minConfidence` from the report.** Read a group's `accuracy` and `fallbackAccuracy`
109
+ together with `fallbacks` by reason. Accepted-but-wrong answers point at raising the floor (or
110
+ changing the maker); fallbacks that were right point at lowering it. Change the floor in the
111
+ catalog entry (or `defaults.minConfidence`), record the new samples, and compare the same two
112
+ numbers — never one of them alone.
54
113
 
55
114
  ## Readiness marks (`status`, feature P11)
56
115
 
@@ -77,6 +136,57 @@ error surfaced as fallback, declined — is still a **successful serve** (exit 0
77
136
  **before** the backend: unknown id, unknown parameter, missing required parameter, type-invalid
78
137
  parameter, unregistered `--maker`, unreadable evidence file.
79
138
 
139
+ ## Executor failure rescue (`executor-failure-class`)
140
+
141
+ The agent executor escalates on `FAILURE_RULES` matches only (`packages/app/src/services/failure-classification.ts`).
142
+ A failure no rule matches — a reworded rate limit, "credits depleted", a proxy error body — ends
143
+ the run as a task defect until somebody adds a regex. `executor-failure-class` covers that residue:
144
+ `resource-exhaustion` / `auth` / `task-defect`, fallback `task-defect`, `minConfidence: 0.85`
145
+ (a false `resource-exhaustion` costs a failover attempt; a false `task-defect` equals today's
146
+ behaviour, so uncertainty must resolve downward).
147
+
148
+ **It ships off.** `workflows.executorFailureDecisionMaker: true` is the opt-in; without it the executor
149
+ makes no model call and the context carries no rescue seam at all. Turning it on is an operator
150
+ call backed by replay evidence (`spur decision run executor-failure-class --evidence <tail>` over
151
+ real unmatched tails), not a default.
152
+
153
+ The rescue runs only when every deterministic check declined: non-zero exit without a timeout
154
+ signal, `undefined` from `classifyDispatch`, no permission-denial evidence (a denial stays terminal),
155
+ no confirmed quota event, and non-blank output. It sees the failure text **redacted first, then
156
+ tailed** to 4096 characters — tailing first could cut a secret at the boundary and send the
157
+ fragment off-box, so never reorder that.
158
+
159
+ **Evidence leaves the machine with a hosted maker.** The tail is failure output from whatever the
160
+ agent printed, so a hosted maker (`clef`) sees it. Two consequences:
161
+
162
+ - Prefer a **probability-native** maker for this id (`decisions.makers.executor-failure-class`):
163
+ sampled makers accept only on unanimity (ts-libs `docs/design/llm-agent-decision-backend.md`
164
+ § Confidence granularity), so the 0.85 floor does not grade them.
165
+ - `laya-local` keeps the evidence on-box (it needs Apple Silicon, and since task 1262 R1 the bundled
166
+ CLI ships its worker, so `workerScript` resolves without a source checkout).
167
+
168
+ Every rescue is attributed `caller: executor` with the failing run's id, so it shows up in
169
+ `decision.*` events, `decision_logs` and `spur decision status --reliability` with no extra work.
170
+ To promote recurring text into a deterministic rule, list the accepted rescues:
171
+
172
+ ```bash
173
+ sqlite3 .spur/spur.db "
174
+ SELECT o.decision_id, json_extract(o.payload_json, '$.data.outcome') AS answer,
175
+ json_extract(s.payload_json, '$.data.workflowName') AS workflow, o.run_id
176
+ FROM system_events o
177
+ JOIN system_events s
178
+ ON s.event_name = 'decision.start'
179
+ AND json_extract(s.payload_json, '$.data.invocationId')
180
+ = json_extract(o.payload_json, '$.data.invocationId')
181
+ WHERE o.event_name = 'decision.end'
182
+ AND json_extract(o.payload_json, '$.data.caller') = 'executor'
183
+ AND json_extract(o.payload_json, '$.data.source') = 'model'
184
+ ORDER BY o.occurred_at DESC;"
185
+ ```
186
+
187
+ Add the `FAILURE_RULES` entry by hand, with a test — the rescue stays the safety net for what the
188
+ table still misses.
189
+
80
190
  ## Parameters and evidence
81
191
 
82
192
  `--param k=v` is repeatable and validated against the decision's declared parameters: values for
@@ -95,23 +205,41 @@ keys). An unreadable evidence file exits 1 before any backend call.
95
205
  Makers resolve `flag → config-decision → config-default → catalog-decision → catalog-default`;
96
206
  `decision show` reports the effective maker as `{name, source}` with that source precedence
97
207
  (operator config outranks the shipped catalog because only the machine owner knows which backends
98
- exist there). The final `catalog-default` maker is the hub built-in `typesafe` — decisions served
99
- without a configured backend still produce deterministic closed-vocabulary outcomes, which is why
100
- `run` is usable in tests and scripts with no model access.
208
+ exist there). The final `catalog-default` maker is the hub built-in `typesafe`, a **hosted** maker
209
+ that needs `TYPESAFE_API_KEY`. Without a reachable backend `run` still exits 0 with the declared
210
+ fallback (`source: default`, reason `no-backend`), which is why it is usable in tests and scripts
211
+ with no model access — the outcome is deterministic, not model-judged.
212
+
213
+ **Available makers:** the four upstream built-ins (`typesafe`, `fm-local`, `laya-local`,
214
+ `llm-agent`) plus Spur's hosted `clef` (Cloudflare Workers AI via `@gobing-ai/ts-decision-clef`).
215
+
216
+ - `llm-agent` samples an installed coding agent: by default the first installed tier-1 agent, 3
217
+ one-shot samples, accepted only when every sample agrees. Spur passes it no driver options, so
218
+ each ask costs several agent runs; name it per decision (`decisions.makers.<id>`) rather than as
219
+ the project-wide `decisions.maker`.
220
+ - `clef` registers only when both `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_AUTH_TOKEN` are set in
221
+ the environment; without them it is absent from `registeredMakers`, so naming it in config or
222
+ `--maker` is a config error, never a silent fallback. Its model defaults to `clef-flash` (a
223
+ catalog `model` selects `clef`).
224
+
225
+ ## Workflow consumption
101
226
 
102
- **Available makers:** the three upstream built-ins (`typesafe`, `fm-local`, `laya-local`) plus
103
- Spur's hosted `clef` (Cloudflare Workers AI via `@gobing-ai/ts-decision-clef`). `clef` registers
104
- only when both `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_AUTH_TOKEN` are set in the environment;
105
- without them it is absent from `registeredMakers`, so naming it in config or `--maker` is a config
106
- error, never a silent fallback. Its model defaults to `clef-flash` (a catalog `model` selects
107
- `clef`).
227
+ Workflows consume a decision through the catalog-reference `decide` action
228
+ (`{id, decision, params?, evidence? | instructions?, resultFile}`); the inline
229
+ `question`/`method`/`default` form is deprecated. `workflows.decideDecisionMaker` (default off)
230
+ gates model calls there — off, every `decide` serves its fallback. `hitl.confirm`/`hitl.select`
231
+ with `decision: {mode: evidence, …}` route through `gate-evidence` under the separate
232
+ `workflows.hitlDecisionMaker` switch (default off — the gate defers to the operator, or under
233
+ `profile=auto` to its declared fallback). Neither switch affects `spur decision run`. Shape, adoption classes and
234
+ guards: [decision/lifecycle.md](decision/lifecycle.md) §1, §3.
108
235
 
109
236
  ## Examples
110
237
 
111
238
  ```bash
112
239
  spur decision list --json | jq -r '.[].id'
113
- spur decision show task-triage # inspect contract before serving
114
- spur decision run task-triage --param wbs=1093 --json
240
+ spur decision show task-semantic-risk # inspect contract before serving
241
+ spur decision run task-semantic-risk --param wbs=1093 \
242
+ --evidence .spur/run/1093-diffstat.json --evidence docs/tasks/1093_*.md --json
115
243
  spur decision run failure-class --param wbs=1093 \
116
244
  --evidence .spur/run/<runId>-test-output.txt # redacted evidence -> instructions
117
245
  spur decision status --json | jq '.loadErrors' # audit catalog health
@@ -124,7 +252,9 @@ spur decision status --json | jq '.makers[] | {name, registered, recorded: .evid
124
252
 
125
253
  - Design: `docs/design/decision-catalog.md` (§2 catalog authoring, §3.4 CLI surface),
126
254
  `docs/design/decision-observability-and-adoption.md` (§3.4 reliability report, §3.4.1 readiness view)
127
- - Hub: `@gobing-ai/ts-ai-decision` 0.5.22 (`DecisionService`, catalog schema, maker registry)
255
+ - Lifecycle: [decision/lifecycle.md](decision/lifecycle.md) (create, wire, evaluate, triage, enhance)
256
+ - Hub: `@gobing-ai/ts-ai-decision`, version pinned by the root `workspaces.catalog` (`DecisionService`,
257
+ catalog schema, maker registry)
128
258
  - App service: `packages/app/src/decision/decision-service.ts` (makers, `probeMaker`),
129
259
  `packages/app/src/decision/decision-status-readiness.ts` (readiness marks, probe loop)
130
260
  - Shipped catalogs: `config/decisions/*.yaml`
@@ -272,12 +272,31 @@ spur feature sync H2 --folder docs/custom-tasks --json # non-default tasks fol
272
272
  task statuses, verdict mtimes); an identical next call replays the prior result
273
273
  (`suppressed: true`) instead of re-deriving. A changed input, `--force`, or a non-blocked
274
274
  outcome re-derives/clears. Dry-run never reads or writes the state.
275
+ - **Single-call completion after the verifying entry (1247):** entering `verifying` runs the
276
+ feature-verification pass (1246), but the hop list was derived before that pass recorded its
277
+ receipt. Sync re-derives **once** after a `verifying` hop lands, so a PASS receipt applies
278
+ `verifying → done` in the same call (`appliedHops: ['verifying','done']`). A FAIL or still-missing
279
+ receipt leaves the feature in `verifying` and still exits `0`; `proposal.reason` then names the
280
+ receipt finding and the rework edge (`spur feature update <id> active`). The re-derive
281
+ never runs when the verifying hop was not applied (guard denied, goal conflict, `--dry-run`).
275
282
  - **`--json`** single-feature emits `{ proposal, applied, appliedHops[], suppressed? }`; `--all` emits
276
283
  `{ totalFeatures, evaluated, updatedCount, results[] }` where each result is the single-feature
277
284
  shape. `proposal` is
278
285
  `{ featureId, from, to, reason, requiresConfirm?, gateBlocked?, gateFindings?, hops? }`.
279
286
  - **Exit codes:** `0` success (including NOOP), `1` error, `2` invalid usage (no id and no `--all`).
280
287
 
288
+ ## Lifecycle playbook
289
+
290
+ | Stage | How | Gate / evidence |
291
+ | --- | --- | --- |
292
+ | **Create** | `spur feature create <name> [--parent <id>]`; place it MECE in the tree ([features/hierarchy-mece.md](features/hierarchy-mece.md)) | `spur feature check <id> --json` exits 0 |
293
+ | **Evaluate** | AC quality ([features/acceptance-criteria.md](features/acceptance-criteria.md)), [traceability](#traceability-habits), roadmap fit ([features/roadmap-priority.md](features/roadmap-priority.md)) | `sp:spur-doctor` report cites `check` rows |
294
+ | **Triage** | Structural finding → `check --fix`; stale roster → `refresh`; status drift → `sync --dry-run` then `sync` | `check` clean; `sync --dry-run` empty |
295
+ | **Enhance** | `update --section` for AC/scope; `move --dry-run` before re-parenting a subtree | Every task still links to a live AC |
296
+
297
+ - A feature is a capability, not a work list; tasks own execution.
298
+ - Re-parent with `move`, never by renaming IDs by hand.
299
+
281
300
  ## What this skill is NOT
282
301
 
283
302
  - **Not the planning loop.** Intake → create → AC generation → check-loop → decomposition →
@@ -22,6 +22,8 @@ Operating the gate well is a full lifecycle — not just running it. This skill
22
22
  | **Run & fix** | Gate the diff, interpret findings, self-correct, re-run until green | this file |
23
23
  | **Author** | Add a constraint by writing a new rule when a standard/anti-pattern emerges | [rules/authoring-rules.md](rules/authoring-rules.md) |
24
24
  | **Fine-tune** | Dial in quality: severity, glob scoping, exemptions, preset `disable`/`overrides`, layering | [rules/fine-tuning.md](rules/fine-tuning.md) |
25
+ | **Evaluate** | `spur rule trace --last <n> --json` for hit rates and noise; `spur rule list --preset <name> --json` for what is enforced | this file, [rules/operations.md](rules/operations.md) |
26
+ | **Triage** | Start from `spur rule trace <run-id> --json`: false positive → scope/exempt; true positive → fix the code, never the rule | [rules/fine-tuning.md](rules/fine-tuning.md) |
25
27
  | **Validate & extend** | Validate files/presets, smoke-test, add custom evaluators/resolvers/formatters | [rules/validation-and-extension.md](rules/validation-and-extension.md) |
26
28
 
27
29
  ## When to use
@@ -51,7 +51,7 @@ re-reading or re-tokenizing the task.
51
51
  | `batch-create` | Create many tasks from a validated JSON array | `--file <path>` `--skip-ready` `--agent <selector>` `--folder` `--json` |
52
52
  | `batch-plan <mode>` | Batch plan projection: `waves` / `order` / `task-children` (JSON on stdout; the batch driver's visible plan) | `--tasks <file>` `--concurrency <n>` `--letter <A-Z>` `--plan <file>` |
53
53
  | `preflight [wbs]` | Read-only batch admission: TABLE A STOP check, quick readiness (`--operation`), one-shot recovery hop; status/deps read from the corpus unless given | `--status <s>` `--deps <list>` `--dep-status <map>` `--operation <op>` `--filtered-count <n>` `--required-sections <list>` `--present-sections <list>` `--force` `--recovery` `--classify-feature <json>` `--folder` `--json` |
54
- | `wrapup <op>` | One deterministic `wrapup-pipeline` step (`resolve`, `route-reason`, `metrics`, `feature-transition`, `doc-sync-snapshot`, `doc-sync-scope`, `doc-supersession`, `drift-probe`) — workflow-internal | `--run-id <id>` `--tasks <json>` `--mode <m>` `--feature <id>` `--feature-gate-cmd <cmd>` `--spur-bin <cmd>` |
54
+ | `wrapup <op>` | One deterministic `wrapup-pipeline` step (`resolve`, `route-reason`, `metrics`, `doc-sync-snapshot`, `doc-sync-scope`, `doc-supersession`, `drift-probe`) — workflow-internal | `--run-id <id>` `--tasks <json>` `--mode <m>` `--spur-bin <cmd>` |
55
55
  | `record <wbs>` | Write `Testing` from a verify verdict (deterministic); bare-`## Review` fallback only; optional Solution + transition | `--verdict-file <path>` `--solution-from-diff` `--transition <status>` `--folder` `--json` |
56
56
  | `verdict <wbs>` | Derive PASS/PARTIAL/FAIL/UNKNOWN from verify answer text → verdict JSON; see [answer-file shape](tasks/verbs.md#answer-file-shape-what---from-answer-parses) | `--from-answer <path>` `--folder` `--json` |
57
57
  | `check [wbs]` | Four-layer validation; `--fix` repairs structure; `--corpus` is the explicit unsuppressed audit; `--precheck` adds the pipeline precheck gate (size + evidence channel, WBS required) | `--strict` `--as <status>` `--strict-core` `--precheck` `--fix` `--folder` `--corpus` `--since <ref>` `--json` |
@@ -359,6 +359,19 @@ spur task path 0040 --json
359
359
  basename-WBS fallback. Exit `1` when no task owns the path.
360
360
  - `path <wbs>` is the inverse — absolute task file path for a WBS.
361
361
 
362
+ ## Lifecycle playbook
363
+
364
+ | Stage | How | Gate / evidence |
365
+ | --- | --- | --- |
366
+ | **Create** | `spur task create <title> --feature <id>` ([Creating tasks](#creating-tasks)), then fill sections ([tasks/section-editing.md](tasks/section-editing.md)) | `spur task check <wbs> --json` exits 0 |
367
+ | **Evaluate** | Readiness matrix ([check --json](#the-readiness-matrix--check---json)); `--precheck` before a pipeline run | `sp:spur-doctor` report cites the check rows |
368
+ | **Triage** | Structural finding → `check --fix`; L3 refusal → [tasks/l3-guard-cheatsheet.md](tasks/l3-guard-cheatsheet.md); batch admission → `spur task preflight <wbs> --json` | Re-run `check` until clean |
369
+ | **Enhance** | Replace one section from a temp file; batch refinement through `/sp:dev-refineall --feature <id> --auto` | Same `check` passes; AC still traces to the feature |
370
+
371
+ - One deliverable per task; split rather than stack ACs from two features.
372
+ - Every AC is observable and verifiable by a named command or file, never by self-report.
373
+ - Status moves only through `update`/`record`; never hand-edit frontmatter.
374
+
362
375
  ## What this skill is NOT
363
376
 
364
377
  - **Not the pipeline.** Driving a task through `task-pipeline.yaml`, HITL surfacing, and
@@ -138,7 +138,7 @@ Spur's host additionally registers (`registerSpurBuiltins`, `packages/app/src/wo
138
138
 
139
139
  | Kind | Capability |
140
140
  | ---- | ---------- |
141
- | Action | `agent.run` (model call via `spur agent run`; `executor: 'fleet'` dispatches into the agent fleet), `decide` (inline `id`/`method`/`question`/`choices`/`default`/`evidence` → outcome written to `resultFile`; catalog-id references are pending task 1094 — use `spur decision run` for catalog decisions), `command.gate`, `rule.check`, `doctor.probe`, `proof.fingerprint`, `run.artifact`, `file.exists`, `file.read`, `file.read.into-var`, `http.request`, `hitl.confirm`, `hitl.select`, `hitl.input`, `shell` (streaming) |
141
+ | Action | `agent.run` (model call via `spur agent run`; `executor: 'fleet'` dispatches into the agent fleet), `decide` (catalog reference `{id, decision, params?, evidence? \| instructions?, resultFile}` → a schemaVersion-1 row; gated by `workflows.decideDecisionMaker`, fallback row when off; the inline `question`/`method`/`default` form is deprecated, and a shell `spur decision run` bypasses the switch — see [decision lifecycle](../decision/lifecycle.md)), `command.gate`, `rule.check`, `doctor.probe`, `proof.fingerprint`, `run.artifact`, `file.exists`, `file.read`, `file.read.into-var`, `http.request`, `hitl.confirm`, `hitl.select`, `hitl.input`, `shell` (streaming) |
142
142
  | Guard | `shell` (env-aware), `contract-violation` |
143
143
 
144
144
  Order matters for both guards and conditions: **the first that passes wins.** Put the discriminating
@@ -53,8 +53,9 @@ when the run count justifies it. One or zero → **descriptive procedure**; say
53
53
  | "Retry until the gate passes", "loop until clean" | workflow — bounded loop is the engine's job |
54
54
  | "Pause for approval, continue later / in another session" | workflow — HITL resume needs a run record |
55
55
  | "Every night", "for each task in the batch", unattended | workflow — replay + record |
56
- | "Then check whether it looks right and decide" | prose — the predicate is judgment, not an exit code |
57
- | "It depends what the diff says" | prose — the branch has no machine-checkable condition |
56
+ | "Then check whether it looks right and decide" | prose — unless the answer is a closed set with a safe default; then a catalog `decide` step |
57
+ | "It depends what the diff says" | prose — unless a deterministic floor routes first and a catalog decision refines it (task-triage pattern) |
58
+ | "Classify the failure, then fix or stop" | workflow — a `decide` row the guard reads ([decision points](../../../spur-dev/references/decision-points.md)) |
58
59
  | Runs once, then the situation has changed | prose — a checklist, not a definition |
59
60
  | One operator, in-session, watching each step | prose — the trace has no second reader |
60
61
 
@@ -422,6 +422,15 @@ a **pipeline** (validate → dry-run → read trace → refine). It also covers
422
422
  extending the engine with custom actions/guards. It does not execute the orchestrated *work* itself —
423
423
  the workflow's actions (`shell`, custom runners) do that; this skill builds and operates the workflow.
424
424
 
425
+ ## Lifecycle playbook
426
+
427
+ | Stage | How | Gate / evidence |
428
+ | --- | --- | --- |
429
+ | **Create** | [Fit gate](#decide-it-is-a-workflow-at-all--before-the-mode), then the composer ladder (ephemeral → `.spur/workflows/` → shared); author per [workflows/authoring-workflows.md](workflows/authoring-workflows.md) | `spur workflow validate <file> --json` clean |
430
+ | **Evaluate** | Dry-run, then a real run; `spur workflow profile <workflow> --json` and `cost` for measured spend | [Composition measures](#composition-measures-and-the-advisory-0615) within budget |
431
+ | **Triage** | `spur workflow trace <run-id> --json` → the failing node; [workflows/operations.md](workflows/operations.md) for HITL/continue/cancel | Re-run reaches the node and passes |
432
+ | **Enhance** | Tune from measured evidence ([workflows/workflow-fit-and-tuning.md](workflows/workflow-fit-and-tuning.md)); judgement steps adopt a catalog `decide` ([decision/lifecycle.md](decision/lifecycle.md) §3) | Before/after `profile` pair |
433
+
425
434
  ## Gotchas
426
435
 
427
436
  1. **`kind` is required for transition-flow, optional for state-machine.** A missing `kind` silently
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: spur-composer
3
- description: "Select, compose and tune spur artifacts — tasks, features, rules, workflows, agent specs and project Board modules. Owns workflow catalog selection, the ephemeral→project→shared ladder, ADR-115 budgets, trace-driven rule tuning and applying accepted sp:spur-doctor proposals. Triggers: compose a workflow, tune a rule, add a Board module."
3
+ description: "Select, compose and tune spur artifacts — tasks, features, rules, workflows, decisions, agent specs and project Board modules. Owns workflow catalog selection, the ephemeral→project→shared ladder, ADR-115 budgets, trace-driven rule tuning and applying accepted sp:spur-doctor proposals. Triggers: compose a workflow, tune a rule, add a Board module."
4
4
  license: Apache-2.0
5
5
  version: 1.0.0
6
6
  metadata:
@@ -49,6 +49,7 @@ its own output and never runs a recurring loop — evaluation is the doctor's jo
49
49
  | feature | Apply accepted rows through `spur feature update --section --from-file`; keep acceptance criteria in Gherkin | [../spur-cli/references/features.md](../spur-cli/references/features.md) |
50
50
  | rule | The trace-driven tuning loop (§ Rule tuning loop) | [../spur-cli/references/rules.md](../spur-cli/references/rules.md) · [fine-tuning](../spur-cli/references/rules/fine-tuning.md) |
51
51
  | workflow | Catalog selection, the composition ladder, and the ADR-115 budgets (§ below) | [../spur-cli/references/workflows.md](../spur-cli/references/workflows.md) · [operations](../spur-cli/references/workflows/operations.md) |
52
+ | decision | Select an existing id, place new entries up the layer ladder, tune maker/criteria from reliability evidence, adopt in workflows by class (§ Decision composition) | [../spur-cli/references/decision.md](../spur-cli/references/decision.md) · [lifecycle](../spur-cli/references/decision/lifecycle.md) |
52
53
  | agent spec | Read through `spur agent list --specs`; fleet member specs are derived in memory from `agent.fleet` (never written to `.spur/agents/`) | [../spur-cli/references/agent.md](../spur-cli/references/agent.md) |
53
54
  | board module | Declare under `bootstrap.modules`, gated by the serve preflight (§ Board module composition) | [../spur-cli/references/serve.md](../spur-cli/references/serve.md) |
54
55
 
@@ -144,6 +145,24 @@ Start from trace evidence, never from a guess:
144
145
  5. `spur rule run` on the affected inputs (constitution T11 — affected inputs, not a corpus sweep).
145
146
  6. Trace again and compare. A tuning with no trace pair behind it is a preference.
146
147
 
148
+ ## Decision composition
149
+
150
+ Same select → compose → tune shape, applied to the decision catalog
151
+ ([lifecycle](../spur-cli/references/decision/lifecycle.md)):
152
+
153
+ 1. **Classify first.** keep-deterministic, rescue-only, adopt or keep-human (lifecycle §1). Only
154
+ adopt and rescue-only become a catalog entry.
155
+ 2. **Select.** `spur decision list --json`; reuse an id whose criteria fit.
156
+ 3. **Compose up the ladder.** `.spur/decisions/<file>.yaml` (project) → `config/decisions/` (shared,
157
+ recorded operator consent + `build:bundle`). A project file shadows the shipped file of the same
158
+ basename whole.
159
+ 4. **Tune from evidence.** Labelled `spur decision run --maker <name>` fixtures, then
160
+ `spur decision status --reliability --json`; one lever per pass (lifecycle §6). The maker
161
+ override goes in operator config, never the shipped catalog.
162
+ 5. **Adopt in a workflow.** One catalog-reference `decide` step per change, validated with
163
+ `spur workflow validate` and `spur workflow run --dry-run`; the fallback must stay safe with
164
+ `workflows.decideDecisionMaker` off.
165
+
147
166
  ## Applying doctor proposals
148
167
 
149
168
  `sp:spur-doctor` ([../spur-doctor/SKILL.md](../spur-doctor/SKILL.md)) returns a proposal table;
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: spur-dev
3
- description: 'The thin orchestration spine for the planning→execution lifecycle: intake, feature-check/batch-create gates, the execution pipeline (precheck→implement→test→review→verify→record→done), HITL gating. Dispatches competency skills; never inlines them. Triggers: "run the pipeline", "drive this task", "plan a feature end to end", "continue the pipeline run", or operating the full lifecycle.'
3
+ description: 'The thin orchestration spine for the planning→execution lifecycle: intake, feature-check/batch-create gates, the execution pipeline (precheck→implement→gate⇄remediate→review→done), catalog decision points, HITL gating. Dispatches competency skills; never inlines them. Triggers: "run the pipeline", "drive this task", "plan a feature end to end", "continue the pipeline run", or operating the full lifecycle.'
4
4
  license: Apache-2.0
5
5
  metadata:
6
6
  author: spur
7
- version: "1.1"
7
+ version: "1.2"
8
8
  platforms: "claude-code,codex,openclaw,opencode,antigravity"
9
9
  interactions:
10
10
  - pipeline
@@ -23,11 +23,9 @@ metadata:
23
23
  execution_steps:
24
24
  - precheck
25
25
  - implement
26
- - test
26
+ - gate
27
+ - remediate
27
28
  - review
28
- - approve
29
- - verify
30
- - record
31
29
  - done
32
30
  openclaw:
33
31
  emoji: "🔄"
@@ -80,7 +78,7 @@ Full procedure: **[references/planning-workflow.md](references/planning-workflow
80
78
 
81
79
  ```
82
80
  pick task (spur task list --json)
83
- → interactive omit/inline: read task-pipeline.yaml → drive actions + guards in host session
81
+ → interactive omit/inline: spur workflow run … executor=host → perform each returned work item
84
82
  → explicit/headless executor: spur workflow run task-pipeline.yaml --vars '{"wbs":"<wbs>"}'
85
83
  → on HITL pause: surface to operator; resume the selected driver from the paused state
86
84
  ```
@@ -88,7 +86,7 @@ pick task (spur task list --json)
88
86
  The pipeline (`kind: state-machine`) runs the work loop:
89
87
 
90
88
  ```
91
- precheck → implement[→escalate(HITL)] → test → review → approve(HITL) → verify → record → done
89
+ precheck → implement[⇄escalate(HITL)] → gate[⇄remediate] → review → done | failed
92
90
  ```
93
91
 
94
92
  **Escalation contract (0933).** The implement agent may pause on an operator question: it writes
@@ -97,6 +95,10 @@ run. Resume with `spur workflow continue --answer-text <answer>` — the guard a
97
95
  `.spur/run/<wbs>-escalation.md` and re-enters implement (transcript via `--escalation-file`).
98
96
  `maxEscalations` (default 2) bounds the loop; exhausted or empty answer → `failed`. Implement only.
99
97
 
98
+ **Decision points.** Lane, failure class, review failure class and approval are recorded
99
+ `spur decision` answers with deterministic fallbacks, read by the next guard. Report the row; never
100
+ re-judge it: **[references/decision-points.md](references/decision-points.md)**.
101
+
100
102
  Full procedure: **[references/execution-workflow.md](references/execution-workflow.md)**.
101
103
  Host executor: `spur workflow run` with `executor=host` → work item → `spur workflow continue`.
102
104
 
@@ -112,9 +114,6 @@ Read this `SKILL.md` plus your mode's files below before the first dispatch; eve
112
114
 
113
115
  `--mode auto` reads as sequential-inline until § 2.4a resolves it.
114
116
 
115
- Byte budgets for these sums live in `plugins/sp/tests/bootstrap-budget.test.ts`
116
- (`BOOTSTRAP_BUDGETS_BYTES`, task 1128); the test fails the build when a mode's bootstrap grows.
117
-
118
117
  ## Step routing
119
118
 
120
119
  Each step delegates to a CLI verb and is documented in exactly one reference file. Read the
@@ -133,8 +132,9 @@ reference for the half you're operating; do not duplicate its content here.
133
132
  | Task selection | execution | `spur task list` | [execution-workflow.md](references/execution-workflow.md) |
134
133
  | Pipeline run | execution | `spur workflow run` / `spur workflow continue` | [execution-workflow.md](references/execution-workflow.md) |
135
134
  | Implement (dispatch) | execution | `sp:code-implementation` | competency skill — the spine dispatches, does not inline |
136
- | Test (dispatch) | execution | `sp:code-testing` | competency skill — the spine dispatches, does not inline |
137
- | Review / verify (dispatch) | execution | `sp:dev-review` → `sp:code-verification` + `sp:functional-review` + `sp:code-improvement` | competency skills — the spine dispatches, does not inline |
135
+ | Gate / remediate | execution | `spur task gate` + `sp:dev-fixall` | [execution-workflow.md](references/execution-workflow.md) |
136
+ | Review (dispatch) | execution | `sp:dev-review` → `sp:code-verification` + `sp:functional-review` + `sp:code-improvement` | competency skills — the spine dispatches, does not inline |
137
+ | Decision points | execution | `spur decision status` (+ `show`, `run`) | [decision-points.md](references/decision-points.md) |
138
138
  | Operation catalog | execution | `sp:dev-*` operations | [dev-operations.md](references/dev-operations.md) (spine dispatch table) |
139
139
  | Continue | execution | `spur feature update` / `refresh` | [execution-workflow.md](references/execution-workflow.md) |
140
140
  | Batch run | execution | `sp:super-planner` + `spur workflow run` | [execution-batch.md](references/execution-batch.md) |
@@ -193,8 +193,8 @@ CLI does.
193
193
  you ship corrupted corpus.
194
194
  2. **One writer per evidence section (F92 0593 R1).** `## Solution` is authored by the
195
195
  implement step; `## Review` by the review coordinator (`/sp:dev-review`); `## Testing` by the
196
- deterministic `record` step from the verdict artifact (`record` backfills `## Review` only when
197
- bare — fallback-only, never an overwrite). During execution, do not hand-edit another stage's
196
+ deterministic `spur task record` close in `done` from the verdict artifact (it backfills
197
+ `## Review` only when bare — fallback-only, never an overwrite). During execution, do not hand-edit another stage's
198
198
  section.
199
199
  3. **Resolve task IDs through the CLI.** Read a known WBS with `spur task show <wbs> --json`; it
200
200
  returns metadata, full content, and `filePath` across configured task folders. Use `spur task
@@ -226,7 +226,7 @@ section adds per-file content summaries and items with no single step (glossary,
226
226
  five gates (feature-check, batch-create, precheck, review, verify).
227
227
  - [references/section-batching.md](references/section-batching.md) — the evidence-section
228
228
  **one-writer protocol** (F92 0593 R1/R2): implement owns `Solution`, the review coordinator owns
229
- `Review`, deterministic `record` owns `Testing`; skills query `spur task sections` /
229
+ `Review`, the deterministic `spur task record` close owns `Testing`; skills query `spur task sections` /
230
230
  `spur task check` instead of static section tables.
231
231
  - [references/ac-style-guide.md](references/ac-style-guide.md) — BDD scenario authoring:
232
232
  R-numbering, the two AC tiers, scenario-title stability, Gherkin template usage.
@@ -127,7 +127,8 @@ scenario, it matches by title. Rules:
127
127
  - **No synonyms in cross-references.** The title in the feature file and the title in the
128
128
  task's AC reference must be byte-identical.
129
129
  - **Avoid gate vocabulary in titles.** `spur task check` (L4.gate-language) rejects task sections
130
- containing `HITL`, `merge event`, `content-gate`, `GATED`, or `capstone` as standalone words;
130
+ containing `HITL`, `merge event`, `content-gate`, `GATED`, or `capstone` as standalone words in any
131
+ case (dotted action kinds such as `hitl.confirm` are fine);
131
132
  ambiguous words (`approval`, `approved`, `merged`) warn only when the same sentence also carries
132
133
  a gating cue (`until`, `after`, `once`, `before`, `pending`, `blocked`, `wait for`, `requires`)
133
134
  — plain composition prose like "the stages merged back into one step" stays silent. Task AC
@@ -181,14 +181,14 @@ spawned step resolves to `agent.default` (step 2) or the YAML literal (step 3).
181
181
 
182
182
  `task-pipeline.yaml` declares a separate `implementAgent` var. The `implement` state's `agent.run`
183
183
  step reads `${vars.implementAgent}` instead of `${vars.agent}`, so an operator can pin the
184
- implement hop to one executor while review/verify/test-fix hops keep the default agent:
184
+ implement hop to one executor while the remediate and review hops keep the default agent:
185
185
 
186
186
  ```bash
187
187
  # Pin implement to a specific executor; other hops keep agent.default / YAML literal
188
188
  --vars '{"implementAgent":"omp-zai"}'
189
189
  ```
190
190
 
191
- All other `agent.run` steps (test-fix, review, verify) read `${vars.agent}`. `implementAgent`
191
+ All other `agent.run` steps (remediate, review) read `${vars.agent}`. `implementAgent`
192
192
  applies **only** to the implement hop. **The `--agent` flag forwards into BOTH `agent` and
193
193
  `implementAgent`** at the execution-batch boundary (§3.2), so a pinned `--agent X` reaches every
194
194
  hop including implement. To pin ONLY implement to a different executor while other hops keep the
@@ -0,0 +1,97 @@
1
+ ---
2
+ name: decision-points
3
+ description: "The lifecycle's catalog decision points: which workflow state asks which `spur decision` id, its fallback, the row it writes and the guard that reads it — plus the rules for consuming, diagnosing and adding one."
4
+ see_also:
5
+ - spur-dev
6
+ - execution-workflow
7
+ - spur-cli
8
+ - spur-composer
9
+ ---
10
+
11
+ # Decision points
12
+
13
+ A **decision point** is a closed-vocabulary judgment the workflow asks through `spur decision`
14
+ instead of through an agent's prose. It names a catalog id, carries a deterministic fallback, and
15
+ writes a schemaVersion-1 row (`value`, `source: model|default`, `degraded`, `confidence`,
16
+ `confidenceLevel`, `reason`) that the next guard or projection reads. The verb catalog, maker
17
+ resolution and the authoring lifecycle live in `sp:spur-cli`
18
+ ([decision.md](../../spur-cli/references/decision.md),
19
+ [decision/lifecycle.md](../../spur-cli/references/decision/lifecycle.md)); this file is the
20
+ lifecycle's map of where each one sits.
21
+
22
+ ## Inventory
23
+
24
+ | Workflow · state | Decision id | Values (fallback) | Row | Consumer |
25
+ | --- | --- | --- | --- | --- |
26
+ | task-pipeline · `gate` | `task-triage` | low · standard · high (`standard`) | `.spur/run/<wbs>-triage.decision` | lane projection → `<wbs>-mode.txt` → review focus. Runs only after the deterministic floor (caller `mode`, sensitive path, > 400 lines) has not already pinned the lane |
27
+ | task-pipeline · `gate` | `failure-class` | fix · stop (`fix`) | `.spur/run/<wbs>-failure-class.decision` | red gate: `stop` → `failed`, `fix` → `remediate` (under the attempt cap) |
28
+ | task-pipeline · `review` | `review-failure-class` | fix · stop (`fix`) | `.spur/run/<wbs>-review-failure-class.decision` | non-PASS review: `stop` → `failed`, `fix` → `remediate` (same cap) |
29
+ | task-pipeline · `review` | `gate-evidence` (approval `hitl.confirm`) | yes · no · defer (`defer`) | `.spur/run/<wbs>-approval.status` (APPROVED · DENIED) | `done` requires APPROVED; DENIED → `failed` (`cancelled`) |
30
+ | idea-pipeline · discovery | `idea-recommendation` | proceed · reshape · drop · unknown (`unknown`) | `.spur/run/<runId>-idea-recommendation.decision` | rescue only: consulted when the awk parse says `unknown`; only a `source: model` answer is projected |
31
+ | idea-pipeline · discovery | `needs-design` | design · skip (`design`) | `.spur/run/<runId>-idea-needs-design.decision` | rescue only: used when the report's boolean is missing; anything but a model `skip` keeps `design` |
32
+ | idea-pipeline · `idea-eval`, `design-approval` | `gate-evidence` | yes · no · defer (`defer`) | `__hitlAnswer` → `<runId>-idea-eval-answer.txt` etc. | a deferred answer is a denial there |
33
+ | wrapup-pipeline · `branch-cleanup` | `gate-evidence` | yes · no · defer (`defer`) | `branchCleanupStatus` | consent record only; wrap-up performs no git operation |
34
+ | history-anatomy · validate | `anatomy-validation-verdict` | PASS · FAIL (`FAIL`) | shell `spur decision run` (not a `decide` row) | rescue for an unparseable validation verdict; a deterministic FAIL is never overturned |
35
+
36
+ `maker-liveness` is served only by `spur decision status --probe`. `spur decision list --json` is
37
+ the live inventory; when it and this table disagree, the CLI wins and this table is stale.
38
+
39
+ ## Switches
40
+
41
+ - **`workflows.decideDecisionMaker`** (default off) gates every `decide` action. Off, the action
42
+ makes no model call and writes the fallback row (`source: default`). The pipeline still routes:
43
+ a red gate is `fix` and goes to the bounded repair, the lane is `standard`.
44
+ - **`workflows.hitlDecisionMaker`** (default off) gates the evidence-mode HITL gates. Off,
45
+ interactive gates ask the operator; under `profile=auto` the gate's answer is deferred, which
46
+ task-pipeline treats as no objection and idea-pipeline treats as a denial.
47
+ - **`spur decision run` ignores both switches.** A workflow that shells out to it calls a model
48
+ even with the switches off. That is why the typed `decide` action is the preferred form.
49
+
50
+ ## Rules for the lifecycle
51
+
52
+ 1. **Read the row; never re-judge it.** When a run reports `failed`, `remediate` or a lane, the
53
+ reason is the recorded `.decision` row or the approval token. Quote its `value`, `source` and
54
+ `reason`. Do not overrule a `stop` by hand-driving `remediate`, and do not treat a `source:
55
+ default` row as a model verdict.
56
+ 2. **A fallback row is a real outcome.** `source: default` (switch off, `no-backend`, low
57
+ confidence, invalid answer) is what the run acted on. Report it as degraded, not as an error.
58
+ 3. **Outside a pipeline, ask the catalog.** For a question a catalog id already owns (is this
59
+ red gate fixable? which lane does this diff need?), run `spur decision show <id>` and then
60
+ `spur decision run <id> --param … --evidence … --json` instead of improvising an answer. Never
61
+ let a `spur decision run` answer substitute for a gate the workflow owns.
62
+ 4. **Recurring judgment → a decision point, not prose.** A judgment that has a closed set of
63
+ answers and a safe default, recurs across runs, and currently lives in an agent prompt or a
64
+ `case` over free text belongs in a catalog entry plus a `decide` step. Author it through the
65
+ decision lifecycle (decision/lifecycle.md) and `sp:spur-composer`. A new shared catalog id
66
+ needs recorded operator consent (composition ladder §7) and `build:bundle`.
67
+ 5. **Keep deterministic floors first.** A decision point rescues or refines a deterministic
68
+ check; it never replaces one. task-triage runs after the sensitive-path/size floor, and the
69
+ idea-pipeline decides run after the parse they rescue.
70
+
71
+ ## Reporting a run
72
+
73
+ After a task-pipeline run settles, `/sp:dev-run`, `/sp:dev-runall` and `sp:super-planner` report
74
+ its decision points under the execution summary, one line per row, plus the approval token:
75
+
76
+ ```bash
77
+ for f in .spur/run/<wbs>-{triage,failure-class,review-failure-class}.decision; do
78
+ [ -f "$f" ] && jq -r --arg f "${f##*/}" '"\($f): \(.value) — \(.source // (if .degraded then "default" else "model" end)) \(.backend // "-") \(.reason)"' "$f"
79
+ done
80
+ printf 'approval: %s\n' "$(cat .spur/run/<wbs>-approval.status 2>/dev/null || echo none)"
81
+ ```
82
+
83
+ Rows outlive their run: a row's presence does not prove this run reached that state (the run's
84
+ trace does). Rows written before `source` existed carry only `degraded`; the fallback above reads it.
85
+
86
+ ## Diagnosis
87
+
88
+ | Question | Evidence |
89
+ | --- | --- |
90
+ | Why did this run take that route? | The row file in the table above; `spur workflow trace <run-id> --json` |
91
+ | Is a maker serving at all? | `spur decision status --probe` |
92
+ | How reliable is a decision in practice? | `spur decision status --reliability [--since <date>] --json` — per decision × maker: samples, acceptedRate, fallbacks by reason, median confidence, p50/p95 latency |
93
+ | Which runs asked it? | Board → Decisions tab (`decision_logs`) |
94
+ | Is the catalog healthy? | `spur decision status --json` → `loadErrors`, `makers[]` |
95
+
96
+ A decision whose rows are almost all `source: default` while its switch is on has a maker
97
+ problem, not a workflow problem. Hand it to `sp:spur-doctor`, which owns the proposal.