@gobing-ai/spur 0.3.67 → 0.3.69
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 +2 -50
- package/config/templates/docs/99_PROJECT_CONSTITUTION.md +21 -16
- package/config/workflow-composition-baseline.json +110 -60
- package/config/workflows/basic.yaml +1 -1
- package/config/workflows/docs-pipeline.yaml +99 -23
- package/config/workflows/feature-dev.yaml +1 -1
- package/config/workflows/idea-pipeline.yaml +21 -23
- package/config/workflows/task-pipeline.yaml +233 -121
- package/config/workflows/wayfinder-resolution.yaml +1 -1
- package/config/workflows/wrapup-pipeline.yaml +1 -1
- package/package.json +9 -9
- package/plugins/sp/README.md +6 -7
- package/plugins/sp/commands/dev-idea.md +5 -3
- package/plugins/sp/commands/dev-plan.md +3 -1
- package/plugins/sp/commands/dev-review-session.md +2 -1
- package/plugins/sp/hooks/context-post-tool.ts +101 -2
- package/plugins/sp/hooks/context-session-start.ts +22 -1
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/stage-registry-adapter.ts +144 -2
- package/plugins/sp/scripts/surface-drift-inventory.ts +2 -15
- package/plugins/sp/scripts/task-size-precheck.ts +21 -79
- package/plugins/sp/skills/session-review/SKILL.md +16 -0
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +42 -17
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +6 -6
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +80 -11
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +18 -13
- package/spur.js +2062 -609
- package/web/_astro/{BoardApp.BEtcJqde.js → BoardApp.BQFbkeqq.js} +15 -15
- package/web/_astro/BoardApp.CTkqrhWd.js +1 -0
- package/web/_astro/{TaskDetail.ClAbCXom.js → TaskDetail.Dl2Eaj1w.js} +1 -1
- package/web/_astro/{arc.CCvf51_y.js → arc.uG14rp8A.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.C0cb0J5M.js → architectureDiagram-3BPJPVTR.Dye6uD_x.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.CIyjqoCE.js → blockDiagram-GPEHLZMM.B9Pkh7Hb.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.fs14IuFs.js → c4Diagram-AAUBKEIU.C2x7SC_X.js} +1 -1
- package/web/_astro/channel.Dsvulp7W.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.CaBKv4ZO.js → chunk-2J33WTMH.D2p4-nWk.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.BOllTPto.js → chunk-4BX2VUAB.S-6jf33o.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.ChEof0O4.js → chunk-55IACEB6.DVXj4Fdh.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.Co2kdjD8.js → chunk-727SXJPM.Dz689FMN.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.SWmfcnog.js → chunk-AQP2D5EJ.KxYj5TnI.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.rDAFifF3.js → chunk-FMBD7UC4.itTQyHQB.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.BCnoXKCw.js → chunk-ND2GUHAM.euSrbJf5.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.RSmy2hDO.js → chunk-QZHKN3VN.OWASJRQy.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.Be7PEfrX.js → classDiagram-4FO5ZUOK.BLvrlpNO.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.Be7PEfrX.js → classDiagram-v2-Q7XG4LA2.BLvrlpNO.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.BkUp2aSK.js → cose-bilkent-S5V4N54A.XBF-rmyD.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.BegGGlUV.js → cynefin-OW5HDTMX.DlCx762Z.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.BkUdjsaC.js → dagre-BM42HDAG.D17Rshxv.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.E9vugt3-.js → diagram-2AECGRRQ.AhBIVJC8.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.Dj4yeHXB.js → diagram-5GNKFQAL.C9ximjyC.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.Buaquwli.js → diagram-KO2AKTUF.CZb7Ru_9.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.BV3dgGgm.js → diagram-LMA3HP47.BW7LwqoS.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.Cnx3s-tc.js → diagram-OG6HWLK6.XC025W0V.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.DKK_abu4.js → erDiagram-TEJ5UH35.CpMXmBDP.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.BNuu9fbm.js → flowDiagram-I6XJVG4X.D2ednJWg.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.b16KUMjy.js → ganttDiagram-6RSMTGT7.BjL9FGKO.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.Kh41lbG5.js → gitGraphDiagram-PVQCEYII.B90g1VGk.js} +1 -1
- package/web/_astro/{infoDiagram-5YYISTIA.DEWBXkp-.js → infoDiagram-5YYISTIA.RqgycKtQ.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.DiAdmcL6.js → ishikawaDiagram-YF4QCWOH.Ctn-zt6a.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.D1Ki7IRm.js → journeyDiagram-JHISSGLW.DJhT8Ctp.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.CWUhrQpc.js → kanban-definition-UN3LZRKU.BY1QdejI.js} +1 -1
- package/web/_astro/{linear.BaFsgcCe.js → linear.Di7YObSt.js} +1 -1
- package/web/_astro/{mermaid.core.CHw_AsGy.js → mermaid.core.CbxtJS3Q.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.UIhghgmN.js → mindmap-definition-RKZ34NQL.CxvR4g_J.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.D05l3JUA.js → pieDiagram-4H26LBE5.jNWqnBHH.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.BcWIhIcE.js → quadrantDiagram-W4KKPZXB.BCp12MbA.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.B1rYvKGn.js → requirementDiagram-4Y6WPE33.Dxhm4TyR.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.CKylVRC4.js → sankeyDiagram-5OEKKPKP.BtQXp4J9.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.Dm3uA_s4.js → sequenceDiagram-3UESZ5HK.BWEM1R_Q.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.Bgca_BLe.js → stateDiagram-AJRCARHV.BG3wUkWB.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.C1T7YFrG.js → stateDiagram-v2-BHNVJYJU.BLtMeFVP.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.ZOHJn3Sn.js → timeline-definition-PNZ67QCA.D5fHo0az.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.DegZitjD.js → vennDiagram-CIIHVFJN.0DcuMluU.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.BDsC115d.js → wardleyDiagram-YWT4CUSO.BZ-dxgHm.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.D0MO70ea.js → xychartDiagram-2RQKCTM6.Bg-XWF7z.js} +1 -1
- package/web/index.html +1 -1
- package/web/_astro/BoardApp.DBEin4N5.js +0 -1
- package/web/_astro/channel.BGn_DUCD.js +0 -1
|
@@ -1,24 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
/**
|
|
3
|
-
* task-size-precheck — pipeline size precheck guard (R2, task 0454
|
|
4
|
-
*
|
|
3
|
+
* task-size-precheck — pipeline size precheck guard (R2, task 0454; count-only
|
|
4
|
+
* since task 0723).
|
|
5
5
|
*
|
|
6
6
|
* Shells `spur task show <wbs> --json`, evaluates R-item count and Plan
|
|
7
|
-
* checklist count against limits, writes PASS/FAIL to status file.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* checklist count against limits, writes PASS/FAIL to status file. No executor
|
|
8
|
+
* or doctor involvement: executor liveness/routing/capabilities are attested
|
|
9
|
+
* fail-closed at the `agent.run` dispatch boundary, not predicted here.
|
|
10
10
|
*
|
|
11
|
-
* Always exits 0 (soft
|
|
12
|
-
* task-pipeline.yaml reads the status file
|
|
11
|
+
* Always exits 0 (soft action). The precheck→implement guard in
|
|
12
|
+
* task-pipeline.yaml reads the status file; a missing or failing checker writes
|
|
13
|
+
* FAIL, so readiness fails closed.
|
|
13
14
|
*
|
|
14
15
|
* Ships with the plugin to arbitrary projects, so it stays node-builtin-only —
|
|
15
|
-
* no workspace imports.
|
|
16
|
-
* than from `getExecutorTier` directly; `spur agent doctor --json` exposes it as
|
|
17
|
-
* `capabilityTier` precisely so the inference regex is not duplicated here.
|
|
16
|
+
* no workspace imports.
|
|
18
17
|
*
|
|
19
18
|
* Usage:
|
|
20
19
|
* bun plugins/sp/scripts/task-size-precheck.ts <wbs> [--spur-bin <path>]
|
|
21
|
-
* [--max-reqs <n>] [--max-plan-items <n>]
|
|
20
|
+
* [--max-reqs <n>] [--max-plan-items <n>]
|
|
22
21
|
*
|
|
23
22
|
* Env: SPUR_BIN, MAX_IMPLEMENT_REQS, MAX_IMPLEMENT_PLAN_ITEMS
|
|
24
23
|
*/
|
|
@@ -27,7 +26,6 @@ import { execFileSync } from 'node:child_process';
|
|
|
27
26
|
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
28
27
|
import { join } from 'node:path';
|
|
29
28
|
import { fileURLToPath } from 'node:url';
|
|
30
|
-
import { STAGE_FLOOR_TIER, TIER_ORDER } from './stage-registry-adapter';
|
|
31
29
|
|
|
32
30
|
// ─── Regex (sync with packages/app/src/services/task-size-precheck.ts) ───────
|
|
33
31
|
|
|
@@ -37,32 +35,11 @@ const R_ITEM_RE = /^\s*-\s*\[[ xX]\]\s*(\*\*)?R\d+\./m;
|
|
|
37
35
|
/** Matches checklist items under the Plan section. */
|
|
38
36
|
const CHECKLIST_ITEM_RE = /^\s*-\s*\[[ xX]\]/m;
|
|
39
37
|
|
|
40
|
-
/**
|
|
41
|
-
* Large-task thresholds for the capability gate (R3, task 0487) — the DEFAULT
|
|
42
|
-
* caps, not the overridable `--max-*` limits. Raising the caps says "I accept a
|
|
43
|
-
* big task"; it does not make a flash-tier model able to finish one inside
|
|
44
|
-
* `implementTimeoutMs`.
|
|
45
|
-
*/
|
|
46
|
-
const LARGE_TASK_REQS = 5;
|
|
47
|
-
const LARGE_TASK_PLAN_ITEMS = 8;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Capability tiers strong enough for a large task (R3, task 0487). The floor is
|
|
51
|
-
* the `review` stage's Layer-1 tier — `reviewer` per `references/roles.md`,
|
|
52
|
-
* read via the stage-registry adapter (0538 R4: no tier literal here; roles.md
|
|
53
|
-
* is the pointer). Tiers at or above the floor pass. An unreachable roles.md
|
|
54
|
-
* degrades to the pre-reconcile band — fail-closed for a safety gate.
|
|
55
|
-
*/
|
|
56
|
-
const CAPABLE_TIERS: ReadonlySet<string> = (() => {
|
|
57
|
-
const floor = STAGE_FLOOR_TIER.get('review') ?? 'capable-1';
|
|
58
|
-
return new Set(TIER_ORDER.slice(Math.max(0, TIER_ORDER.indexOf(floor))));
|
|
59
|
-
})();
|
|
60
|
-
|
|
61
38
|
// ─── CLI ─────────────────────────────────────────────────────────────────────
|
|
62
39
|
|
|
63
40
|
function usage(): never {
|
|
64
41
|
console.error(
|
|
65
|
-
'Usage: bun plugins/sp/scripts/task-size-precheck.ts <wbs> [--spur-bin <path>] [--max-reqs <n>] [--max-plan-items <n>]
|
|
42
|
+
'Usage: bun plugins/sp/scripts/task-size-precheck.ts <wbs> [--spur-bin <path>] [--max-reqs <n>] [--max-plan-items <n>]',
|
|
66
43
|
);
|
|
67
44
|
process.exit(1);
|
|
68
45
|
}
|
|
@@ -87,13 +64,14 @@ function parseArgs(argv: string[]): {
|
|
|
87
64
|
spurBin: string;
|
|
88
65
|
maxReqs: number;
|
|
89
66
|
maxPlanItems: number;
|
|
90
|
-
executor: string;
|
|
91
67
|
} {
|
|
92
68
|
let spurBin = defaultSpurBin();
|
|
93
69
|
let wbs = '';
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
70
|
+
// Doubled deterministic ceiling (0723 operator decision): 10 R-items / 16
|
|
71
|
+
// Plan items — keep in sync with DEFAULT_TASK_SIZE_LIMITS in
|
|
72
|
+
// packages/app/src/services/task-size-precheck.ts (asserted by test).
|
|
73
|
+
let maxReqs = Number(process.env.MAX_IMPLEMENT_REQS) || 10;
|
|
74
|
+
let maxPlanItems = Number(process.env.MAX_IMPLEMENT_PLAN_ITEMS) || 16;
|
|
97
75
|
|
|
98
76
|
let i = 0;
|
|
99
77
|
while (i < argv.length) {
|
|
@@ -102,13 +80,10 @@ function parseArgs(argv: string[]): {
|
|
|
102
80
|
spurBin = argv[i + 1] ?? defaultSpurBin();
|
|
103
81
|
i += 2;
|
|
104
82
|
} else if (arg === '--max-reqs') {
|
|
105
|
-
maxReqs = Number(argv[i + 1]) ||
|
|
83
|
+
maxReqs = Number(argv[i + 1]) || 10;
|
|
106
84
|
i += 2;
|
|
107
85
|
} else if (arg === '--max-plan-items') {
|
|
108
|
-
maxPlanItems = Number(argv[i + 1]) ||
|
|
109
|
-
i += 2;
|
|
110
|
-
} else if (arg === '--executor') {
|
|
111
|
-
executor = argv[i + 1] ?? '';
|
|
86
|
+
maxPlanItems = Number(argv[i + 1]) || 16;
|
|
112
87
|
i += 2;
|
|
113
88
|
} else if (!arg.startsWith('--')) {
|
|
114
89
|
wbs = arg;
|
|
@@ -119,7 +94,7 @@ function parseArgs(argv: string[]): {
|
|
|
119
94
|
}
|
|
120
95
|
|
|
121
96
|
if (!wbs) usage();
|
|
122
|
-
return { wbs, spurBin, maxReqs, maxPlanItems
|
|
97
|
+
return { wbs, spurBin, maxReqs, maxPlanItems };
|
|
123
98
|
}
|
|
124
99
|
|
|
125
100
|
/**
|
|
@@ -135,29 +110,8 @@ function runSpur(spurBin: string, args: string[]): string {
|
|
|
135
110
|
});
|
|
136
111
|
}
|
|
137
112
|
|
|
138
|
-
/**
|
|
139
|
-
* Capability tier of `executor` per `spur agent doctor <exec> --json`.
|
|
140
|
-
* Unknown executor, unreadable doctor output, or an undeclared-and-uninferrable
|
|
141
|
-
* tier all read as `standard` — conservative: a false block is one flag away,
|
|
142
|
-
* a false pass costs a 30-minute timed-out implement.
|
|
143
|
-
*/
|
|
144
|
-
function resolveCapabilityTier(spurBin: string, executor: string): { tier: string; resolvedName: string } {
|
|
145
|
-
try {
|
|
146
|
-
const out = runSpur(spurBin, ['agent', 'doctor', executor, '--json']);
|
|
147
|
-
const row = JSON.parse(out)?.agents?.[0];
|
|
148
|
-
const tier = row?.capabilityTier;
|
|
149
|
-
// R1 (0622 F2/F4 residue): `doctor <role>` resolves the role to its cheapest
|
|
150
|
-
// eligible executor (`coder` → `omp`); surface the resolved executor name in
|
|
151
|
-
// the block message, not the role the caller passed in.
|
|
152
|
-
const resolvedName = typeof row?.agent === 'string' && row.agent.length > 0 ? row.agent : executor;
|
|
153
|
-
return { tier: typeof tier === 'string' && tier ? tier : 'standard', resolvedName };
|
|
154
|
-
} catch {
|
|
155
|
-
return { tier: 'standard', resolvedName: executor };
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
113
|
function main(): void {
|
|
160
|
-
const { wbs, spurBin, maxReqs, maxPlanItems
|
|
114
|
+
const { wbs, spurBin, maxReqs, maxPlanItems } = parseArgs(process.argv.slice(2));
|
|
161
115
|
|
|
162
116
|
// Fetch task content via spur
|
|
163
117
|
let taskContent: string;
|
|
@@ -166,7 +120,7 @@ function main(): void {
|
|
|
166
120
|
const task = JSON.parse(result);
|
|
167
121
|
taskContent = task.content ?? task.body ?? '';
|
|
168
122
|
} catch {
|
|
169
|
-
// If spur fails, write FAIL and exit 0
|
|
123
|
+
// If spur fails, write FAIL and exit 0 — the status file carries the verdict
|
|
170
124
|
const statusDir = join(process.cwd(), '.spur', 'run');
|
|
171
125
|
if (!existsSync(statusDir)) mkdirSync(statusDir, { recursive: true });
|
|
172
126
|
writeFileSync(join(statusDir, `${wbs}-precheck-size.status`), 'FAIL\n');
|
|
@@ -187,18 +141,6 @@ function main(): void {
|
|
|
187
141
|
const planItemCount = planBody.match(new RegExp(CHECKLIST_ITEM_RE.source, 'gm'))?.length ?? 0;
|
|
188
142
|
|
|
189
143
|
const reasons: string[] = [];
|
|
190
|
-
// R3 (0487): a large task on a sub-capable executor blocks even when the caller
|
|
191
|
-
// raised the caps — the caps are an acceptance of size, not a capability grant.
|
|
192
|
-
if (executor && (reqCount > LARGE_TASK_REQS || planItemCount > LARGE_TASK_PLAN_ITEMS)) {
|
|
193
|
-
const { tier, resolvedName } = resolveCapabilityTier(spurBin, executor);
|
|
194
|
-
if (!CAPABLE_TIERS.has(tier)) {
|
|
195
|
-
reasons.push(
|
|
196
|
-
`Task size (${reqCount} R-items / ${planItemCount} Plan items) requires a capable executor, ` +
|
|
197
|
-
`but ${resolvedName} is tier ${tier}. ` +
|
|
198
|
-
`Pass \`--agent <capable>\` or \`--vars '{"implementAgent":"<capable>"}'\`, or split the task.`,
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
144
|
if (reqCount > maxReqs) {
|
|
203
145
|
reasons.push(
|
|
204
146
|
`Task has ${reqCount} R-items (max ${maxReqs}). ` +
|
|
@@ -46,6 +46,9 @@ cross-agent windows, recurrence, trends, or quantitative performance forensics.
|
|
|
46
46
|
name the confirmation needed.
|
|
47
47
|
- State `not available` when compaction or missing output removed evidence. Never reconstruct it from
|
|
48
48
|
memory or claim a verification that did not run.
|
|
49
|
+
- Derive timing only from timestamps and tool-call records visible in the active session. Use
|
|
50
|
+
non-overlapping stages whose durations sum to elapsed time; render unavailable duration or call
|
|
51
|
+
counts as `n/a` instead of estimating them.
|
|
49
52
|
|
|
50
53
|
## Triage mode (`--triage`)
|
|
51
54
|
|
|
@@ -91,6 +94,19 @@ three buckets — never skip triage and start fixing from the raw findings list.
|
|
|
91
94
|
|
|
92
95
|
State the overall result in one to three sentences, including partial or blocked scope.
|
|
93
96
|
|
|
97
|
+
### Time breakdown
|
|
98
|
+
|
|
99
|
+
Summarize elapsed time and, when supported by evidence, productive work, avoidable setup/recovery,
|
|
100
|
+
and operator wait time. Then render non-overlapping stages:
|
|
101
|
+
|
|
102
|
+
| Stage | Time | Tool calls | Assessment |
|
|
103
|
+
| --- | ---: | ---: | --- |
|
|
104
|
+
|
|
105
|
+
Format durations as `M:SS` below one hour and `H:MM:SS` at one hour or above (`1:44`, not `1m44s`;
|
|
106
|
+
`0:33`, not `33s`). Include a `Total` row when elapsed time is available. Keep operator approval
|
|
107
|
+
waits separate from execution bottlenecks. Use `n/a` for any value not supported by the active
|
|
108
|
+
session evidence.
|
|
109
|
+
|
|
94
110
|
### Resolved issues
|
|
95
111
|
|
|
96
112
|
| Issue | Root cause | Resolution | Evidence |
|
|
@@ -96,9 +96,9 @@ surface is already resolved and name the trigger / `operator override`. A comman
|
|
|
96
96
|
inside that subprocess boundary runs its backing skill in that process; it must not spawn another
|
|
97
97
|
`spur agent run` for the same trigger. This prevents recursive dispatch.
|
|
98
98
|
|
|
99
|
-
**Pipeline wrappers (`dev-run`, `dev-runall`)** — the orchestrator is a loop; its *stages* do the
|
|
99
|
+
**Pipeline wrappers (`dev-run`, `dev-runall`, `dev-idea`, `dev-plan`)** — the orchestrator is a loop; its *stages* do the
|
|
100
100
|
model-bearing work. Interactive omit/`inline` therefore uses the
|
|
101
|
-
[inline pipeline driver](inline-pipeline-driver.md): it reads
|
|
101
|
+
[inline pipeline driver](inline-pipeline-driver.md): it reads the selected pipeline YAML, executes each
|
|
102
102
|
`agent.run` input through the backing skill in the host session, and preserves every shell action
|
|
103
103
|
and guard. `auto` or a named executor is merged into per-task `vars.agent` and
|
|
104
104
|
`vars.implementAgent`, and the workflow's `agent.run` steps run under that subprocess executor (see
|
|
@@ -122,9 +122,11 @@ leg for eligible stages. Operator
|
|
|
122
122
|
confirmation actions, `pause: true`, and approve/taste/ask decisions stay host-owned. Each inline
|
|
123
123
|
model stage appends `stage <id> executed inline in session <session-id>` to its run log; a
|
|
124
124
|
subagent-dispatched stage appends `stage <id> executed via subagent <agent-id> (host session
|
|
125
|
-
<session-id>)` instead. `dev-
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
<session-id>)` instead. `dev-idea` and `dev-plan` also drive `idea-pipeline.yaml` in the host session,
|
|
126
|
+
with no native subagent unless the operator explicitly requests delegation. `--agent auto` or a
|
|
127
|
+
name, parallel batches, and every headless `spur workflow run` / `spur agent run` use subprocesses;
|
|
128
|
+
dev-command workflow subprocesses launch `--async` so cancellation owns a process group and only
|
|
129
|
+
`killed: true` means a live run stopped. `dev-run --mode implement` continues to run its single competency in-session under omitted
|
|
128
130
|
`--agent` or explicit `--agent inline` (identical values, 0687 R1).
|
|
129
131
|
|
|
130
132
|
### Executor precedence chain (R7)
|
|
@@ -137,13 +139,14 @@ resolved in this order; first match wins:
|
|
|
137
139
|
2. **`agent.default`** from `.spur/config.yaml` (project layer, then `~/.config/spur/config.yaml`) —
|
|
138
140
|
`spur workflow run` injects it as the `agent` var when `vars.agent` was not set by the caller.
|
|
139
141
|
3. **YAML literal `agent:` in the pipeline file** — the last-resort fallback declared in the
|
|
140
|
-
workflow YAML
|
|
142
|
+
workflow YAML. Every shipped pipeline declares `agent: "auto"`, so this rung resolves through
|
|
143
|
+
the role/tier ladder instead of pinning an executor name; it fires only when no
|
|
141
144
|
`agent.default` is configured anywhere.
|
|
142
145
|
|
|
143
146
|
`--agent auto` tier-resolves an executor (stage `model_policy` → `agent.default` → tier priority)
|
|
144
147
|
**before** merging, so it enters the chain at step 1 already resolved to a concrete name.
|
|
145
148
|
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
|
|
149
|
+
(0687 R3) instead of rejecting — it resolves exactly like an omitted flag. Interactive pipeline wrappers
|
|
147
150
|
consume both inline resolutions identically (0508 eligibility as generalized by 0687 R2) before this
|
|
148
151
|
chain and use the host driver. Omitting the flag on a headless surface forwards nothing, so the
|
|
149
152
|
spawned step resolves to `agent.default` (step 2) or the YAML literal (step 3).
|
|
@@ -193,18 +196,17 @@ explicit process boundary and retain their existing resolution, output, timeout,
|
|
|
193
196
|
contracts. The interactive task wrapper does not change the YAML or engine; it reads the YAML as
|
|
194
197
|
SSOT and interprets the actions in-session before any workflow subprocess exists. It records inline
|
|
195
198
|
provenance without fabricating an `AgentRunTracedResult`.
|
|
196
|
-
`spur agent run`
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
CLI's resolution.
|
|
199
|
+
`spur agent run` resolves omitted or explicit `inline` through tier substitution on its headless
|
|
200
|
+
surface and emits one warning naming the concrete executor; `auto` tier-resolves without the inline
|
|
201
|
+
warning. Interactive dev wrappers consume inline before this boundary, so they never spawn it.
|
|
200
202
|
|
|
201
203
|
### Inline trade-off
|
|
202
204
|
|
|
203
205
|
Inline avoids process startup and preserves the host session's context and tools. Relative to
|
|
204
206
|
subprocess dispatch it provides **no isolated workspace**, **no per-stage subprocess action
|
|
205
207
|
record**, **no independent timeout or abort boundary**, and **no tier-selected executor**: the
|
|
206
|
-
executor is the current coding agent. Interactive
|
|
207
|
-
|
|
208
|
+
executor is the current coding agent. Interactive pipelines retain a run log and session provenance
|
|
209
|
+
through the inline driver; task pipelines additionally record a task run-link. If process isolation or an independently killable
|
|
208
210
|
stage is required, select the subprocess path (`--agent auto` or `--agent <name>`).
|
|
209
211
|
|
|
210
212
|
## Every write is CLI-gated
|
|
@@ -611,22 +613,30 @@ CLI-gated corpus artifact. The `wrapup-pipeline.yaml` `learning-capture` step wr
|
|
|
611
613
|
## Session Checkpoint Convention
|
|
612
614
|
|
|
613
615
|
Long-running pipelines write resumable checkpoints to `.spur/memory/sessions/` so an interrupted
|
|
614
|
-
run can be resumed. The
|
|
615
|
-
|
|
616
|
+
run can be resumed. The canonical frontmatter schema below is parsed by
|
|
617
|
+
`packages/app/src/workflow/checkpoint-contract.ts` (task 0711): a checkpoint that does not match
|
|
618
|
+
it is ignored by routing/cleanup (safe fallthrough) and never reclaimed.
|
|
616
619
|
|
|
617
|
-
**Format:** Markdown file with YAML frontmatter:
|
|
620
|
+
**Format:** Markdown file with YAML frontmatter (canonical field set, task 0711):
|
|
618
621
|
|
|
619
622
|
```yaml
|
|
620
623
|
---
|
|
624
|
+
schema_version: 1
|
|
621
625
|
session_id: "2026-07-01-0167"
|
|
622
626
|
workflow: "task-pipeline"
|
|
623
627
|
run_id: "wf_..."
|
|
624
628
|
task_wbs: "0167"
|
|
625
629
|
feature_id: "I"
|
|
626
630
|
phase: "verify"
|
|
631
|
+
status: "running"
|
|
627
632
|
last_gate: "review-approved"
|
|
628
|
-
|
|
633
|
+
source_commit: "<full 40-hex HEAD at write time>"
|
|
634
|
+
digest: "sha256:..."
|
|
635
|
+
generated_at: "2026-07-01T18:30:00Z"
|
|
636
|
+
updated_at: "2026-07-01T18:30:00Z"
|
|
629
637
|
next_action: "run verification"
|
|
638
|
+
artifacts:
|
|
639
|
+
- .spur/run/0167-verdict.json
|
|
630
640
|
---
|
|
631
641
|
|
|
632
642
|
## Session Notes
|
|
@@ -634,6 +644,17 @@ next_action: "run verification"
|
|
|
634
644
|
<free-form markdown: what was done, what's pending, any blockers>
|
|
635
645
|
```
|
|
636
646
|
|
|
647
|
+
Field semantics (enforced by `parseCheckpointMetadata` / `checkpointStaleness`):
|
|
648
|
+
|
|
649
|
+
- `schema_version` must be `1`; any other value → ignored.
|
|
650
|
+
- `status` is one of `running|pending|approved|done|failed|cancelled|skipped`. A terminal status
|
|
651
|
+
(`done|failed|cancelled|skipped`) marks the checkpoint terminal — never resumed, only cleaned
|
|
652
|
+
up once retention expires.
|
|
653
|
+
- `source_commit` pins repository HEAD at write time; drift staleness is reported, not hidden.
|
|
654
|
+
- `artifacts` lists referenced run files; a missing artifact makes the checkpoint stale.
|
|
655
|
+
- Writers: write after every HITL gate decision, phase transition, and terminal state; overwrite
|
|
656
|
+
the same file on resume (`session_id` = `<date>-<wbs-or-feature>`).
|
|
657
|
+
|
|
637
658
|
**Write checkpoints after:**
|
|
638
659
|
|
|
639
660
|
- Every HITL gate decision (approved/rejected/deferred).
|
|
@@ -652,6 +673,10 @@ next_action: "run verification"
|
|
|
652
673
|
- **Not CLI-gated.** Checkpoint files are written directly by the pipeline's checkpoint action
|
|
653
674
|
(a `shell` step that writes to `.spur/memory/sessions/<session-id>.md`). They do not go through
|
|
654
675
|
`spur task update`.
|
|
676
|
+
- **Canonical schema.** The frontmatter above is the contract (task 0711 R1). Non-canonical or
|
|
677
|
+
malformed checkpoints are ignored by the router and kept (never silently deleted) by cleanup.
|
|
678
|
+
- **Retention.** Terminal checkpoints older than `workflowLogRetentionDays` with no active run are
|
|
679
|
+
reclaimed by `spur workflow clean` (task 0711 R5–R8); non-terminal checkpoints are always kept.
|
|
655
680
|
- **Not a validated corpus.** Checkpoints are working memory. They are overwritten when a session
|
|
656
681
|
resumes and re-checkpoints. They are NOT authoritative task state — the task file is.
|
|
657
682
|
- **One file per session.** The `session_id` is `<date>-<wbs-or-feature>`. A resumed session
|
|
@@ -74,7 +74,7 @@ each would be scope creep for one-liner procedures.
|
|
|
74
74
|
| 4 | run | `dev-run` | `Skill()` | `sp:spur-dev` (`run` / `implement`) | `<wbs> [--mode <full\|implement>] [--agent <inline\|auto\|name>] [--auto] [--next] [--wrap] [--continue] [--worktree [<name>]]` |
|
|
75
75
|
| 5 | refine | `dev-refine` | `Skill()` | `sp:spur-dev` (`refine`) | `<wbs> [--focus <mode>] [--description <text>] [--depth <standard\|ready>] [--agent <inline\|auto\|name>] [--auto] [--next]` |
|
|
76
76
|
| 5a | refineall | `dev-refineall` | `Skill()` | `sp:spur-dev` (`refineall`) | `--feature <id> \| --tasks <selector> [--focus <mode>] [--description <text>] [--depth <standard\|ready>] [--agent <inline\|auto\|name>] [--auto] [--keep-going] [--status <s>] [--json] [--worktree [<name>]]` |
|
|
77
|
-
| 6 | plan | `dev-plan` | `Skill()` |
|
|
77
|
+
| 6 | plan | `dev-plan` | `Skill()` | inline driver (`idea-pipeline`) or async workflow | `"<description>" [--feature <id>] [--parent <feature-id>] [--agent <inline\|auto\|name>] [--skip-design] [--auto] [--approve-taste]` |
|
|
78
78
|
| 7 | docs | _(no thin wrapper)_ | `Skill()` | `sp:doc-evolve` | `"<change description>"` |
|
|
79
79
|
| 8 | changelog | `dev-changelog` | `inline` | git log + conventional-commit grouping | `[--since <ref>] [--until <ref>] [--version <ver>]` |
|
|
80
80
|
| 9 | gitmsg | `dev-gitmsg` | `inline` | bounded diff capture → concern grouping → conventional commit | `[--commit] [--squash] [--all] [--scope <path>]` |
|
|
@@ -85,7 +85,7 @@ each would be scope creep for one-liner procedures.
|
|
|
85
85
|
| 13a | parallel | `dev-parallel` | `Skill()` | `sp:parallel-execution` | `--tasks <selector> [--feature <id>] [--mode <fan-out\|review-panel\|investigation>] [--agent <inline\|auto\|name>] [--json]` |
|
|
86
86
|
| 14 | wrap | `dev-wrap` | `Skill()` | `spur workflow run` (wrapup-pipeline) | `<wbs> [--agent <inline\|auto\|name>] [--auto] [--merge] [--dry-run]` |
|
|
87
87
|
| 15 | wrapall | `dev-wrapall` | `Skill()` | `spur workflow run` (wrapup-pipeline) | `[--since <iso>] [--feature <id>] [--status <s>] [--agent <inline\|auto\|name>] [--auto] [--merge] [--dry-run]` |
|
|
88
|
-
| 16 | idea | `dev-idea` | `Skill()` |
|
|
88
|
+
| 16 | idea | `dev-idea` | `Skill()` | inline driver (`idea-pipeline`) or async workflow | `"<idea>" [--auto] [--skip-design] [--approve-taste] [--agent <inline\|auto\|name>]` |
|
|
89
89
|
|
|
90
90
|
## Skill-backed operations
|
|
91
91
|
|
|
@@ -255,7 +255,7 @@ must not be changed without updating the backing skill.
|
|
|
255
255
|
### 6. plan
|
|
256
256
|
|
|
257
257
|
- **Purpose:** Plan a feature from a description — intake → feature create → AC generation → feature check gate → decomposition → batch-create (with **Design by default**).
|
|
258
|
-
- **Inputs:** `"<description>"` (required). `--feature <id>` links to an existing feature. `--parent <feature-id>` nests under a parent.
|
|
258
|
+
- **Inputs:** `"<description>"` (required). `--feature <id>` links to an existing feature. `--parent <feature-id>` nests under a parent. Omitted/`inline` drives `idea-pipeline.yaml` in the host session; `auto` or a name launches the async workflow worker. **Design package flags (unified with `/sp:dev-idea`):**
|
|
259
259
|
- **Default:** author task `design` on every batch item + feature satellite when the seam heuristic fires (**ties lean design**). There is **no** `--design` force flag.
|
|
260
260
|
- `--skip-design` — skip feature satellite **and** omit task `design` fields (scaffold only; refine fills later). Sole design opt-out.
|
|
261
261
|
- `--approve-taste` — with `--auto`, pre-clear design-approval taste pause when that gate is used (`design_approved=true`). Alias: `--design-approved`.
|
|
@@ -340,9 +340,9 @@ must not be changed without updating the backing skill.
|
|
|
340
340
|
- `--skip-design` — design package off (system-design + task Design).
|
|
341
341
|
- `--approve-taste` — with `--auto`, skip **all** remaining taste pauses this run (idea-eval + design-approval). Sets `idea_approved=true` and `design_approved=true`.
|
|
342
342
|
Aliases (prefer `--approve-taste`): `--idea-approved` → `idea_approved`; `--design-approved` → `design_approved`. There is **no** `--design` force flag.
|
|
343
|
-
- **Backing:** `spur workflow run idea-pipeline.yaml`
|
|
344
|
-
- **Behavior:** Builds vars from the table above and
|
|
345
|
-
- **Delegation:**
|
|
343
|
+
- **Backing:** `idea-pipeline.yaml` through the inline driver for omitted/`inline`, or `spur workflow run idea-pipeline.yaml --async` for `auto`/name.
|
|
344
|
+
- **Behavior:** Builds vars from the table above and drives the idea pipeline. Flow: discovery → **idea-eval** (taste; reject → cancelled) → feature-create → ac-generate → feature-check → system-design (conditional) → design-approval (taste) → decompose → batch-create → handoff. STOPS at handoff — no task execution, no pipeline nesting. Headless runs use one `trace --follow`; cancellation is reported stopped only when `workflow cancel --json` returns `killed: true`.
|
|
345
|
+
- **Delegation:** Host-session inline driver by default; explicit executor selection uses the async workflow worker.
|
|
346
346
|
- **Idea-evaluation gate:** After discovery, operator reviews `.spur/run/idea-eval-report.md` ([idea-evaluation.md](idea-evaluation.md)). Approve continues; reject/cancel → no feature. Under `--auto`, still pauses unless taste pre-cleared (`--approve-taste` / alias). Enhanced idea is a sidecar — `vars.idea` is not overwritten.
|
|
347
347
|
- **Design package (`--skip-design` only):**
|
|
348
348
|
|
|
@@ -421,6 +421,23 @@ written for a batch with nothing to run. The early-exit report carries zero per-
|
|
|
421
421
|
terminal action runs. A contract test pins this
|
|
422
422
|
(`plugins/sp/tests/dogfood-testing/execution-batch-contract.test.ts`).
|
|
423
423
|
|
|
424
|
+
**Evidence persistence (worktree batches — task 0720 R3).** A worktree batch's Step 5 report and
|
|
425
|
+
verdict artifacts live in the worktree's own `.spur/run/` while the batch runs — exactly the tree
|
|
426
|
+
create-mode WT-4 deletes. Before any WT-4 removal, persist them into the **invoking** tree, which
|
|
427
|
+
survives removal:
|
|
428
|
+
|
|
429
|
+
- Write the emitted batch report to `.spur/run/worktree-<marker-id>-batch-report.md`.
|
|
430
|
+
- Copy each attempted task's `.spur/run/<wbs>-verdict.json` from the worktree to
|
|
431
|
+
`.spur/run/worktree-<marker-id>-verdicts/<wbs>-verdict.json`.
|
|
432
|
+
- Make the report's per-task verdict references use those persisted invoking-tree paths, not the
|
|
433
|
+
worktree-local paths that removal deletes.
|
|
434
|
+
|
|
435
|
+
Evidence persistence precedes destructive cleanup: a persistence failure (unreadable verdict file,
|
|
436
|
+
disk-full, missing directory) routes to **WT-5** — the worktree and branch are retained so a green
|
|
437
|
+
batch can never destroy its own evidence. Reuse mode retains its operator-owned tree but still
|
|
438
|
+
persists the Step 5 report under the invoking tree; the reused tree's `.spur/run/` remains the live
|
|
439
|
+
copy while that tree lives on.
|
|
440
|
+
|
|
424
441
|
## Worktree isolation (`--worktree [<name>]`)
|
|
425
442
|
|
|
426
443
|
When a batch command (`dev-runall`, `dev-refineall`, `dev-verifyall`) is invoked with
|
|
@@ -671,14 +688,52 @@ git checkout "$BASE_REF"
|
|
|
671
688
|
[ "$(git rev-list --count "$BASE_SHA..$BRANCH")" -gt 0 ] \
|
|
672
689
|
|| { echo "halt: branch carries no commits - nothing to merge" >&2; false; } # -> WT-5
|
|
673
690
|
git merge --ff-only "$BRANCH" # FF-only: never rebase, merge-commit, or resolve conflicts
|
|
674
|
-
# if FF succeeded:
|
|
691
|
+
# if FF succeeded — WT-4a evidence persistence (Step 5, task 0720 R3) runs FIRST:
|
|
692
|
+
# persist the batch report + verdict artifacts into the invoking tree's .spur/run/
|
|
693
|
+
# before anything below touches the worktree. Persistence failure routes to WT-5.
|
|
694
|
+
#
|
|
695
|
+
# WT-4b — bounded CWD-holder cleanup (task 0720 R1). Resolve the EXACT absolute
|
|
696
|
+
# worktree path; a relative path or a stale entry matches the wrong processes.
|
|
697
|
+
WT_PATH="$(cd "../<worktree-dir>" && pwd)"
|
|
698
|
+
# Holders = processes with any open fd under the worktree tree (lsof +D walks the
|
|
699
|
+
# tree; CWD holders are the common case but +D also catches open-file holders —
|
|
700
|
+
# over-match errs toward removal success; a plain -t <dir> matches only the
|
|
701
|
+
# directory itself). Orphaned `serve` proof daemons (PPID 1) are exactly this
|
|
702
|
+
# class: they defeat `git worktree remove` (ENOTEMPTY), defeat rm -rf, while
|
|
703
|
+
# `git worktree prune` still deregisters the tree. Note +D is a full-tree walk,
|
|
704
|
+
# so the wait loop below bounds ITERATIONS (6 × 1s ticks + walk cost), not
|
|
705
|
+
# wall-clock.
|
|
706
|
+
HOLDERS="$(lsof -t +D "$WT_PATH" 2>/dev/null | sort -u)"
|
|
707
|
+
if [ -n "$HOLDERS" ]; then
|
|
708
|
+
kill -TERM $HOLDERS 2>/dev/null # 1) TERM first, all holders (unquoted — word-split PID list)
|
|
709
|
+
for _ in 1 2 3 4 5 6; do # 2) bounded wait: 6 × 1s ticks
|
|
710
|
+
sleep 1
|
|
711
|
+
[ -z "$(lsof -t +D "$WT_PATH" 2>/dev/null)" ] && break
|
|
712
|
+
done
|
|
713
|
+
SURVIVORS="$(lsof -t +D "$WT_PATH" 2>/dev/null | sort -u)"
|
|
714
|
+
if [ -n "$SURVIVORS" ]; then
|
|
715
|
+
kill -KILL $SURVIVORS 2>/dev/null # 3) KILL only the survivors (unquoted — one arg per PID)
|
|
716
|
+
sleep 1
|
|
717
|
+
fi
|
|
718
|
+
fi
|
|
719
|
+
# 4) Re-query: only an EMPTY holder set may proceed to remove/prune/branch delete.
|
|
720
|
+
FINAL="$(lsof -t +D "$WT_PATH" 2>/dev/null | sort -u)"
|
|
721
|
+
if [ -n "$FINAL" ]; then
|
|
722
|
+
PORTS="$(lsof -nP -a -p "$(echo "$FINAL" | paste -sd, -)" -iTCP -sTCP:LISTEN 2>/dev/null \
|
|
723
|
+
| awk 'NR>1 {print $9}' | sort -u | paste -sd' ' -)"
|
|
724
|
+
echo "halt: worktree still held by PID(s): $FINAL ${PORTS:+listening: $PORTS}" >&2
|
|
725
|
+
exit 1 # -> WT-5: retain worktree + branch,
|
|
726
|
+
fi # NO prune/remove/branch delete
|
|
675
727
|
git worktree remove "../<worktree-dir>"
|
|
676
728
|
git branch -d "$BRANCH"
|
|
677
729
|
# update marker: status = "merged"
|
|
678
730
|
```
|
|
679
731
|
|
|
680
|
-
On
|
|
681
|
-
|
|
732
|
+
On either guard firing — zero-commit branch (task 0701 R1), or surviving CWD holders (task 0720 R1)
|
|
733
|
+
— fall through to **WT-5**: the worktree and branch are retained, never removed. While any holder
|
|
734
|
+
remains, do **not** run `git worktree prune`, `git worktree remove`, or branch deletion. The holder
|
|
735
|
+
halt report names every surviving PID; the listening port is best-effort — a CWD holder may own no
|
|
736
|
+
socket, and `lsof` port discovery failing must not hide the PIDs.
|
|
682
737
|
|
|
683
738
|
#### Reuse mode — merge, retain
|
|
684
739
|
|
|
@@ -711,14 +766,28 @@ risk losing work); WT-5 retains the worktree and branch whenever FF is impossibl
|
|
|
711
766
|
Reuse mode is **narrower** than the carve-out (it merges but does not delete the branch), so the
|
|
712
767
|
carve-out text needs no widening.
|
|
713
768
|
|
|
714
|
-
**Lifecycle-DB disposition (task 0701 R2d).** The worktree has its own `.spur`
|
|
715
|
-
WT-4/WT-5 remove or retain that tree — the DB state does **not** travel with the
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
769
|
+
**Lifecycle-DB disposition (task 0701 R2d, amended by 0720).** The worktree has its own `.spur`
|
|
770
|
+
lifecycle DB, and WT-4/WT-5 remove or retain that tree — the DB state does **not** travel with the
|
|
771
|
+
merge. One contract, no alternatives:
|
|
772
|
+
|
|
773
|
+
- **Committed task files own lifecycle state.** The **committed task file is authoritative**: after
|
|
774
|
+
a green merge the branch's task files already read `done`/`testing` in the invoking tree; no
|
|
775
|
+
`spur task update` or `spur task record` replay runs post-merge. Replay is not "one of two
|
|
776
|
+
options" — it is removed: it writes `updated_at`-only churn and can never restore worktree-only
|
|
777
|
+
DB rows.
|
|
778
|
+
- **The persisted invoking-tree artifacts own evidence.** The Step 5 batch report at
|
|
779
|
+
`.spur/run/worktree-<marker-id>-batch-report.md` and the copied verdict JSONs under
|
|
780
|
+
`.spur/run/worktree-<marker-id>-verdicts/` (written before WT-4 removal) are the batch/verdict
|
|
781
|
+
record.
|
|
782
|
+
- **Per-worktree lifecycle DB rows intentionally do not travel.** No `task_run_links` import, no
|
|
783
|
+
cross-database provenance synthesis — the DB is per-tree by design.
|
|
784
|
+
- **No timestamp-only corpus churn.** Post-merge the invoking tree's DB statuses may read stale
|
|
785
|
+
relative to the committed files; that divergence is accepted, not repaired. Do not run
|
|
786
|
+
`task update`/`task record` to "catch up" the DB, and do not repair churn with
|
|
787
|
+
`git checkout -- docs/tasks*/`.
|
|
788
|
+
|
|
789
|
+
This is a deliberate choice over auto-migrating DB state: the committed corpus files are the durable
|
|
790
|
+
record and the persisted run artifacts are the evidence record.
|
|
722
791
|
|
|
723
792
|
### WT-5 — Failure path: retain and report (R5)
|
|
724
793
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: inline-pipeline-driver
|
|
3
|
-
description: "Interactive host-session interpreter for
|
|
3
|
+
description: "Interactive host-session interpreter for Spur state-machine pipelines: execute the existing FSM without a workflow agent subprocess while preserving actions, guards, artifacts, and provenance."
|
|
4
4
|
see_also:
|
|
5
5
|
- spur-dev
|
|
6
6
|
- execution-workflow
|
|
@@ -9,12 +9,13 @@ see_also:
|
|
|
9
9
|
|
|
10
10
|
# Inline Pipeline Driver
|
|
11
11
|
|
|
12
|
-
This driver is the interactive control-inversion path granted by ADR-047. It applies
|
|
13
|
-
interactive `/sp:dev-run --mode full
|
|
14
|
-
|
|
15
|
-
batch mode, `spur workflow run`, and `spur agent run` keep the existing
|
|
12
|
+
This driver is the interactive control-inversion path granted by ADR-047. It applies when an
|
|
13
|
+
interactive `/sp:dev-run --mode full`, sequential `/sp:dev-runall`, `/sp:dev-idea`, or
|
|
14
|
+
`/sp:dev-plan` invocation omits `--agent` or passes `--agent inline`. A named executor,
|
|
15
|
+
`--agent auto`, parallel batch mode, `spur workflow run`, and `spur agent run` keep the existing
|
|
16
|
+
subprocess path.
|
|
16
17
|
|
|
17
|
-
The project runtime definition — `task-pipeline.yaml`, resolved through the two-tier
|
|
18
|
+
The selected project runtime definition — `task-pipeline.yaml` or `idea-pipeline.yaml`, resolved through the two-tier
|
|
18
19
|
project→bundled model (task 0648/0650, never an unbundled runtime path) — remains the sole
|
|
19
20
|
FSM definition. The driver MUST read that file
|
|
20
21
|
at invocation time. It must not copy the state list, actions, guards, or transition order into a
|
|
@@ -22,9 +23,9 @@ command, skill, script, or second workflow.
|
|
|
22
23
|
|
|
23
24
|
## Run setup
|
|
24
25
|
|
|
25
|
-
1. Resolve
|
|
26
|
-
with those invocation values. An explicit
|
|
27
|
-
|
|
26
|
+
1. Resolve the command inputs, `--auto`, and any explicit `--vars`; read the selected YAML and overlay
|
|
27
|
+
its `vars` defaults with those invocation values. An explicit non-inline executor selection
|
|
28
|
+
chooses the subprocess workflow path.
|
|
28
29
|
2. Allocate a collision-resistant inline run id (`uuidgen`, with a timestamp/pid fallback), create
|
|
29
30
|
`.spur/run/`, and use `.spur/run/<run-id>.log` as the run log.
|
|
30
31
|
3. Resolve the host session id from `.spur/context/.session.json`, accepting the normalized hook key
|
|
@@ -42,13 +43,14 @@ command, skill, script, or second workflow.
|
|
|
42
43
|
never per action.
|
|
43
44
|
- **Source of truth** = the CLI projection for layer 1; the YAML parsed in step 1 for layer 2.
|
|
44
45
|
Never hand-copy or hand-derive the state list into the driver, a command, a skill, or a script.
|
|
45
|
-
5.
|
|
46
|
+
5. For task execution only, record lifecycle provenance before entering the FSM:
|
|
46
47
|
|
|
47
48
|
```bash
|
|
48
49
|
spur task run-link <wbs> --source inline-full --run-id <run-id> --json
|
|
49
50
|
```
|
|
50
51
|
|
|
51
|
-
This is required for the normal `testing → done` provenance guard.
|
|
52
|
+
This is required for the normal `testing → done` provenance guard. Planning pipelines have no
|
|
53
|
+
task lifecycle link and skip this task-specific action.
|
|
52
54
|
|
|
53
55
|
## YAML interpreter
|
|
54
56
|
|
|
@@ -61,12 +63,15 @@ Action semantics come from the YAML and the workflow action contract:
|
|
|
61
63
|
- `shell` — run the expanded command in the project working tree with resolved vars exported as
|
|
62
64
|
environment variables. A non-zero result follows the action's existing failure policy.
|
|
63
65
|
- `note` — append the expanded message to the inline run log.
|
|
66
|
+
- `doctor.probe` — run the declared Spur doctor once, persist its status file, and apply any
|
|
67
|
+
`setVars` result (including a resolved executor) before the next action or state.
|
|
64
68
|
- `file.read.into-var` — read the declared file into the declared run variable before subsequent
|
|
65
69
|
actions/guards.
|
|
66
70
|
- `hitl.confirm` — under `profile=auto`, follow the YAML's auto-skip transition. Otherwise pause,
|
|
67
71
|
surface the prompt, and resume from the same state with the operator's answer.
|
|
68
|
-
- `agent.run` — execute the action's
|
|
69
|
-
|
|
72
|
+
- `agent.run` — execute the action's input in the host session. Task execution may use the native
|
|
73
|
+
subagent eligibility below; idea/plan never dispatch a native subagent unless the operator
|
|
74
|
+
explicitly requested delegation. Do not call `spur agent run` or re-enter a full pipeline. Preserve the YAML options: capture
|
|
70
75
|
`answerFile`; assert `expectFile`; enforce `requireDiff` against a pre-action git snapshot,
|
|
71
76
|
including the task-scope guard; honor declared error policy. `timeoutMs` is recorded as not
|
|
72
77
|
applicable because the host session has no independent kill boundary.
|