@gobing-ai/spur 0.2.11 → 0.3.0
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/package.json +1 -1
- package/spur-cli/config/workflows/feature-dev.yaml +5 -0
- package/spur-cli/config/workflows/idea-pipeline.yaml +426 -0
- package/spur-cli/config/workflows/planning-pipeline.yaml +50 -18
- package/spur-cli/config/workflows/task-pipeline.yaml +69 -5
- package/spur-cli/config/workflows/wrapup-pipeline.yaml +240 -0
- package/spur.js +515 -78
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
# (0055) apply identically. Run linkage is written to `task_run_links` (kind=pipeline).
|
|
8
8
|
#
|
|
9
9
|
# Shape: precheck → implement → test → review → approve(HITL) → verify → record → done
|
|
10
|
-
# (precheck failure short-circuits to `failed`
|
|
10
|
+
# (precheck failure short-circuits to `failed`; approve routes to `failed` on
|
|
11
|
+
# operator rejection or `cancelled` on operator cancel — R1, bug-750).
|
|
11
12
|
#
|
|
12
13
|
# Vars (passed as a JSON object via `--vars`):
|
|
13
14
|
# wbs — the task WBS (required), e.g. `--vars '{"wbs":"0042"}'`
|
|
@@ -24,6 +25,7 @@ initialState: precheck
|
|
|
24
25
|
terminalStates:
|
|
25
26
|
- done
|
|
26
27
|
- failed
|
|
28
|
+
- cancelled
|
|
27
29
|
vars:
|
|
28
30
|
wbs: "0000"
|
|
29
31
|
profile: "standard"
|
|
@@ -39,6 +41,15 @@ vars:
|
|
|
39
41
|
# subprocess is killed (not abandoned) and the step fails → pipeline routes to
|
|
40
42
|
# `failed`. Override per run with `--vars '{"stepTimeoutMs":120000}'`.
|
|
41
43
|
stepTimeoutMs: "600000"
|
|
44
|
+
# Timeout for the `implement` step specifically (R2a). Implementation is the
|
|
45
|
+
# heaviest agent.run step (full read/write/test-probe loop) and has timed out
|
|
46
|
+
# at the 600s default in five consecutive dogfood runs (bugs 742/744/746/748),
|
|
47
|
+
# a 100% failure rate at that budget. 30 min gives real headroom; STOP and
|
|
48
|
+
# record honestly rather than raising it further without operator sign-off.
|
|
49
|
+
implementTimeoutMs: "1800000"
|
|
50
|
+
# Answer captured by the approve gate's hitl.confirm (R1): "yes" | "no" | "cancel".
|
|
51
|
+
# Empty by default; only meaningful once the approve state has been entered.
|
|
52
|
+
__hitlAnswer: ""
|
|
42
53
|
|
|
43
54
|
states:
|
|
44
55
|
- id: precheck
|
|
@@ -46,6 +57,9 @@ states:
|
|
|
46
57
|
Pre-flight structural validation. The precheck→implement guard runs
|
|
47
58
|
`spur task check <wbs>`; a failure routes to `failed` (blocked report).
|
|
48
59
|
onEnter:
|
|
60
|
+
- kind: shell
|
|
61
|
+
options:
|
|
62
|
+
command: "${vars.spurBin} agent doctor ${vars.agent}"
|
|
49
63
|
- kind: note
|
|
50
64
|
options:
|
|
51
65
|
# R4 task_run_links linkage (kind=pipeline) is written by a WorkflowService
|
|
@@ -70,11 +84,24 @@ states:
|
|
|
70
84
|
- kind: agent.run
|
|
71
85
|
options:
|
|
72
86
|
agent: ${vars.agent}
|
|
73
|
-
input:
|
|
74
|
-
|
|
87
|
+
input: >-
|
|
88
|
+
/sp:dev-run --mode implement ${vars.wbs} --auto.
|
|
89
|
+
Work only in this working tree on task ${vars.wbs}; NEVER invoke
|
|
90
|
+
`spur workflow run` or `/sp:dev-run` without `--mode implement` —
|
|
91
|
+
this step IS the pipeline (bug-742).
|
|
92
|
+
timeoutMs: ${vars.implementTimeoutMs}
|
|
75
93
|
- kind: shell
|
|
76
94
|
options:
|
|
77
95
|
command: "${vars.spurBin} task update ${vars.wbs} wip --no-lifecycle"
|
|
96
|
+
# Post-implement cleanup: auto-format any unformatted output the agent produced.
|
|
97
|
+
# The agent may leave files that don't pass `biome check`, which then fails
|
|
98
|
+
# `bun run lint` downstream. Running format here is cheap (< 1 s) and prevents
|
|
99
|
+
# the test stage from tripping on a purely mechanical formatting defect (dogfood
|
|
100
|
+
# bug-733 — omp left agent-run.test.ts unformatted, causing a spurious lint gate
|
|
101
|
+
# failure that the ## Testing section mis-attributed to "pre-existing gaps").
|
|
102
|
+
- kind: shell
|
|
103
|
+
options:
|
|
104
|
+
command: "bun run format"
|
|
78
105
|
|
|
79
106
|
- id: test
|
|
80
107
|
description: Test execution + coverage via /sp:dev-unit.
|
|
@@ -84,6 +111,13 @@ states:
|
|
|
84
111
|
agent: ${vars.agent}
|
|
85
112
|
input: /sp:dev-unit ${vars.wbs} --auto
|
|
86
113
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
114
|
+
# Post-test gate: run the project's full lint gate (biome check + tsc). If the
|
|
115
|
+
# implement or test step left unformatted files or type errors, the run routes to
|
|
116
|
+
# `failed` rather than letting an agent-authored `## Testing` claim "all pass"
|
|
117
|
+
# against a red gate (dogfood bug-733).
|
|
118
|
+
- kind: shell
|
|
119
|
+
options:
|
|
120
|
+
command: "bun run lint"
|
|
87
121
|
|
|
88
122
|
- id: review
|
|
89
123
|
description: SECUA-framework code review via /sp:dev-review (Security, Efficiency, Correctness, Usability, Architecture).
|
|
@@ -99,6 +133,8 @@ states:
|
|
|
99
133
|
Human-in-the-loop approval gate. Under `profile=auto` this state is never entered
|
|
100
134
|
(review routes around it straight to verify). In interactive mode this state pauses
|
|
101
135
|
the run for `spur workflow continue` (E3), making approval an explicit operator action.
|
|
136
|
+
The operator's answer is routed (R1, bug-750): yes -> verify; no -> failed (rejection
|
|
137
|
+
recorded, not silently approved); cancel -> cancelled (a distinct terminal state).
|
|
102
138
|
pause: true
|
|
103
139
|
onEnter:
|
|
104
140
|
- kind: hitl.confirm
|
|
@@ -147,10 +183,17 @@ states:
|
|
|
147
183
|
- kind: note
|
|
148
184
|
options:
|
|
149
185
|
message: "Pipeline complete for task ${vars.wbs} (done gate cleared)."
|
|
186
|
+
# Checkpoint write: record session state for resume (Phase 4, task 0171 R3)
|
|
187
|
+
- kind: shell
|
|
188
|
+
options:
|
|
189
|
+
command: 'mkdir -p .spur/memory/sessions && echo "checkpoint: task-pipeline done wbs=${vars.wbs} ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)" > .spur/memory/sessions/${vars.wbs}-checkpoint.md'
|
|
150
190
|
|
|
151
191
|
- id: failed
|
|
152
192
|
description: Terminal — precheck (or a gated step) failed; reported, not advanced.
|
|
153
193
|
|
|
194
|
+
- id: cancelled
|
|
195
|
+
description: Terminal — pipeline cancelled by operator at the approval gate (R1).
|
|
196
|
+
|
|
154
197
|
transitions:
|
|
155
198
|
# ── precheck: shell guard runs `spur task check`; pass → implement, fail → failed ──
|
|
156
199
|
- from: precheck
|
|
@@ -197,12 +240,33 @@ transitions:
|
|
|
197
240
|
guard:
|
|
198
241
|
kind: always
|
|
199
242
|
|
|
200
|
-
# ── approve: HITL gate (reached only when profile != auto)
|
|
243
|
+
# ── approve: HITL gate (reached only when profile != auto). Routed by the
|
|
244
|
+
# operator's captured answer (R1, bug-750) — three ordered guards, declaration
|
|
245
|
+
# order matters (yes first, then no, then cancel), mirroring idea-pipeline's
|
|
246
|
+
# design-approval gate (config/workflows/idea-pipeline.yaml:352-366). No
|
|
247
|
+
# `always` edge remains out of `approve`. ──
|
|
201
248
|
- from: approve
|
|
202
249
|
to: verify
|
|
203
250
|
description: Approved — proceed to verification.
|
|
204
251
|
guard:
|
|
205
|
-
kind:
|
|
252
|
+
kind: shell
|
|
253
|
+
options:
|
|
254
|
+
command: 'test "${vars.__hitlAnswer}" = yes'
|
|
255
|
+
- from: approve
|
|
256
|
+
to: failed
|
|
257
|
+
description: Operator rejected at the approval gate — report and stop.
|
|
258
|
+
guard:
|
|
259
|
+
kind: shell
|
|
260
|
+
options:
|
|
261
|
+
command: 'test "${vars.__hitlAnswer}" = no'
|
|
262
|
+
- from: approve
|
|
263
|
+
to: cancelled
|
|
264
|
+
description: Operator cancelled at the approval gate.
|
|
265
|
+
guard:
|
|
266
|
+
kind: shell
|
|
267
|
+
options:
|
|
268
|
+
command: 'test "${vars.__hitlAnswer}" = cancel'
|
|
269
|
+
|
|
206
270
|
# ── completion gate (the YAML-native replacement for rd3's default-on --postflight-verify) ──
|
|
207
271
|
# The verify step emits .spur/run/<wbs>-verdict.json. Only `verdict: PASS` clears
|
|
208
272
|
# the gate to `record`; any non-PASS (PARTIAL/FAIL), a missing file, or malformed
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# Wrap-up pipeline — post-execution wrap-up for one task or a batch (design §wrapup-pipeline).
|
|
2
|
+
#
|
|
3
|
+
# Orchestration is configuration (ADR-022 / §3.2): this is YAML over the existing
|
|
4
|
+
# dual-workflow engine — zero new engine code. The pipeline NEVER mutates task status —
|
|
5
|
+
# wrap-up consumes completed tasks and produces learning/metrics/doc artifacts only.
|
|
6
|
+
# Feature transitions go through `spur feature update` so the feature-lifecycle guards
|
|
7
|
+
# apply identically. Branch cleanup is an irreversible HITL gate — it always pauses,
|
|
8
|
+
# even under --auto (Iron Law #6: irreversible action -> surface to human).
|
|
9
|
+
#
|
|
10
|
+
# Shape: start -> task-resolve -> doc-sync -> learning-capture -> metrics-record
|
|
11
|
+
# -> feature-transition (conditional: if vars.feature set)
|
|
12
|
+
# -> branch-cleanup (conditional: if vars.merge=true)
|
|
13
|
+
# -> done
|
|
14
|
+
# (task-resolve with empty list short-circuits to `skipped`).
|
|
15
|
+
#
|
|
16
|
+
# Vars (passed as a JSON object via `--vars`):
|
|
17
|
+
# tasks — JSON array of WBS strings, passed as a JSON-encoded STRING value (the CLI
|
|
18
|
+
# rejects non-string --vars values), e.g. --vars '{"tasks":"[\"0167\"]"}'
|
|
19
|
+
# feature — feature id to advance through legal lifecycle edges (optional)
|
|
20
|
+
# profile — set --vars '{"profile":"auto"}' to skip objective confirmations
|
|
21
|
+
# merge — set --vars '{"merge":"true"}' to run branch cleanup (irreversible HITL)
|
|
22
|
+
# spurBin — PATH-independent spur invocation (overridden by CLI at run start)
|
|
23
|
+
# agent — agent for agent.run steps (default: omp)
|
|
24
|
+
#
|
|
25
|
+
# Seeded by `spur init`; adapt the agent.run inputs to your project's command set.
|
|
26
|
+
|
|
27
|
+
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
28
|
+
kind: state-machine
|
|
29
|
+
name: wrapup-pipeline
|
|
30
|
+
description: "Post-execution wrap-up: doc-sync, learning-capture, metrics, feature-transition, branch-cleanup"
|
|
31
|
+
iterationBound: 10
|
|
32
|
+
initialState: start
|
|
33
|
+
terminalStates:
|
|
34
|
+
- done
|
|
35
|
+
- skipped
|
|
36
|
+
vars:
|
|
37
|
+
tasks: "[]"
|
|
38
|
+
feature: ""
|
|
39
|
+
profile: "standard"
|
|
40
|
+
merge: "false"
|
|
41
|
+
spurBin: "spur"
|
|
42
|
+
agent: "omp"
|
|
43
|
+
stepTimeoutMs: "600000"
|
|
44
|
+
|
|
45
|
+
states:
|
|
46
|
+
- id: start
|
|
47
|
+
description: >
|
|
48
|
+
Pipeline start. Task statuses are NOT mutated by wrap-up — wrap-up consumes
|
|
49
|
+
completed tasks and produces learning/metrics/doc artifacts only.
|
|
50
|
+
onEnter:
|
|
51
|
+
- kind: note
|
|
52
|
+
options:
|
|
53
|
+
message: "Wrap-up pipeline start for tasks: ${vars.tasks}. Task statuses are NOT mutated."
|
|
54
|
+
|
|
55
|
+
- id: task-resolve
|
|
56
|
+
description: >
|
|
57
|
+
Validate the task list passed by the wrapper is non-empty. The command wrapper
|
|
58
|
+
(dev-wrap/dev-wrapall) resolves the task list and passes it as vars.tasks;
|
|
59
|
+
this state validates it before proceeding. Empty list routes to `skipped`.
|
|
60
|
+
onEnter:
|
|
61
|
+
- kind: note
|
|
62
|
+
options:
|
|
63
|
+
message: "Resolving task list: ${vars.tasks}"
|
|
64
|
+
|
|
65
|
+
- id: doc-sync
|
|
66
|
+
description: >
|
|
67
|
+
Dispatch sp:doc-evolve once for the entire batch. Project-level doc drift repair
|
|
68
|
+
runs once, not per-task. The agent reads the batch's completed tasks, identifies
|
|
69
|
+
doc drift (04_DESIGN, 03_ARCHITECTURE, 00_ADR, docs/design/*), and repairs it
|
|
70
|
+
following the constitution's edit rules. Does NOT write task or feature corpus.
|
|
71
|
+
onEnter:
|
|
72
|
+
- kind: agent.run
|
|
73
|
+
options:
|
|
74
|
+
agent: ${vars.agent}
|
|
75
|
+
input: "Run sp:doc-evolve for completed tasks ${vars.tasks}. Repair doc drift in 04_DESIGN, 03_ARCHITECTURE, 00_ADR, docs/design/* following the constitution edit rules. Do not write task or feature corpus files."
|
|
76
|
+
timeoutMs: ${vars.stepTimeoutMs}
|
|
77
|
+
|
|
78
|
+
- id: learning-capture
|
|
79
|
+
description: >
|
|
80
|
+
Append working learnings to .spur/memory/learnings.md. The agent extracts
|
|
81
|
+
insights from the batch and writes them to a capture file; a downstream shell
|
|
82
|
+
step appends that file to the learnings scratchpad. This decouples content
|
|
83
|
+
generation from file persistence: the shell step's exit code is the real
|
|
84
|
+
success signal, and the capture file is verified before the append.
|
|
85
|
+
onEnter:
|
|
86
|
+
- kind: agent.run
|
|
87
|
+
options:
|
|
88
|
+
agent: ${vars.agent}
|
|
89
|
+
input: "Extract working learnings from tasks ${vars.tasks}. Output as markdown grouped by date and task WBS. Include: conventions discovered, errors hit and resolved, patterns that worked, gotchas. Output raw markdown (no code fences) — your answer will be captured to a file and appended to .spur/memory/learnings.md."
|
|
90
|
+
answerFile: .spur/run/wrapup-learnings.md
|
|
91
|
+
expectFile: .spur/run/wrapup-learnings.md
|
|
92
|
+
timeoutMs: ${vars.stepTimeoutMs}
|
|
93
|
+
- kind: shell
|
|
94
|
+
options:
|
|
95
|
+
command: 'test -s .spur/run/wrapup-learnings.md && mkdir -p .spur/memory && cat .spur/run/wrapup-learnings.md >> .spur/memory/learnings.md && printf "\n" >> .spur/memory/learnings.md'
|
|
96
|
+
|
|
97
|
+
- id: metrics-record
|
|
98
|
+
description: >
|
|
99
|
+
Append one JSONL row per task to .spur/memory/wrapup-metrics.jsonl. The agent
|
|
100
|
+
produces the JSONL content to a capture file; a downstream shell step appends
|
|
101
|
+
it. Each row records: task WBS, feature id, status, verdict, timestamp.
|
|
102
|
+
Append-only and machine-readable. The capture+shell decoupling means a
|
|
103
|
+
non-empty capture file is verified before the append (the agent.run exit-0
|
|
104
|
+
is no longer the sole success signal — the shell step's exit code gates the
|
|
105
|
+
write).
|
|
106
|
+
onEnter:
|
|
107
|
+
- kind: agent.run
|
|
108
|
+
options:
|
|
109
|
+
agent: ${vars.agent}
|
|
110
|
+
input: "For each task in ${vars.tasks}, read the task file and output one JSONL line per task with fields: wbs, feature_id, status, verdict (from .spur/run/<wbs>-verdict.json if available), timestamp. Output raw JSONL (no code fences) — your answer will be captured to a file and appended to .spur/memory/wrapup-metrics.jsonl."
|
|
111
|
+
answerFile: .spur/run/wrapup-metrics.jsonl
|
|
112
|
+
expectFile: .spur/run/wrapup-metrics.jsonl
|
|
113
|
+
timeoutMs: ${vars.stepTimeoutMs}
|
|
114
|
+
- kind: shell
|
|
115
|
+
options:
|
|
116
|
+
command: 'test -s .spur/run/wrapup-metrics.jsonl && mkdir -p .spur/memory && cat .spur/run/wrapup-metrics.jsonl >> .spur/memory/wrapup-metrics.jsonl'
|
|
117
|
+
|
|
118
|
+
- id: feature-transition
|
|
119
|
+
description: >
|
|
120
|
+
If vars.feature is set, advance the feature through the legal forward path of
|
|
121
|
+
the feature-lifecycle FSM via `spur feature advance` (task 0180 F9c / ADR-029).
|
|
122
|
+
The verb walks backlog → active → verifying → done idempotently, verifying the
|
|
123
|
+
observed status after every hop, and returns a per-hop trail. Replaces the prior
|
|
124
|
+
~20-line inline shell ladder; the verb now owns the multi-hop walk + legal-edge
|
|
125
|
+
enforcement. Task statuses are NOT mutated.
|
|
126
|
+
onEnter:
|
|
127
|
+
- kind: shell
|
|
128
|
+
options:
|
|
129
|
+
command: '${vars.spurBin} feature advance ${vars.feature} --json'
|
|
130
|
+
|
|
131
|
+
- id: branch-cleanup
|
|
132
|
+
description: >
|
|
133
|
+
Irreversible HITL gate. If vars.merge=true, dispatch branch cleanup (merge or
|
|
134
|
+
delete). This gate ALWAYS pauses — even under --auto — because branch operations
|
|
135
|
+
are irreversible (Auto-Decision Principle #6). The operator must explicitly confirm.
|
|
136
|
+
pause: true
|
|
137
|
+
onEnter:
|
|
138
|
+
- kind: hitl.confirm
|
|
139
|
+
options:
|
|
140
|
+
prompt: "Branch cleanup for tasks ${vars.tasks}. This is IRREVERSIBLE (merge or delete). Confirm to proceed?"
|
|
141
|
+
|
|
142
|
+
- id: done
|
|
143
|
+
description: >
|
|
144
|
+
Terminal — wrap-up complete. Output summary: tasks wrapped, learnings captured,
|
|
145
|
+
metrics recorded, feature advanced (if applicable), branch cleaned (if applicable).
|
|
146
|
+
onEnter:
|
|
147
|
+
- kind: note
|
|
148
|
+
options:
|
|
149
|
+
message: "Wrap-up pipeline complete for tasks: ${vars.tasks}. Learnings at .spur/memory/learnings.md, metrics at .spur/memory/wrapup-metrics.jsonl."
|
|
150
|
+
# Checkpoint write: record session state for resume (Phase 4, task 0171 R3)
|
|
151
|
+
- kind: shell
|
|
152
|
+
options:
|
|
153
|
+
command: 'mkdir -p .spur/memory/sessions && echo "checkpoint: wrapup-pipeline done tasks=${vars.tasks} ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)" > .spur/memory/sessions/wrapup-checkpoint.md'
|
|
154
|
+
|
|
155
|
+
- id: skipped
|
|
156
|
+
description: Terminal — wrap-up skipped (empty task list or operator abort).
|
|
157
|
+
|
|
158
|
+
transitions:
|
|
159
|
+
# ── start -> task-resolve ──
|
|
160
|
+
- from: start
|
|
161
|
+
to: task-resolve
|
|
162
|
+
description: Begin wrap-up.
|
|
163
|
+
guard:
|
|
164
|
+
kind: always
|
|
165
|
+
|
|
166
|
+
# ── task-resolve: non-empty -> doc-sync, empty -> skipped ──
|
|
167
|
+
- from: task-resolve
|
|
168
|
+
to: doc-sync
|
|
169
|
+
description: Task list resolved and non-empty — begin doc-sync.
|
|
170
|
+
guard:
|
|
171
|
+
kind: shell
|
|
172
|
+
options:
|
|
173
|
+
command: 'test "$(echo "${vars.tasks}" | jq length)" -gt 0'
|
|
174
|
+
- from: task-resolve
|
|
175
|
+
to: skipped
|
|
176
|
+
description: Task list is empty — skip wrap-up.
|
|
177
|
+
guard:
|
|
178
|
+
kind: shell
|
|
179
|
+
options:
|
|
180
|
+
command: 'test "$(echo "${vars.tasks}" | jq length)" -eq 0'
|
|
181
|
+
|
|
182
|
+
# ── linear body: doc-sync -> learning-capture -> metrics-record ──
|
|
183
|
+
- from: doc-sync
|
|
184
|
+
to: learning-capture
|
|
185
|
+
description: Doc sync complete — capture learnings.
|
|
186
|
+
guard:
|
|
187
|
+
kind: always
|
|
188
|
+
- from: learning-capture
|
|
189
|
+
to: metrics-record
|
|
190
|
+
description: Learnings captured — record metrics.
|
|
191
|
+
guard:
|
|
192
|
+
kind: always
|
|
193
|
+
|
|
194
|
+
# ── metrics-record: conditional routing based on feature and merge ──
|
|
195
|
+
# Declaration order matters: feature-transition is tried first (if feature is set),
|
|
196
|
+
# then branch-cleanup (if merge=true but no feature), then done (neither).
|
|
197
|
+
- from: metrics-record
|
|
198
|
+
to: feature-transition
|
|
199
|
+
description: Feature id is set — advance feature through legal lifecycle edges.
|
|
200
|
+
guard:
|
|
201
|
+
kind: shell
|
|
202
|
+
options:
|
|
203
|
+
command: 'test -n "${vars.feature}"'
|
|
204
|
+
- from: metrics-record
|
|
205
|
+
to: branch-cleanup
|
|
206
|
+
description: No feature id, but merge is requested — go to branch cleanup.
|
|
207
|
+
guard:
|
|
208
|
+
kind: shell
|
|
209
|
+
options:
|
|
210
|
+
command: 'test -z "${vars.feature}" && test "${vars.merge}" = true'
|
|
211
|
+
- from: metrics-record
|
|
212
|
+
to: done
|
|
213
|
+
description: No feature id and no merge — wrap-up is done.
|
|
214
|
+
guard:
|
|
215
|
+
kind: shell
|
|
216
|
+
options:
|
|
217
|
+
command: 'test -z "${vars.feature}" && test "${vars.merge}" != true'
|
|
218
|
+
|
|
219
|
+
# ── feature-transition: -> branch-cleanup (if merge) or done ──
|
|
220
|
+
- from: feature-transition
|
|
221
|
+
to: branch-cleanup
|
|
222
|
+
description: Feature advanced — merge is requested.
|
|
223
|
+
guard:
|
|
224
|
+
kind: shell
|
|
225
|
+
options:
|
|
226
|
+
command: 'test "${vars.merge}" = true'
|
|
227
|
+
- from: feature-transition
|
|
228
|
+
to: done
|
|
229
|
+
description: Feature advanced — no merge requested.
|
|
230
|
+
guard:
|
|
231
|
+
kind: shell
|
|
232
|
+
options:
|
|
233
|
+
command: 'test "${vars.merge}" != true'
|
|
234
|
+
|
|
235
|
+
# ── branch-cleanup: HITL confirmed -> done ──
|
|
236
|
+
- from: branch-cleanup
|
|
237
|
+
to: done
|
|
238
|
+
description: Branch cleanup confirmed — wrap-up complete.
|
|
239
|
+
guard:
|
|
240
|
+
kind: always
|