@gobing-ai/spur 0.3.63 → 0.3.64
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.
- package/.claude-plugin/marketplace.json +1 -1
- package/config/corpus-baseline.json +3326 -3886
- package/config/workflows/history-anatomy.yaml +2 -2
- package/package.json +9 -9
- package/plugins/sp/README.md +6 -3
- package/plugins/sp/agents/expert-spur.md +20 -4
- package/plugins/sp/commands/dev-find-issue.md +1 -1
- package/plugins/sp/commands/dev-gitmsg.md +12 -6
- package/plugins/sp/commands/dev-gtd.md +8 -19
- package/plugins/sp/commands/dev-idea.md +1 -1
- package/plugins/sp/commands/dev-plan.md +1 -1
- package/plugins/sp/commands/dev-run.md +2 -2
- package/plugins/sp/commands/dev-runall.md +2 -2
- package/plugins/sp/commands/dev-wrap.md +5 -6
- package/plugins/sp/commands/dev-wrapall.md +5 -7
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/history-anatomy-cache.mjs +3 -2
- package/plugins/sp/scripts/history-anatomy-cache.ts +7 -2
- package/plugins/sp/skills/dogfood-testing/SKILL.md +14 -1
- package/plugins/sp/skills/history-anatomy/references/report-contract.md +8 -0
- package/plugins/sp/skills/next-router/SKILL.md +4 -4
- package/plugins/sp/skills/pr-reviewing/SKILL.md +2 -3
- package/plugins/sp/skills/redesign-web-ui/SKILL.md +184 -0
- package/plugins/sp/skills/redesign-web-ui/references/audit-checklist.md +121 -0
- package/plugins/sp/skills/redesign-web-ui/references/upgrade-techniques.md +66 -0
- package/plugins/sp/skills/spur-cli/references/agent.md +1 -1
- package/plugins/sp/skills/spur-cli/references/workflows/authoring-workflows.md +5 -0
- package/plugins/sp/skills/spur-cli/references/workflows/operations.md +20 -5
- package/plugins/sp/skills/spur-cli/references/workflows/workflow-fit-and-tuning.md +230 -0
- package/plugins/sp/skills/spur-cli/references/workflows.md +26 -4
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +22 -30
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +62 -20
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +1 -1
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +18 -8
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +7 -6
- package/spur.js +297 -214
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: workflow-fit-and-tuning
|
|
3
|
+
description: Decide whether a process should be a spur workflow at all, tune an accepted one for latency and observability, keep its nodes inside the simplicity budget, and refactor across the boundary — promote a descriptive procedure into YAML, demote a YAML back to prose, or optimize one in place.
|
|
4
|
+
see_also:
|
|
5
|
+
- spur-cli
|
|
6
|
+
- operations
|
|
7
|
+
- authoring-workflows
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Workflow Fit & Tuning
|
|
11
|
+
|
|
12
|
+
Three questions that sit **before and around** the mode gate:
|
|
13
|
+
|
|
14
|
+
1. **Fit** — should this be a `spur workflow` at all, or a descriptive procedure / checklist?
|
|
15
|
+
2. **Tuning** — given it is one, how is it configured for low latency and legible traces?
|
|
16
|
+
3. **Refactor** — how do you move a process across that boundary in either direction?
|
|
17
|
+
|
|
18
|
+
**Polaris:** get things done with high efficiency, low latency, and observability. A workflow that
|
|
19
|
+
does not beat prose on all three is prose with a YAML tax.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 1. Fit gate — workflow or prose?
|
|
24
|
+
|
|
25
|
+
Run this **before** the [mode-selection gate](operations.md#sub-procedure-mode-selection-gate). The
|
|
26
|
+
mode gate answers *which kind of workflow*; it presumes an answer to *whether* — and that
|
|
27
|
+
presumption is the more expensive one to get wrong.
|
|
28
|
+
|
|
29
|
+
### What each side actually buys
|
|
30
|
+
|
|
31
|
+
| | `spur workflow` YAML | Descriptive procedure (skill reference, slash command, checklist) |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| Buys you | Durable run record, resumable HITL pause, bounded retry loops, machine-readable terminal status, `trace`/`--follow`, `cancel`/`clean`, unattended `--async` | Judgment at every step, zero authoring ceremony, edits are one-line, no subprocess per step |
|
|
34
|
+
| Costs you | Authoring + `validate`/`dry-run` upkeep, one subprocess per action node, indirection an agent must read through, a definition that rots when the surface moves | Nothing is replayable, nothing is recorded, "what happened on that run" is unanswerable |
|
|
35
|
+
| Fails at | Steps whose outcome only a reader can judge | Anything that must run the same way twice, unattended |
|
|
36
|
+
|
|
37
|
+
### The three-part test
|
|
38
|
+
|
|
39
|
+
A process earns a workflow only when **all three** hold:
|
|
40
|
+
|
|
41
|
+
- **Replay** — it runs repeatedly, unattended, or across different operators.
|
|
42
|
+
- **Branch** — at least one step routes on a *machine-checkable* predicate (exit code, JSON field,
|
|
43
|
+
file present), and the run may retry or gate on it.
|
|
44
|
+
- **Record** — someone will later need to answer "what happened on run X" from persisted evidence.
|
|
45
|
+
|
|
46
|
+
Three yes → author the workflow. Two → borderline: prefer prose plus one shell script, and revisit
|
|
47
|
+
when the run count justifies it. One or zero → **descriptive procedure**; say so and stop.
|
|
48
|
+
|
|
49
|
+
### Signals
|
|
50
|
+
|
|
51
|
+
| Signal in the described process | Read as |
|
|
52
|
+
| --- | --- |
|
|
53
|
+
| "Retry until the gate passes", "loop until clean" | workflow — bounded loop is the engine's job |
|
|
54
|
+
| "Pause for approval, continue later / in another session" | workflow — HITL resume needs a run record |
|
|
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 |
|
|
58
|
+
| Runs once, then the situation has changed | prose — a checklist, not a definition |
|
|
59
|
+
| One operator, in-session, watching each step | prose — the trace has no second reader |
|
|
60
|
+
|
|
61
|
+
### The two anti-patterns
|
|
62
|
+
|
|
63
|
+
- **Prose wearing YAML.** Every node is an `agent.run` with a raw prompt, edges are all
|
|
64
|
+
unconditional. The engine adds a process spawn per node and contributes no branching, no gate, no
|
|
65
|
+
retry. This is the ADR-069 R2 measure firing on *every* node — the advisory is telling you the
|
|
66
|
+
whole file is misplaced, not that eight prompts need polish. Demote it.
|
|
67
|
+
- **YAML wearing prose.** A skill reference that says "then run the checks, and if they fail fix and
|
|
68
|
+
re-run, up to three times" — a bounded retry loop written as a paragraph an agent re-interprets
|
|
69
|
+
every session, with no record of how many attempts actually happened. Promote it.
|
|
70
|
+
|
|
71
|
+
### Hybrid is the usual answer
|
|
72
|
+
|
|
73
|
+
The boundary runs *through* most processes, not around them. Keep judgment in the skill or slash
|
|
74
|
+
command; put the deterministic gate loop in the workflow; let the workflow's judgment steps call the
|
|
75
|
+
command by name. That is the ADR-043 preference restated as an architecture: **the workflow selects
|
|
76
|
+
and orders capabilities, it does not contain them** (ADR-069).
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 2. Tuning — latency, efficiency, observability
|
|
81
|
+
|
|
82
|
+
### Cost model — know what you are spending
|
|
83
|
+
|
|
84
|
+
| Node / element | Real cost | Consequence for design |
|
|
85
|
+
| --- | --- | --- |
|
|
86
|
+
| `agent.run` action | A full agent session: seconds to minutes, plus tokens | **The dominant cost.** Count them; every one you remove is the largest single win available |
|
|
87
|
+
| `shell` action | One `sh -c` subprocess, milliseconds | Cheap individually — but each node is also a persisted transition round-trip |
|
|
88
|
+
| Guard (`kind: shell`) | One subprocess **per evaluation**, re-run on every loop iteration | A loop with a bound of 5 pays its guards 5 times |
|
|
89
|
+
| Loop `iterationBound` | Worst case = bound x per-iteration cost | It is a **latency ceiling**, not only a runaway safety net |
|
|
90
|
+
| HITL pause | Unbounded wall-clock (waits on a human) | Never put one inside a loop body |
|
|
91
|
+
|
|
92
|
+
### Latency checklist
|
|
93
|
+
|
|
94
|
+
- [ ] **Fewest `agent.run` nodes that still do the work.** Two adjacent judgment steps with no gate
|
|
95
|
+
between them are one judgment step.
|
|
96
|
+
- [ ] **Soft status-file probe over repeated probing.** Run the expensive check once in an action
|
|
97
|
+
that always exits 0 and writes its verdict to a run-scoped file; branch with ordered cheap
|
|
98
|
+
guards that read that file. One subprocess instead of one per branch — the `basic.yaml` and
|
|
99
|
+
`task-pipeline.yaml` quality-gate idiom.
|
|
100
|
+
- [ ] **Order guards cheapest-discriminating-first.** The first passing guard wins, so a `test -f`
|
|
101
|
+
ahead of a `spur … --json` parse skips the expensive call on the common path.
|
|
102
|
+
- [ ] **No guard recomputes what a prior node already wrote to disk.**
|
|
103
|
+
- [ ] **`iterationBound` from the budget, not from optimism.** Set it to the observed maximum plus
|
|
104
|
+
one; a bound of 10 on a 40-second gate is a seven-minute worst case nobody chose.
|
|
105
|
+
- [ ] **Fan out only genuinely independent branches** (`type: parallel`, transition-flow). Serial
|
|
106
|
+
nodes with no data dependency are pure dead latency.
|
|
107
|
+
- [ ] **`--async` when the caller does not need the terminal state inline**, then follow with
|
|
108
|
+
`spur workflow trace <run-id> --follow`.
|
|
109
|
+
|
|
110
|
+
### Observability is an authoring decision, not a run flag
|
|
111
|
+
|
|
112
|
+
The flags (`--detail`, `--verbose`, `--trace-file`, `--follow`, `--output`) are catalogued in
|
|
113
|
+
[../workflows.md](../workflows.md); they only expose what the definition already made legible.
|
|
114
|
+
|
|
115
|
+
- [ ] **Name states/nodes after the outcome they establish, not the tool they invoke.**
|
|
116
|
+
`quality-gate-passed` reads at 3am; `run-script-2` does not.
|
|
117
|
+
- [ ] **`description` carries the WHY** of the workflow; a one-line comment carries the why of any
|
|
118
|
+
non-obvious guard order or `iterationBound`.
|
|
119
|
+
- [ ] **Every gate writes a machine-readable verdict to a run-scoped file.** It is the guard's input
|
|
120
|
+
*and* the post-mortem's evidence — one artifact, two consumers.
|
|
121
|
+
- [ ] **Declare `failureStates`** so a failed run reports `status: 'failed'` instead of finalizing as
|
|
122
|
+
a `done` run that landed somewhere bad (the 0425 reader contract).
|
|
123
|
+
- [ ] **`env.allow` lists exactly what is used** — no more; an over-broad allowlist is an
|
|
124
|
+
undocumented dependency.
|
|
125
|
+
- [ ] **While tuning:** `--detail full` plus `--trace-file`, and compare traces. **In production:**
|
|
126
|
+
default detail, retained run log.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 3. Node simplicity budget
|
|
131
|
+
|
|
132
|
+
Simplicity is the operating constraint, and it is already measurable — `spur workflow validate`
|
|
133
|
+
reports it. Do not invent a second threshold; author to the one that is frozen (ADR-069, task 0614).
|
|
134
|
+
|
|
135
|
+
| Element | Budget | What breaching it means |
|
|
136
|
+
| --- | --- | --- |
|
|
137
|
+
| `shell` action `command` | **<= 5** non-comment units (split on newline and `;`) | >= 6 flags the composition advisory: the program holds reusable behavior that wants an owner |
|
|
138
|
+
| `agent.run` action `input` | A **slash command or skill invocation** | A raw prose prompt flags: the operation belongs behind a centralized command (ADR-043). Prompt length sets severity only |
|
|
139
|
+
| Transition guard | **One** boolean predicate | Guards are exempt from the shell measure by design. A guard needing five lines is a probe node in disguise — make it one |
|
|
140
|
+
| Node count | Every node earns its transition round-trip | A node that always runs immediately after another, with no guard between them, is one node |
|
|
141
|
+
|
|
142
|
+
**When a node breaches the budget, do not reformat to dodge the measure.** Joining five lines with
|
|
143
|
+
`&&` moves the complexity, not the ownership. Pick an owner from the five recorded options in
|
|
144
|
+
`docs/design/workflow-shell-ownership.md`: public `spur` verb (consent-gated), application service,
|
|
145
|
+
least-privilege built-in action kind, workflow-relative external extension, or a recorded
|
|
146
|
+
stays-shell exception with its reason in `config/workflow-composition-baseline.json`.
|
|
147
|
+
|
|
148
|
+
**Advisory posture is binding.** Composition findings never block a run, never change a `validate`
|
|
149
|
+
exit status, and are never a reason to hot-edit an executing pipeline. Surface them; fix on operator
|
|
150
|
+
acceptance.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 4. Refactor — moving across the boundary
|
|
155
|
+
|
|
156
|
+
Three named directions. All three end in the shared
|
|
157
|
+
[validate-and-dry-run core](operations.md#sub-procedure-validate-and-dry-run) when a YAML definition
|
|
158
|
+
survives the change.
|
|
159
|
+
|
|
160
|
+
### promote — descriptive procedure → workflow
|
|
161
|
+
|
|
162
|
+
Use only when the [fit gate](#the-three-part-test) clears all three parts. Steps:
|
|
163
|
+
|
|
164
|
+
1. **Split the prose into spine and judgment.** The spine is every step whose outcome is
|
|
165
|
+
machine-checkable. Everything else stays judgment and does *not* become a node's inline prompt.
|
|
166
|
+
2. **Name the terminals** — the success terminal, and every failure terminal that deserves its own
|
|
167
|
+
name. Declare them in `failureStates`.
|
|
168
|
+
3. **Run the [mode-selection gate](operations.md#sub-procedure-mode-selection-gate)** — the spine's
|
|
169
|
+
shape decides it: retry loop → state-machine; forward pipeline → transition-flow.
|
|
170
|
+
4. **Author one node per spine step.** Each judgment step becomes an `agent.run` whose `input`
|
|
171
|
+
*references the existing slash command or skill* — never a copy of the prose. If no such command
|
|
172
|
+
exists, create it first; a promotion that inlines prompts has produced anti-pattern one.
|
|
173
|
+
5. **Verify** with validate-and-dry-run against the expected terminal.
|
|
174
|
+
6. **Rewrite the descriptive doc as the entry point** — it now explains the WHY and delegates to
|
|
175
|
+
`spur workflow run`, rather than restating the steps. Two copies of the procedure is the drift
|
|
176
|
+
the promotion was supposed to end.
|
|
177
|
+
|
|
178
|
+
### demote — workflow → descriptive procedure
|
|
179
|
+
|
|
180
|
+
Triggers (any two are sufficient; the first alone is sufficient):
|
|
181
|
+
|
|
182
|
+
- Every node is an `agent.run` with a raw prompt and every edge is unconditional.
|
|
183
|
+
- The definition has been edited more often than it has been run.
|
|
184
|
+
- The graph is a straight line — no guard, no gate, no loop.
|
|
185
|
+
- The run count over its lifetime is in the single digits and not growing.
|
|
186
|
+
- Its `iterationBound` has never been reached because nothing ever loops.
|
|
187
|
+
|
|
188
|
+
Steps:
|
|
189
|
+
|
|
190
|
+
1. **Check for live dependents** — `spur workflow trace --workflow <name> --last 20 --json` for
|
|
191
|
+
recent runs, and grep the shipped surfaces for the filename. A definition another command invokes
|
|
192
|
+
is not demoted unilaterally.
|
|
193
|
+
2. **Return each node's work to its owner** — judgment nodes to the command or skill they should
|
|
194
|
+
have been calling; a genuinely useful shell sequence to one script under its owning surface.
|
|
195
|
+
3. **Rewrite the entry surface as the procedure**, in the order the graph ran.
|
|
196
|
+
4. **Delete the YAML and its `config/workflow-composition-baseline.json` entries** in the same
|
|
197
|
+
change. A baseline entry whose action no longer exists fails the two-sided check.
|
|
198
|
+
5. **Record the demotion** and its trigger, so the next author does not re-promote it by reflex.
|
|
199
|
+
|
|
200
|
+
### optimize — refine an accepted workflow in place
|
|
201
|
+
|
|
202
|
+
A [refine](operations.md#refine) whose `--intent` is latency or legibility rather than correctness.
|
|
203
|
+
Dimensions, highest leverage first:
|
|
204
|
+
|
|
205
|
+
1. **Remove or merge `agent.run` nodes** — the dominant cost, always the first pass.
|
|
206
|
+
2. **Collapse probe-then-branch into a soft status-file probe** with ordered guards.
|
|
207
|
+
3. **Reorder guards** cheapest-discriminating-first.
|
|
208
|
+
4. **Lower `iterationBound`** to the observed maximum plus one.
|
|
209
|
+
5. **Merge nodes that always run together** with no guard between them.
|
|
210
|
+
6. **Move any `>= 6`-unit shell program to a recorded owner** (never by reformatting).
|
|
211
|
+
7. **Rename states/nodes to outcomes** and add the missing `failureStates`.
|
|
212
|
+
|
|
213
|
+
**Measure, do not estimate.** Capture `spur workflow trace <run-id> --json` before and after and
|
|
214
|
+
compare wall-clock per state and the transition sequence. An optimization with no trace pair behind
|
|
215
|
+
it is a preference. Rules that still bind: the smallest change that meets the intent, no mode
|
|
216
|
+
switch inside a refine (that is a rewrite — hand it to `add`), and never edit a workflow that is
|
|
217
|
+
currently executing.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Checklist
|
|
222
|
+
|
|
223
|
+
- [ ] Fit gate run **before** the mode gate; the prose-vs-workflow verdict stated with its reason.
|
|
224
|
+
- [ ] All three of replay / branch / record hold, or the answer was a descriptive procedure.
|
|
225
|
+
- [ ] Judgment steps reference an existing command; none inline a raw prompt.
|
|
226
|
+
- [ ] `agent.run` node count is the minimum the branching requires.
|
|
227
|
+
- [ ] `iterationBound` chosen from a latency budget; guards ordered cheapest-first.
|
|
228
|
+
- [ ] States/nodes named for outcomes; `failureStates` declared; gates write a verdict file.
|
|
229
|
+
- [ ] Every `shell` action within the <= 5-unit budget, or carrying a recorded owner/disposition.
|
|
230
|
+
- [ ] Refactors verified through validate-and-dry-run; optimizations backed by a before/after trace.
|
|
@@ -9,12 +9,24 @@ see_also:
|
|
|
9
9
|
|
|
10
10
|
`spur workflow` runs declarative YAML workflows (powered by `@gobing-ai/ts-dual-workflow-engine`) that
|
|
11
11
|
orchestrate a multi-step process — an implement→check→fix loop, an import→validate→transform→write
|
|
12
|
-
pipeline, an approval gate. The engine executes **two distinct workflow kinds**, and
|
|
13
|
-
|
|
12
|
+
pipeline, an approval gate. The engine executes **two distinct workflow kinds**, and choosing between
|
|
13
|
+
them is the first act of *authoring*. Get the mode right and the rest of authoring follows the
|
|
14
14
|
schema; get it wrong and you fight the engine.
|
|
15
15
|
|
|
16
|
-
Operating a workflow well is a full lifecycle —
|
|
17
|
-
read the trace, and refine. This skill covers all of it.
|
|
16
|
+
Operating a workflow well is a full lifecycle — decide it should be a workflow at all, choose the mode,
|
|
17
|
+
author the YAML, validate it, run it, read the trace, tune it, and refine. This skill covers all of it.
|
|
18
|
+
|
|
19
|
+
## Decide it is a workflow at all — before the mode
|
|
20
|
+
|
|
21
|
+
The mode gate presumes the process belongs in YAML. That presumption is the more expensive one to get
|
|
22
|
+
wrong: a workflow whose nodes are all raw `agent.run` prompts is a descriptive procedure paying a
|
|
23
|
+
process spawn per step, and a bounded retry loop written as a paragraph in a skill reference is a
|
|
24
|
+
workflow nobody authored. Run the **fit gate** first — a process earns a workflow only when it
|
|
25
|
+
replays, branches on a machine-checkable predicate, **and** needs a durable per-run record.
|
|
26
|
+
|
|
27
|
+
Fit gate, cost model, latency/observability tuning, the node simplicity budget, and the
|
|
28
|
+
promote / demote / optimize refactor procedures:
|
|
29
|
+
[workflows/workflow-fit-and-tuning.md](workflows/workflow-fit-and-tuning.md).
|
|
18
30
|
|
|
19
31
|
## Choose the execution mode first
|
|
20
32
|
|
|
@@ -62,6 +74,9 @@ before authoring. Full procedure: [workflows/authoring-workflows.md](workflows/a
|
|
|
62
74
|
|
|
63
75
|
Use this skill to:
|
|
64
76
|
|
|
77
|
+
- **Decide fit** — judge whether a process should be a workflow at all or stay a descriptive
|
|
78
|
+
procedure / checklist, and tune an accepted one for latency and legible traces.
|
|
79
|
+
→ workflows/workflow-fit-and-tuning.md
|
|
65
80
|
- **Author a workflow** — turn a described process into a validated, dry-run-verified YAML definition
|
|
66
81
|
in the right mode. → authoring-workflows.md
|
|
67
82
|
- **Validate before trusting** — schema + semantic-check a workflow file (references, terminal
|
|
@@ -70,6 +85,9 @@ Use this skill to:
|
|
|
70
85
|
taken, terminal status).
|
|
71
86
|
- **Refine an existing workflow** — fix a stuck guard, add a state/node, retune `iterationBound`,
|
|
72
87
|
re-scope variables / `env.allow`, with the smallest change. → workflows/operations.md
|
|
88
|
+
- **Refactor across the prose/YAML boundary** — promote a descriptive procedure into a workflow,
|
|
89
|
+
demote a workflow back to prose, or optimize an accepted one for latency.
|
|
90
|
+
→ workflows/workflow-fit-and-tuning.md
|
|
73
91
|
- **Extend the engine** — register a custom action/guard runner or a trust-gated extension module when
|
|
74
92
|
the built-ins (`note`, `shell`, `always`, `action-ok`) fall short. → workflows/validation-and-extension.md
|
|
75
93
|
|
|
@@ -362,6 +380,10 @@ the workflow's actions (`shell`, custom runners) do that; this skill builds and
|
|
|
362
380
|
|
|
363
381
|
## Additional Resources
|
|
364
382
|
|
|
383
|
+
- [workflows/workflow-fit-and-tuning.md](workflows/workflow-fit-and-tuning.md) — the fit gate
|
|
384
|
+
(workflow vs. descriptive procedure), the per-node cost model, latency and observability tuning,
|
|
385
|
+
the node simplicity budget tied to the frozen ADR-069 measures, and the promote / demote /
|
|
386
|
+
optimize refactor procedures. Read before `add` and before any performance-motivated `refine`.
|
|
365
387
|
- [workflows/operations.md](workflows/operations.md) — the operation procedures (validate/run/list/add/refine),
|
|
366
388
|
the shared find-existing-workflow and validate-and-dry-run cores, and the mode-selection gate. The
|
|
367
389
|
entry point for slash-command delegation.
|
|
@@ -36,21 +36,20 @@ answer. Everything below is a consequence of that sentence, not an additional ru
|
|
|
36
36
|
**Default: execute the backing skill directly in the current coding-agent session.** Do not invoke
|
|
37
37
|
`spur agent run` when no escalation trigger applies and the operator did not select subprocess via
|
|
38
38
|
the `--agent` selector. Omitting `--agent` keeps the default — the backing skill runs in the
|
|
39
|
-
current session, and eligible model stages may dispatch once to a native subagent (task 0508).
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
current session, and eligible model stages may dispatch once to a native subagent (task 0508).
|
|
40
|
+
Explicit `inline` resolves identically (0687 R1: inline is the default selector; R2 generalized
|
|
41
|
+
native-subagent eligibility from omit-only to all inline resolutions).
|
|
42
42
|
|
|
43
|
-
>
|
|
44
|
-
> the
|
|
45
|
-
>
|
|
46
|
-
>
|
|
47
|
-
>
|
|
48
|
-
> fallback.**
|
|
43
|
+
> **`--agent inline` (explicit or omitted) is an honest selector, not a dispatch guarantee. On a
|
|
44
|
+
> host session the work runs in-session and eligible model stages may use a native subagent (0508,
|
|
45
|
+
> as generalized by 0687 R2). A headless dispatch surface (`spur agent run`, workflow `agent.run`,
|
|
46
|
+
> serve-side dispatch) cannot host a session; there AgentService substitutes tier resolution and
|
|
47
|
+
> warns once naming the resolved executor (0687 R3) — no rejection, no `exit 2`.**
|
|
49
48
|
|
|
50
49
|
| Value | Who does the work | Derived surface |
|
|
51
50
|
| --- | --- | --- |
|
|
52
51
|
| `(omitted)` | The agent running this session | Host session — host-controlled; eligible model stages may use a native subagent (0508) |
|
|
53
|
-
| `inline` | The agent running this session |
|
|
52
|
+
| `inline` | The agent running this session | Inline host session — identical to omitted (0687 R1); on headless surfaces tier resolution substitutes with a warning (0687 R3) |
|
|
54
53
|
| `auto` | The role the caller declared — this command's `role:` frontmatter or the workflow step's `role:` (Layer 1, `plugins/sp/references/roles.md`); with nothing declared, `agent.default`'s role (0542) | Subprocess — a tier-resolved executor pins a specific agent/model, which the host session cannot supply |
|
|
55
54
|
| `<name>` (coding agent or configured executor) | That executor | Inline when it resolves to the current session's agent; subprocess otherwise |
|
|
56
55
|
|
|
@@ -58,15 +57,9 @@ The previous `--inline` and `--subprocess` flags (feature H82, task 0413) are co
|
|
|
58
57
|
single selector: `--inline` → `--agent inline`, `--subprocess` → `--agent auto`. The old two-flag
|
|
59
58
|
form is no longer part of the command surface.
|
|
60
59
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
would therefore be a false implementation. On a headless surface (`spur agent run` / workflow
|
|
65
|
-
`agent.run`) explicit `inline` is **rejected** (ADR-047 G5 amendment) with the stable special error —
|
|
66
|
-
headless surfaces cannot host a session: `--agent inline requires a host session: this surface is
|
|
67
|
-
headless and never dispatches inline runs (no fallback to agent.default). Use 'auto', a role, or an
|
|
68
|
-
executor name.` (exit 2 at the CLI; the exported `AGENT_INLINE_HEADLESS_MESSAGE` in
|
|
69
|
-
`agent-service`). No further action is taken — no dispatch, no `agent.default` fallback.
|
|
60
|
+
The explicit-rejection carve-out above was superseded by ADR-087 (task 0687): a headless surface
|
|
61
|
+
substitutes tier resolution with a warning instead of rejecting — see the substitution blockquote
|
|
62
|
+
above and `resolveAgent` in `packages/app/src/services/agent-service.ts`.
|
|
70
63
|
|
|
71
64
|
### Objective triggers override the answer
|
|
72
65
|
|
|
@@ -81,9 +74,8 @@ cannot satisfy, so it wins regardless:
|
|
|
81
74
|
| **Workspace or credential isolation required** | The work must not share the host workspace or credentials. | `trigger 4: workspace or credential isolation required` |
|
|
82
75
|
|
|
83
76
|
A trigger selects subprocess when the selector is omitted, `auto`, or a name, and the applied trigger
|
|
84
|
-
must be named in the dispatch or result.
|
|
85
|
-
|
|
86
|
-
than dispatching a subprocess. When the operator selected a non-current executor and no objective trigger
|
|
77
|
+
must be named in the dispatch or result. Inline selections (omitted or explicit — they are now the
|
|
78
|
+
same value, 0687 R1/R2) satisfy trigger requirements in-session where possible. When the operator selected a non-current executor and no objective trigger
|
|
87
79
|
applies, report `operator override` rather than inventing one of the four. The trigger vocabulary and
|
|
88
80
|
evidence standard are owned by
|
|
89
81
|
[dispatch-surface.md](../../parallel-execution/references/dispatch-surface.md). If none can be named
|
|
@@ -125,15 +117,15 @@ model stage (task 0508): an eligible `agent.run` stage — pure-slash input, non
|
|
|
125
117
|
native subagent with shared-worktree read/write/shell capability — dispatches **once** to that
|
|
126
118
|
native subagent and joins before the driver continues; any pre-dispatch eligibility failure falls
|
|
127
119
|
back to one host execution, and a failure after dispatch follows the stage's error policy with no
|
|
128
|
-
automatic host replay.
|
|
129
|
-
|
|
120
|
+
automatic host replay. Inline resolution (omitted or explicit, 0687 R1/R2) keeps the native-subagent
|
|
121
|
+
leg for eligible stages. Operator
|
|
130
122
|
confirmation actions, `pause: true`, and approve/taste/ask decisions stay host-owned. Each inline
|
|
131
123
|
model stage appends `stage <id> executed inline in session <session-id>` to its run log; a
|
|
132
124
|
subagent-dispatched stage appends `stage <id> executed via subagent <agent-id> (host session
|
|
133
125
|
<session-id>)` instead. `dev-plan` remains a workflow subprocess, as do `dev-run`/`dev-runall` with
|
|
134
126
|
`--agent auto` or a name, parallel batches, and every headless `spur workflow run` / `spur agent
|
|
135
127
|
run`. `dev-run --mode implement` continues to run its single competency in-session under omitted
|
|
136
|
-
`--agent` or explicit `--agent inline` (
|
|
128
|
+
`--agent` or explicit `--agent inline` (identical values, 0687 R1).
|
|
137
129
|
|
|
138
130
|
### Executor precedence chain (R7)
|
|
139
131
|
|
|
@@ -150,11 +142,11 @@ resolved in this order; first match wins:
|
|
|
150
142
|
|
|
151
143
|
`--agent auto` tier-resolves an executor (stage `model_policy` → `agent.default` → tier priority)
|
|
152
144
|
**before** merging, so it enters the chain at step 1 already resolved to a concrete name.
|
|
153
|
-
On a headless workflow surface, explicit `--agent inline`
|
|
154
|
-
—
|
|
155
|
-
|
|
156
|
-
driver. Omitting the flag on a headless surface forwards nothing, so the
|
|
157
|
-
`agent.default` (step 2) or the YAML literal (step 3).
|
|
145
|
+
On a headless workflow surface, explicit `--agent inline` substitutes tier resolution with a warning
|
|
146
|
+
(0687 R3) instead of rejecting — it resolves exactly like an omitted flag. Interactive task wrappers
|
|
147
|
+
consume both inline resolutions identically (0508 eligibility as generalized by 0687 R2) before this
|
|
148
|
+
chain and use the host driver. Omitting the flag on a headless surface forwards nothing, so the
|
|
149
|
+
spawned step resolves to `agent.default` (step 2) or the YAML literal (step 3).
|
|
158
150
|
|
|
159
151
|
### Implement-only executor override (R6)
|
|
160
152
|
|
|
@@ -74,7 +74,7 @@ each would be scope creep for one-liner procedures.
|
|
|
74
74
|
| 6 | plan | `dev-plan` | `Skill()` | `sp:spur-dev` (`plan`) | `"<description>" [--feature <id>] [--parent <feature-id>] [--agent <inline\|auto\|name>] [--skip-design] [--auto] [--approve-taste]` |
|
|
75
75
|
| 7 | docs | _(no thin wrapper)_ | `Skill()` | `sp:doc-evolve` | `"<change description>"` |
|
|
76
76
|
| 8 | changelog | `dev-changelog` | `inline` | git log + conventional-commit grouping | `[--since <ref>] [--until <ref>] [--version <ver>]` |
|
|
77
|
-
| 9 | gitmsg | `dev-gitmsg` | `inline` |
|
|
77
|
+
| 9 | gitmsg | `dev-gitmsg` | `inline` | bounded diff capture → concern grouping → conventional commit | `[--commit] [--squash] [--all] [--scope <path>]` |
|
|
78
78
|
| 10 | fixall | `dev-fixall` | `inline` | lint + test fix loop | `[<validation-command>] [--max-retry <n>] [--scope <path>] [--gate-log <path>] [--findings <anchors>]` |
|
|
79
79
|
| 11 | handover | `dev-handover` | `inline` | structured doc generation | `"<blocker description>"` |
|
|
80
80
|
| 12 | brainstorm | `dev-brainstorm` | `Skill()` | `sp:brainstorm` (`dev-brainstorm`) | `<topic> [--depth <basic\|detailed\|comprehensive>] [--options <n>] [--agent <inline\|auto\|name>] [--skip-discovery] [--wayfind] [--task [<feature-id>]] [--feature [<parent-id>]] [--next]` |
|
|
@@ -140,12 +140,12 @@ must not be changed without updating the backing skill.
|
|
|
140
140
|
### 4. run
|
|
141
141
|
|
|
142
142
|
- **Purpose:** Run a task through the execution pipeline (full) or execute a single pipeline step (implement).
|
|
143
|
-
- **Inputs:** `<wbs>` (required). `--mode <full|implement>` selects the execution mode. `implement` invokes `sp:code-implementation` inline by default. Interactive `full`
|
|
143
|
+
- **Inputs:** `<wbs>` (required). `--mode <full|implement>` selects the execution mode. `implement` invokes `sp:code-implementation` inline by default. Interactive `full` resolves the selector uniformly (task 0687): omit ≡ explicit `--agent inline`; full mode reads `task-pipeline.yaml` and drives its actions/guards in the host session — host-controlled and non-subprocess, with 0508 eligibility applying to the resolved-inline selector (eligible `agent.run` stages dispatch once to a native subagent, host fallback)). `--agent auto`, a name, or headless invocation launches the workflow subprocess. `--agent <inline|auto|name>` selects the execution surface (see [SSOT](cross-cutting.md#inline-default-execution-surface)). `--auto` skips the HITL approve gate / confirmations and propagates down the `--next` chain. `--next` controls chaining only and never changes the mode; a pipeline implement stage must invoke `/sp:dev-run <wbs> --mode implement`. On implement success with `--next`, transition `todo → wip → testing` through the FSM (guards honored — no `--no-lifecycle`) + chain to `/sp:dev-verify <wbs> --auto --next`. On a guard failure, stop as review-pending. **Partial-deliverable rule:** if the task ships only part of its requirements (e.g. an R1/R2 split with the rest in a follow-up task), the `## Solution` section must state that explicitly and the verify verdict will record the scope. `--worktree [<name>]` runs the full pipeline inside an isolated git worktree (create or reuse; FF-merge on success, retain on failure) — the batch lifecycle in [execution-batch.md § Worktree isolation](execution-batch.md#worktree-isolation---worktree-name) applied to a batch of one; rejected with `--mode implement`. `--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
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
|
-
- **Delegation:** `Skill(skill="sp:spur-dev", args="run-inline $ARGUMENTS")` for interactive full omit/inline (
|
|
148
|
+
- **Delegation:** `Skill(skill="sp:spur-dev", args="run-inline $ARGUMENTS")` for interactive full omit/inline (resolved-inline semantics, task 0687); `Skill(skill="sp:spur-dev", args="run $ARGUMENTS")` for explicit/headless full mode; `Skill(skill="sp:code-implementation", args="$ARGUMENTS")` for implement mode.
|
|
149
149
|
|
|
150
150
|
### 5. refine
|
|
151
151
|
|
|
@@ -252,7 +252,7 @@ must not be changed without updating the backing skill.
|
|
|
252
252
|
### 6. plan
|
|
253
253
|
|
|
254
254
|
- **Purpose:** Plan a feature from a description — intake → feature create → AC generation → feature check gate → decomposition → batch-create (with **Design by default**).
|
|
255
|
-
- **Inputs:** `"<description>"` (required). `--feature <id>` links to an existing feature. `--parent <feature-id>` nests under a parent. The planning pipeline's `agent.run` stages always dispatch a subprocess; `--agent <inline|auto|name>` selector accepted —
|
|
255
|
+
- **Inputs:** `"<description>"` (required). `--feature <id>` links to an existing feature. `--parent <feature-id>` nests under a parent. The planning pipeline's `agent.run` stages always dispatch a subprocess; `--agent <inline|auto|name>` selector accepted — `inline` substitutes tier resolution with a warning on this headless surface (task 0687) (see [SSOT](cross-cutting.md#inline-default-execution-surface)). **Design package flags (unified with `/sp:dev-idea`):**
|
|
256
256
|
- **Default:** author task `design` on every batch item + feature satellite when the seam heuristic fires (**ties lean design**). There is **no** `--design` force flag.
|
|
257
257
|
- `--skip-design` — skip feature satellite **and** omit task `design` fields (scaffold only; refine fills later). Sole design opt-out.
|
|
258
258
|
- `--approve-taste` — with `--auto`, pre-clear design-approval taste pause when that gate is used (`design_approved=true`). Alias: `--design-approved`.
|
|
@@ -300,7 +300,7 @@ must not be changed without updating the backing skill.
|
|
|
300
300
|
### 14. wrap
|
|
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
|
-
- **Inputs:** `<wbs>` (required, positional). `--agent <inline|auto|name>` names the wrap's model-bearing executor (default: `
|
|
303
|
+
- **Inputs:** `<wbs>` (required, positional). `--agent <inline|auto|name>` names the wrap's model-bearing executor (default: `inline`, resolving identically when omitted; task 0687); wrap is workflow-backed (headless), so omit ≡ explicit `inline` (task 0687): tier substitution under objective trigger 3 (durable auditable run record required) plus one warning naming the substituted executor. `auto` tier-resolves an executor, and a name pins that executor into `vars.agent` (see [SSOT](cross-cutting.md#inline-default-execution-surface)). `--auto` skips objective confirmations (the branch-cleanup HITL gate still pauses — irreversible). `--merge` triggers branch cleanup (irreversible HITL gate).
|
|
304
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:
|
|
@@ -316,9 +316,9 @@ must not be changed without updating the backing skill.
|
|
|
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
|
-
- **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: `
|
|
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: `inline`, resolving identically when omitted; task 0687); wrap is workflow-backed (headless), so omit ≡ explicit `inline` (task 0687): tier substitution under objective trigger 3 (durable auditable run record required) plus one warning naming the substituted executor. `auto` tier-resolves an executor, and a name pins that executor into `vars.agent` (see [SSOT](cross-cutting.md#inline-default-execution-surface)). `--auto` skips objective confirmations. `--merge` triggers branch cleanup (irreversible HITL gate).
|
|
320
320
|
- **Backing:** `spur workflow run wrapup-pipeline.yaml` — direct workflow invocation.
|
|
321
|
-
- **Behavior:** Resolves the task list via `spur task list --json` (filtered by `--feature`, `--since`, `--status`), resolves the executor (
|
|
321
|
+
- **Behavior:** Resolves the task list via `spur task list --json` (filtered by `--feature`, `--since`, `--status`), resolves the executor (omission and explicit `--agent inline` tier-substitute identically with a warning per task 0687, `auto` tier-resolves, a name pins), emits a pre-dispatch notice naming the subprocess override — `execution surface: subprocess`, `reason: trigger 3 — durable auditable run record required`, `requested agent: <selector>`, `executor: <resolved>` — then builds `--vars '{"tasks":"[...]","feature":"<id>","agent":"<resolved>","profile":"interactive|auto","merge":"true|false"}'` and invokes the wrapup pipeline. The pipeline runs the same states as `wrap` 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
|
|
@@ -378,14 +378,43 @@ is the procedure. The backing is a combination of git CLI, `spur` CLI, and agent
|
|
|
378
378
|
|
|
379
379
|
### 9. gitmsg
|
|
380
380
|
|
|
381
|
-
- **Purpose:** Generate conventional commit message(s)
|
|
382
|
-
- **Inputs:** `--scope <path>` (default:
|
|
383
|
-
- **Backing:** `inline` —
|
|
381
|
+
- **Purpose:** Generate conventional commit message(s) for the current change set — one bounded diff capture → concern grouping → one message per concern; optionally commit.
|
|
382
|
+
- **Inputs:** `--scope <path>` (default: the whole change set) — path filter; an explicit `--scope` always wins over change-set auto-discovery. `--all` (default: off) — widen the change set past the index to every change in the tree: unstaged **and untracked** files. Omitted, the change set is the index only, which keeps pre-commit semantics stable. `--commit` (default: off) — commit the change set, one commit per concern. `--squash` (default: off) — collapse every concern into one message and one commit; implies `--commit`.
|
|
383
|
+
- **Backing:** `inline` — bounded diff capture + concern grouping + conventional commit formatting.
|
|
384
384
|
- **Behavior:**
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
385
|
+
|
|
386
|
+
1. **Gather once — a single shell round trip.** No temp file, no second read, no follow-up `git` call for context:
|
|
387
|
+
|
|
388
|
+
```bash
|
|
389
|
+
# default: RANGE=--cached (the index)
|
|
390
|
+
# --all: RANGE=HEAD (plus untracked, listed below)
|
|
391
|
+
git rev-parse --abbrev-ref HEAD --git-dir --git-common-dir
|
|
392
|
+
git diff $RANGE --stat $PATHSPEC
|
|
393
|
+
git diff $RANGE --name-status $PATHSPEC
|
|
394
|
+
git diff $RANGE -U0 $PATHSPEC ':(exclude)*.lock' ':(exclude)*lock.json' ':(exclude)*.lockb' | head -c 60000
|
|
395
|
+
# --all only:
|
|
396
|
+
git ls-files --others --exclude-standard $PATHSPEC
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
`$PATHSPEC` is `-- <path>` when `--scope` is given, empty otherwise.
|
|
400
|
+
|
|
401
|
+
**Empty index, no `--all`** — the dead-end this command used to hand back. Reading is
|
|
402
|
+
not a mutation, so the two cases split there:
|
|
403
|
+
- **No committing flag** → re-gather once with `--all` semantics and say so
|
|
404
|
+
(`index empty — read the whole tree instead`). A message-only run has nothing to lose by
|
|
405
|
+
looking wider, and the operator gets an answer instead of an errand.
|
|
406
|
+
- **`--commit` / `--squash`** → stop. Print the worktree counts (`N tracked, M untracked`) and
|
|
407
|
+
the exact re-run line (`/sp:dev-gitmsg <the same flags> --all`). Committing
|
|
408
|
+
untracked files is never inferred from an empty index — that is the operator's call, and it
|
|
409
|
+
is now one paste away rather than a guess.
|
|
410
|
+
|
|
411
|
+
Empty under `--all`, or still empty after the widen → report `no changes in <scope>` and stop.
|
|
412
|
+
|
|
413
|
+
2. **Stay inside the budget.** `-U0` (no context lines) plus the lockfile exclusions plus the 60 KB cap is the token contract — never re-run the diff with context to "see more", never paste diff hunks into the output. If the cap truncated the diff, say so and derive the message from `--stat` + `--name-status` alone; a large mechanical change rarely needs hunk detail to be typed and scoped correctly.
|
|
414
|
+
|
|
415
|
+
3. **Summarize only what shapes the message.** One sentence — what changed and _why_, not a line count — for each file whose change is not obvious from its path and status. Skip the obvious ones (generated files, lockfiles, pure renames, `docs/**` under a `docs` group). Past ~12 interesting files, summarize per directory instead of per file.
|
|
416
|
+
|
|
417
|
+
4. **Group by concern**, and for each group derive type, scope, message:
|
|
389
418
|
- Type from the dominant change — `feat` (new functionality) · `fix` (bug fix) · `refactor` (restructuring, no behavior change) · `docs` (documentation only) · `chore` (build/config/tooling) · `perf` · `test` · `style`.
|
|
390
419
|
- Scope from the affected module/package (`cli`, `domain`, `server`, `web`, `app`, …); `--scope` overrides.
|
|
391
420
|
- Message:
|
|
@@ -393,16 +422,29 @@ is the procedure. The backing is a combination of git CLI, `spur` CLI, and agent
|
|
|
393
422
|
```
|
|
394
423
|
<type>(<scope>): <summary>
|
|
395
424
|
|
|
396
|
-
<body —
|
|
425
|
+
<body — why, only when the why is not obvious>
|
|
397
426
|
```
|
|
398
427
|
|
|
399
|
-
Summary: imperative mood, ≤72 chars, lowercase first word, no period. Body
|
|
428
|
+
Summary: imperative mood, ≤72 chars, lowercase first word, no period. Body explains intent and consequence; it never restates the diff. Behavior changes and pure formatting/refactor churn are **different concerns** — never one group.
|
|
429
|
+
|
|
430
|
+
5. **Resolve the message shape without a round trip to the operator.** `--squash` → one combined
|
|
431
|
+
message (dominant type/scope, one body bullet per group). Otherwise one message per group, in
|
|
432
|
+
dependency order (refactor before the feature that uses it); a single group is that same rule
|
|
433
|
+
with one group.
|
|
434
|
+
|
|
435
|
+
6. **Report, then commit if asked.** Print the resolved message(s), a copy-paste `git commit` line per message, and a one-line context header: current branch, and `linked worktree` when `--git-dir` differs from `--git-common-dir` and `git rev-parse --show-superproject-working-tree` is empty (a non-empty result means submodule, not worktree). With neither `--commit` nor `--squash`, stop here — the operator commits.
|
|
436
|
+
|
|
437
|
+
7. **Committing (`--commit` / `--squash`).** The two flags name outcomes, not dimensions: `--commit`
|
|
438
|
+
commits **by concern**, `--squash` commits **everything as one**. `--squash` implies `--commit`,
|
|
439
|
+
so `--commit --squash` is just `--squash`, never an error.
|
|
400
440
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
441
|
+
First scan the captured diff's added lines for credentials (`password`, `secret`, `api[_-]?key`, `token`, `BEGIN [A-Z ]*PRIVATE KEY`, long base64-looking literals). On a hit: print the offending `file:line`, commit nothing, stop. Otherwise:
|
|
442
|
+
- `--all` → `git add -A $PATHSPEC` first (this is what stages the untracked files).
|
|
443
|
+
- `--squash`, or `--commit` on a single group → `git commit -m "$MESSAGE"`.
|
|
444
|
+
- `--commit` on multiple groups → **commit each group in sequence**: record the full staged file list once, then per group `git reset -q -- <all staged paths>` → `git add -- <that group's paths>` → `git commit -m "<that group's message>"`. This is the split; it needs no re-run and no re-staging by the operator.
|
|
445
|
+
- **Partial-staging guard:** if any path appears in _both_ `git diff --cached --name-only` and `git diff --name-only` (a `git add -p` staging), the sequence above would silently widen those commits to the whole file. Do not split — report the affected paths and commit nothing unless `--squash` was given.
|
|
404
446
|
|
|
405
|
-
- **Invariants:**
|
|
447
|
+
- **Invariants:** With neither `--commit` nor `--squash`, never runs `git commit` — message only. Neither flag commits across a credential hit, and `--commit` never splits across a partial staging. Without `--all` the change set is the index, so plain `/sp:dev-gitmsg` keeps pre-commit semantics; only `--all` reaches unstaged and untracked files, and only a committing run (`--commit` / `--squash`) stages them. The empty-index widen is read-only and always announced — a committing run never widens its own scope. An explicit `--scope <path>` always bounds the change set, with or without `--all`. One diff capture per run, bounded and context-free — never a second, wider read.
|
|
406
448
|
|
|
407
449
|
### 10. fixall
|
|
408
450
|
|
|
@@ -147,7 +147,7 @@ On the subprocess path, when `--agent <value>` is set (passed through from the t
|
|
|
147
147
|
`--vars '{"wbs":"<wbs>","agent":"<value>","implementAgent":"<value>"}'`. The pipeline YAML reads
|
|
148
148
|
`${vars.agent}` for review/verify/test-fix and `${vars.implementAgent}` for implement — setting
|
|
149
149
|
both keys ensures the pinned executor reaches every hop. The full value-semantics contract (one
|
|
150
|
-
rule, value table, objective triggers, and headless `inline`
|
|
150
|
+
rule, value table, objective triggers, and headless `inline` substitution (tier resolution + one warning) per ADR-087)
|
|
151
151
|
lives in [cross-cutting.md](cross-cutting.md#inline-default-execution-surface) — the SSOT.
|
|
152
152
|
This file documents only the **workflow-pipeline mechanics**: how the selector reaches `agent.run`
|
|
153
153
|
steps. Precedence chain: `--agent` / explicit `--vars` → `agent.default` → YAML literal (see SSOT
|