@gobing-ai/spur 0.3.90 → 0.3.91
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/pipeline-budgets.json +7 -0
- package/config/workflows/decision-routing-example.yaml +134 -0
- package/config/workflows/idea-pipeline.yaml +8 -0
- package/config/workflows/task-pipeline.yaml +2 -0
- package/config/workflows/wayfinder-resolution.yaml +2 -0
- package/config/workflows/wrapup-pipeline.yaml +2 -0
- package/package.json +9 -9
- package/plugins/sp/README.md +1 -0
- package/plugins/sp/agents/expert-spur.md +2 -2
- package/plugins/sp/lib/idea-handoff.generated.mjs +2 -2
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/inline-pipeline-parity-check.ts +1 -0
- package/plugins/sp/scripts/surface-drift-inventory.ts +71 -6
- package/plugins/sp/scripts/validate-flag-contracts.ts +3 -3
- package/plugins/sp/skills/spur-cli/references/agent.md +11 -2
- package/plugins/sp/skills/spur-cli/references/self.md +5 -1
- package/plugins/sp/skills/spur-cli/references/workflows.md +3 -1
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +21 -2
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +13 -4
- package/plugins/sp/skills/spur-dev/references/glossary.md +9 -1
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +9 -1
- package/plugins/sp/skills/wayfinder/SKILL.md +1 -1
- package/spur.js +2737 -891
- package/web/_astro/{BoardApp.CDUcHlTJ.js → BoardApp.BEDWpzsr.js} +82 -82
- package/web/_astro/BoardApp.DQG2xfEz.js +1 -0
- package/web/_astro/{TaskDetail.DwTmbQp5.js → TaskDetail.CXGltuT_.js} +1 -1
- package/web/_astro/{arc.BzF71EFI.js → arc.C0rrflm_.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.jvdDahWM.js → architectureDiagram-3BPJPVTR.DJ8DHkWE.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.zSg4AmFD.js → blockDiagram-GPEHLZMM.D8DHK3Jl.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.BkUIUQWH.js → c4Diagram-AAUBKEIU.BugQbX9u.js} +1 -1
- package/web/_astro/channel.SRrg1P-w.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.DvfQ_f50.js → chunk-2J33WTMH.Mv26KlVn.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.DuI4gQqX.js → chunk-4BX2VUAB.CD51JoT_.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.D3BWBOpF.js → chunk-55IACEB6.D_PFaIEe.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.3QSi0a9M.js → chunk-727SXJPM.DemMW1ao.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.xazCQrAF.js → chunk-AQP2D5EJ.Iu2V5-ex.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.B2g6u4rA.js → chunk-FMBD7UC4.MsNgSP-E.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.wWwWs99t.js → chunk-ND2GUHAM.DfbRaAlm.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.BD5g3qa9.js → chunk-QZHKN3VN.BbJAQ4h-.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.C7CzCdsX.js → classDiagram-4FO5ZUOK.CPurtiC2.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.C7CzCdsX.js → classDiagram-v2-Q7XG4LA2.CPurtiC2.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.Xyiau0gw.js → cose-bilkent-S5V4N54A.CKKdx1bM.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.BeC5MWas.js → cynefin-OW5HDTMX.CoKMTg-R.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.yZbMN9vc.js → dagre-BM42HDAG.D4h4_k56.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.Cmo2zQM-.js → diagram-2AECGRRQ.DJ0h9zgw.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.D033eSVi.js → diagram-5GNKFQAL.DvPk1jYd.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.CR6k3Y3G.js → diagram-KO2AKTUF.BFoCkiCr.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.x7mwu8jz.js → diagram-LMA3HP47.exHn9OVx.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.D8aTTvUr.js → diagram-OG6HWLK6.CeqO34nN.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.BoBqcKXQ.js → erDiagram-TEJ5UH35.D_v7HqxR.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.D3mTQdrU.js → flowDiagram-I6XJVG4X.EUmrpbwh.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.H-cqgIh-.js → ganttDiagram-6RSMTGT7.BOCF5lII.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.B6s9zbfC.js → gitGraphDiagram-PVQCEYII.Di7otYZD.js} +1 -1
- package/web/_astro/index.Bx6GY4RH.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.BzgCoV6P.js → infoDiagram-5YYISTIA.TcBkCAJk.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.BZzVhy1-.js → ishikawaDiagram-YF4QCWOH.D-2y4M0c.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.BV3195Py.js → journeyDiagram-JHISSGLW.DPbJI_n2.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.BjRd2DWz.js → kanban-definition-UN3LZRKU.EFxhQ9Fj.js} +1 -1
- package/web/_astro/{linear.BILTgS5N.js → linear.DSAsQLzs.js} +1 -1
- package/web/_astro/{mermaid.core.DBy_WKeW.js → mermaid.core.kAZjgJHG.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.BiEjaI4-.js → mindmap-definition-RKZ34NQL.CJY1N_7V.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.i_8V5pIn.js → pieDiagram-4H26LBE5.567ZNoL2.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.BWaW3MHn.js → quadrantDiagram-W4KKPZXB.mqfz9-MY.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.CzddBbtg.js → requirementDiagram-4Y6WPE33.Bv1Gv9In.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.X2ww0e-D.js → sankeyDiagram-5OEKKPKP.B6Gs4X4r.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.DSA4kTcc.js → sequenceDiagram-3UESZ5HK.BhYj4v-m.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.D0DtFSpR.js → stateDiagram-AJRCARHV.BPbBnkpw.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.BfQq0zQv.js → stateDiagram-v2-BHNVJYJU.C4squMNK.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.Dmlrgi1m.js → timeline-definition-PNZ67QCA.C_SwIHgl.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.D5mpl00Z.js → vennDiagram-CIIHVFJN.Bz4NZGpQ.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.Df4BdzO4.js → wardleyDiagram-YWT4CUSO.CozMVZ3i.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.DiTRreKN.js → xychartDiagram-2RQKCTM6.BwMGBwjB.js} +1 -1
- package/web/index.html +2 -2
- package/web/_astro/BoardApp.CaCGU_uX.js +0 -1
- package/web/_astro/channel.SSVY0JPQ.js +0 -1
- package/web/_astro/index.CcU5weKX.css +0 -1
|
@@ -346,6 +346,67 @@ export function checkNounVerbFlags(
|
|
|
346
346
|
}
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
+
// ─── Semantic operand layer (I7 / task 0906 R2) ─────────────────────────────
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Metadata-only operands the I6 sweep classified. This is the audit boundary, not a
|
|
353
|
+
* CLI section matrix: a key that is ALSO a genuine body heading for the noun (feature
|
|
354
|
+
* `Scope`) is accepted per-noun in checkSectionOperand; anything outside this set is
|
|
355
|
+
* not statically classifiable and stays unverified.
|
|
356
|
+
*/
|
|
357
|
+
const SECTION_METADATA_KEYS = new Set([
|
|
358
|
+
'tags',
|
|
359
|
+
'priority',
|
|
360
|
+
'status',
|
|
361
|
+
'phase',
|
|
362
|
+
'id',
|
|
363
|
+
'parent',
|
|
364
|
+
'name',
|
|
365
|
+
'owner',
|
|
366
|
+
'scope',
|
|
367
|
+
]);
|
|
368
|
+
|
|
369
|
+
/** Body headings that overlap the swept metadata keys, per noun (case-insensitive). */
|
|
370
|
+
const NOUN_BODY_SECTIONS: Record<string, ReadonlySet<string>> = {
|
|
371
|
+
feature: new Set(['scope']),
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Semantic operand check (I7 / task 0906 R2): `--section` is a real flag on
|
|
376
|
+
* task/feature update, so existence parity passes while the operand names a
|
|
377
|
+
* metadata-only key instead of a body section. Classify literal operands only —
|
|
378
|
+
* dynamic operands (placeholders) stay unverified under the existing scanner
|
|
379
|
+
* convention, and quoted/equal forms are handled alongside the spaced form.
|
|
380
|
+
* Comparison is case-normalized; the original operand text stays in the row evidence.
|
|
381
|
+
*/
|
|
382
|
+
export function checkSectionOperand(spanRaw: string, occ: { file: string; line: number }): void {
|
|
383
|
+
const parsed = parseInvocation(spanRaw);
|
|
384
|
+
if (!parsed) return;
|
|
385
|
+
const noun = parsed.nouns[0];
|
|
386
|
+
if ((noun !== 'task' && noun !== 'feature') || !parsed.verbs.includes('update')) return;
|
|
387
|
+
for (const m of spanRaw.matchAll(/--section(?:=|\s+)("([^"]*)"|'([^']*)'|[^\s]+)/g)) {
|
|
388
|
+
const operand = (m[2] ?? m[3] ?? m[1] ?? '').replace(/[.,;:]+$/, '');
|
|
389
|
+
if (!operand || PLACEHOLDER.test(operand)) continue; // dynamic — unverified by convention
|
|
390
|
+
const key = operand.toLowerCase();
|
|
391
|
+
if ((NOUN_BODY_SECTIONS[noun] ?? new Set()).has(key)) continue; // genuine body heading
|
|
392
|
+
if (!SECTION_METADATA_KEYS.has(key)) continue; // outside the audit boundary — unverified
|
|
393
|
+
// P3 (0906 review): feature update owns the generic --field/--value pair; task update
|
|
394
|
+
// exposes metadata only via dedicated flags (no --field/--value, no --tags), so the hint
|
|
395
|
+
// must not suggest a pair that would fail with VALIDATION_FAILED on task rows.
|
|
396
|
+
const remediation =
|
|
397
|
+
noun === 'feature'
|
|
398
|
+
? `use --field ${operand} --value <value>`
|
|
399
|
+
: `task update exposes metadata only via dedicated flags (e.g. --priority <value>), not --section`;
|
|
400
|
+
record(
|
|
401
|
+
`spur ${noun} update --section ${operand}`,
|
|
402
|
+
'semantic-operand(I6 metadata keys vs noun body sections)',
|
|
403
|
+
'mismatch',
|
|
404
|
+
`--section writes a body section; "${operand}" is a metadata-only ${noun} operand — ${remediation}`,
|
|
405
|
+
occ,
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
349
410
|
export function sweepPluginTrees(root: string = PLUGIN_ROOT): void {
|
|
350
411
|
const files = [
|
|
351
412
|
...walk(join(root, 'commands'), ['.md']),
|
|
@@ -398,6 +459,7 @@ export function sweepPluginTrees(root: string = PLUGIN_ROOT): void {
|
|
|
398
459
|
continue;
|
|
399
460
|
}
|
|
400
461
|
checkNounVerbFlags(parsed.nouns, parsed.verbs, parsed.flags, occ);
|
|
462
|
+
checkSectionOperand(span, occ);
|
|
401
463
|
}
|
|
402
464
|
if (file.endsWith('.ts')) return;
|
|
403
465
|
if (inVerbTable && refNoun && /^\|/.test(line)) {
|
|
@@ -430,7 +492,10 @@ export function sweepPluginTrees(root: string = PLUGIN_ROOT): void {
|
|
|
430
492
|
} else {
|
|
431
493
|
for (const span of lineInvocationSpans(line)) {
|
|
432
494
|
const parsed = parseInvocation(span);
|
|
433
|
-
if (parsed)
|
|
495
|
+
if (parsed) {
|
|
496
|
+
checkNounVerbFlags(parsed.nouns, parsed.verbs, parsed.flags, occ);
|
|
497
|
+
checkSectionOperand(span, occ);
|
|
498
|
+
}
|
|
434
499
|
}
|
|
435
500
|
}
|
|
436
501
|
});
|
|
@@ -764,11 +829,11 @@ export function sweepWorkflows(opts: { run?: CliRunner; wfDir?: string; link?: s
|
|
|
764
829
|
.forEach((line: string, i: number) => {
|
|
765
830
|
for (const span of lineInvocationSpans(line)) {
|
|
766
831
|
const parsed = parseInvocation(span);
|
|
767
|
-
if (parsed)
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
832
|
+
if (parsed) {
|
|
833
|
+
const occ = { file: path, line: i + 1 };
|
|
834
|
+
checkNounVerbFlags(parsed.nouns, parsed.verbs, parsed.flags, occ);
|
|
835
|
+
checkSectionOperand(span, occ);
|
|
836
|
+
}
|
|
772
837
|
}
|
|
773
838
|
});
|
|
774
839
|
}
|
|
@@ -406,9 +406,9 @@ export function extractTriggerTable(crossCuttingRaw: string): string[] | null {
|
|
|
406
406
|
function adrAgentClaims(adrRaw: string): Map<string, SurfaceBehavior> | null {
|
|
407
407
|
if (adrRaw.includes('## ADR-047')) {
|
|
408
408
|
const out = new Map<string, SurfaceBehavior>();
|
|
409
|
-
//
|
|
410
|
-
//
|
|
411
|
-
//
|
|
409
|
+
// ADR-087 (task 0687) retired the G5 frozen rejection: explicit `inline` on a headless
|
|
410
|
+
// surface resolves via role/tier substitution with one warning. This claims map mirrors
|
|
411
|
+
// the ADR-047 amendment text as written; ADR-087-aware parsing is a follow-up.
|
|
412
412
|
out.set('inline', {
|
|
413
413
|
surfaces: new Set(['inline']),
|
|
414
414
|
conditional: false,
|
|
@@ -231,8 +231,17 @@ to executors via `agent.executors[].agent` (or the model's `<provider>/` prefix)
|
|
|
231
231
|
write path. A provider is exhausted when any `primary|secondary|tertiary` window reports
|
|
232
232
|
`usedPercent >= 100`; the window name and `resetsAt` go into the observation reason. Per-provider
|
|
233
233
|
`{ "error": … }` entries are skipped (listed, never treated as recovery); healthy entries still
|
|
234
|
-
apply.
|
|
235
|
-
|
|
234
|
+
apply. Providers whose windows are all null carry no signal (`no-usage`): they are reported and
|
|
235
|
+
excluded from availability decisions — an absent signal neither disables nor recovers, and an
|
|
236
|
+
exhausted signal wins on shared executors. Operator-owned availability (`disabled: true` or an
|
|
237
|
+
operator ownership object) is never touched. A missing codexbar binary or an unparsable payload
|
|
238
|
+
exits `1` and changes nothing. Unmapped providers are listed and never guessed.
|
|
239
|
+
|
|
240
|
+
Each reported change carries a delivery-semantics `action` (0907): `would-apply` (dry run),
|
|
241
|
+
`applied` — the exact observation created by the invocation was acknowledged without a skip
|
|
242
|
+
(desired state confirmed; not proof of a YAML byte change), `no-op` — already satisfied or
|
|
243
|
+
operator-owned, `skipped` — the observation was superseded or rejected, `pending` — delivery
|
|
244
|
+
unconfirmed or failed. For `skipped`/`pending` the printed target is intent only.
|
|
236
245
|
|
|
237
246
|
**Scheduling is external** (cron/launchd, same pattern as `spur history daily`):
|
|
238
247
|
|
|
@@ -90,7 +90,11 @@ spur self status --json # machine-readable
|
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
Reports the project's Spur configuration state (init status, feature/task counts, rule preset
|
|
93
|
-
health)
|
|
93
|
+
health), git working-tree status, and DecisionMaker readiness (0911): the human output adds a
|
|
94
|
+
`DecisionMaker:` line and `--json` exposes `decisionMaker: {enabled, provider,
|
|
95
|
+
credentialPresent, state, connectivity, inlineSupport}` with states `disabled` / `missing-key` /
|
|
96
|
+
`configured-not-probed`. Presence check only — never a live probe, never echoes the key.
|
|
97
|
+
Optional `[path]` argument targets a different project
|
|
94
98
|
directory. Only flag is `--json`.
|
|
95
99
|
|
|
96
100
|
## What this skill is NOT
|
|
@@ -80,7 +80,9 @@ Use this skill to:
|
|
|
80
80
|
- **Author a workflow** — turn a described process into a validated, dry-run-verified YAML definition
|
|
81
81
|
in the right mode. → authoring-workflows.md
|
|
82
82
|
- **Validate before trusting** — schema + semantic-check a workflow file (references, terminal
|
|
83
|
-
reachability, template vars
|
|
83
|
+
reachability, template vars, and since 0911 the HITL decision policy: `decision` only on
|
|
84
|
+
`hitl.confirm`/`hitl.select`, evidence mode banned in `pause: true` states/nodes, one evidence
|
|
85
|
+
action per state/node, existing producer nodes, valid select choices) before running it.
|
|
84
86
|
- **Run a workflow** — execute a definition and read its run trace (states/nodes entered, transitions
|
|
85
87
|
taken, terminal status).
|
|
86
88
|
- **Refine an existing workflow** — fix a stuck guard, add a state/node, retune `iterationBound`,
|
|
@@ -61,6 +61,18 @@ The explicit-rejection carve-out above was superseded by ADR-087 (task 0687): a
|
|
|
61
61
|
substitutes tier resolution with a warning instead of rejecting — see the substitution blockquote
|
|
62
62
|
above and `resolveAgent` in `packages/app/src/services/agent-service.ts`.
|
|
63
63
|
|
|
64
|
+
### Run-scoped session policy (B7, summary)
|
|
65
|
+
|
|
66
|
+
When an inline resolution dispatches a native subagent, or a workflow `agent.run` executes, the
|
|
67
|
+
run-scoped session policy applies: coder stages default to `session: reuse`; reviewer, planner,
|
|
68
|
+
and scribe stages default to `fresh`; a reviewer stage may declare `session: reuse` explicitly.
|
|
69
|
+
A runner record without resume capability (`supportsResumeById: false`) forces a fresh dispatch,
|
|
70
|
+
and a disabled pinned executor re-resolves once, then starts fresh. Run traces record the session
|
|
71
|
+
provenance (`reused | fresh`). This is a summary only: the owning contract is
|
|
72
|
+
[session-pinned-dispatch.md §4](../../../../../docs/design/session-pinned-dispatch.md) with role
|
|
73
|
+
semantics in [roles.md](../../../references/roles.md); this section does not restate that
|
|
74
|
+
contract.
|
|
75
|
+
|
|
64
76
|
### Objective triggers override the answer
|
|
65
77
|
|
|
66
78
|
The one rule resolves operator *intent*. A trigger is a detected *requirement* the chosen executor
|
|
@@ -188,8 +200,15 @@ Do not read provider auth or quota from `spur agent doctor`. The doctor resolves
|
|
|
188
200
|
config), so it historically degraded to `status: usable · auth: no · model: unknown` for GLM-style
|
|
189
201
|
executors and was useless as a preflight gate. Feature B4 removed the auth signal from the surface
|
|
190
202
|
entirely (no column, no `authenticated` in `--json`) precisely so nothing can read it by mistake;
|
|
191
|
-
the precheck probe classifies on usability alone.
|
|
192
|
-
|
|
203
|
+
the precheck probe classifies on usability alone. Preflight availability does exist — as a
|
|
204
|
+
separate, owned surface (session-pinned-dispatch.md §3.4): `spur agent usage` captures provider
|
|
205
|
+
quota windows into a durable snapshot, the availability drain derives `quota.exhausted` /
|
|
206
|
+
`quota.recovered` observations from it, and `spur agent doctor` renders the provenance (`owner`,
|
|
207
|
+
`since`, `reason`, snapshot `age`). A snapshot older than `agent.usage.maxAgeMs` renders `stale`
|
|
208
|
+
and never enables an executor. Usability (doctor) is not authentication, and neither is a live
|
|
209
|
+
quota guarantee — the signals stay distinct. Mid-run exhaustion is still detected by the
|
|
210
|
+
escalation classifier; the preflight surface informs planning, it does not replace the in-run
|
|
211
|
+
detector.
|
|
193
212
|
|
|
194
213
|
### Explicit subprocess surfaces are unchanged
|
|
195
214
|
|
|
@@ -260,7 +260,10 @@ an executor failed — the executor is swappable via config, the pipeline is not
|
|
|
260
260
|
**When an `agent.run` step fails (timeout, non-zero exit, empty output):**
|
|
261
261
|
|
|
262
262
|
1. **Diagnose, don't bypass.** Check `spur agent doctor <executor>` — is the agent
|
|
263
|
-
installed
|
|
263
|
+
installed and usable? Doctor reports usability plus availability provenance (`owner`,
|
|
264
|
+
`since`, `reason`, snapshot `age`); it is read-only and a stale availability snapshot never
|
|
265
|
+
enables an executor — `spur agent usage` is the preflight availability signal. Then check
|
|
266
|
+
`.spur/config.yaml` → which role/executor does the
|
|
264
267
|
run resolve to (`agent.default` role → stage-registry tier ladder)? Which model does that
|
|
265
268
|
executor use? Could that model be out of tokens, rate-limited, or deprecated?
|
|
266
269
|
2. **Switch executors, don't abandon the pipeline.** Override the agent for the run:
|
|
@@ -275,12 +278,18 @@ an executor failed — the executor is swappable via config, the pipeline is not
|
|
|
275
278
|
Manual section fills outside either driver are indistinguishable from pipeline output and bypass
|
|
276
279
|
the provenance contract silently.
|
|
277
280
|
|
|
278
|
-
**Known diagnostic gap:** `spur agent doctor` checks installation, version, and
|
|
279
|
-
|
|
280
|
-
|
|
281
|
+
**Known diagnostic gap:** `spur agent doctor` checks installation, version, and usability
|
|
282
|
+
(read-only, with availability provenance from `spur agent usage`; a stale snapshot never
|
|
283
|
+
enables) — it cannot detect mid-run token quota exhaustion, model deprecation, or rate limits.
|
|
284
|
+
An executor configured with `agent: omp` + `model: <provider/model>` passes doctor if `omp` is
|
|
281
285
|
installed, even if the model is unavailable. If an `agent.run` times out with no useful
|
|
282
286
|
diagnostic, suspect the model, not the agent binary.
|
|
283
287
|
|
|
288
|
+
Executor switching also interacts with run-scoped session policy (coder `reuse`,
|
|
289
|
+
reviewer/planner/scribe `fresh`): see the session-policy summary in
|
|
290
|
+
[cross-cutting.md#inline-default-execution-surface](cross-cutting.md#inline-default-execution-surface);
|
|
291
|
+
this file does not restate that contract.
|
|
292
|
+
|
|
284
293
|
## Large tasks and timed-out implement resume (task 0424)
|
|
285
294
|
|
|
286
295
|
Two obligations when driving a task that may not fit one implement pass.
|
|
@@ -72,10 +72,18 @@ reserved for this specific planning/execution split).
|
|
|
72
72
|
**HITL** (human-in-the-loop) — a workflow state that pauses for explicit operator approval
|
|
73
73
|
before continuing (`hitl.confirm`). A HITL gate is never auto-dismissed by the engine; `--auto`
|
|
74
74
|
can only route *around* one whose objective precondition is already met (see the `--auto`
|
|
75
|
-
routing contract in `cross-cutting.md`).
|
|
75
|
+
routing contract in `cross-cutting.md`). A `decision` option (0911) pins that action's policy:
|
|
76
|
+
`mode: never` forces the human responder, `mode: evidence` lets the optional DecisionMaker answer
|
|
77
|
+
only from verified prior action evidence (deferring otherwise), and absence keeps the 0910
|
|
78
|
+
implicit behavior when `workflow.hitlDecisionMaker` is enabled.
|
|
76
79
|
Avoid: *prompt* (reserved for LLM input text), *interrupt* (implies an exception, not a planned
|
|
77
80
|
pause point).
|
|
78
81
|
|
|
82
|
+
**DecisionMaker** — optional provider-backed responder for executed `hitl.confirm`/`hitl.select`
|
|
83
|
+
actions (ADR-123), enabled via `workflow.hitlDecisionMaker: true` and `TYPESAFE_API_KEY`.
|
|
84
|
+
Evidence answers clear the answer variable and record `statusVar: accepted/deferred`; unresolved
|
|
85
|
+
cases defer or delegate to the human. Offline readiness: `spur self status`.
|
|
86
|
+
|
|
79
87
|
**WBS** (work-breakdown-structure ID) — the four-digit task identifier (e.g. `0187`) that
|
|
80
88
|
names a task file and its position in the corpus. WBS IDs are assigned once and never reused.
|
|
81
89
|
Avoid: *task ID* alone (acceptable in prose, but *WBS* is the canonical term when precision
|
|
@@ -23,7 +23,7 @@ the resolved actions and guards of every `.spur/workflows/*.yaml`; any element p
|
|
|
23
23
|
in one and absent in the other fails the check. Add a new kind here when the driver
|
|
24
24
|
implements it; remove the entry when the corresponding kind is dropped from the YAML.
|
|
25
25
|
|
|
26
|
-
**Actions:** `shell` · `note` · `doctor.probe` · `file.read.into-var` · `hitl.confirm` · `agent.run` · `proof.fingerprint` · `run.artifact` · `command.gate`
|
|
26
|
+
**Actions:** `shell` · `note` · `doctor.probe` · `file.read.into-var` · `hitl.confirm` · `hitl.select` · `agent.run` · `proof.fingerprint` · `run.artifact` · `command.gate`
|
|
27
27
|
|
|
28
28
|
**Guards (transitions):** `always` · `shell` · `action-ok` · `contract-violation`
|
|
29
29
|
|
|
@@ -45,6 +45,14 @@ FSM definition. The driver MUST read that file
|
|
|
45
45
|
at invocation time. It must not copy the state list, actions, guards, or transition order into a
|
|
46
46
|
command, skill, script, or second workflow.
|
|
47
47
|
|
|
48
|
+
**Inline HITL defer semantics (0911):** bundled pipeline human gates ship `decision: {mode:
|
|
49
|
+
never}`, so the inline driver always prompts the operator for those gates regardless of
|
|
50
|
+
`workflow.hitlDecisionMaker` — the decision policy never answers inline pipeline gates. A local
|
|
51
|
+
workflow override (`.spur/workflows/*.yaml`) may declare `mode: evidence`, but the driver does
|
|
52
|
+
not claim full inline parity for the DecisionMaker path: evidence-mode auto-answering inside the
|
|
53
|
+
inline driver is an explicit non-goal (subprocess runs own that behavior). No hot reload: config
|
|
54
|
+
and YAML changes require restarting the invoking process.
|
|
55
|
+
|
|
48
56
|
## Run setup
|
|
49
57
|
|
|
50
58
|
**Shared startup contract (task 0814 R1/R3/R4/R6/R7).** The order is load-bearing: publish a compact
|
|
@@ -120,7 +120,7 @@ Invoked when the operator has a loose idea and the destination itself is foggy.
|
|
|
120
120
|
- **## Decisions so far** — empty on creation; populated as questions and tickets resolve (one line each: the decision, or WBS + title + one-line gist)
|
|
121
121
|
- **### Not yet specified** — the fog of war: in-scope questions you can sense but can't yet phrase sharply enough to ticket. Nest under `## Notes`.
|
|
122
122
|
- **### Out of scope** — work consciously ruled beyond this destination. Nest under `## Notes`.
|
|
123
|
-
- **Tag the feature as a wayfinder map.** `spur feature update <id> --
|
|
123
|
+
- **Tag the feature as a wayfinder map.** `spur feature update <id> --field tags --value wayfinder-map`. The `wayfinder-map` tag tells `feature check` to skip BDD AC validation — maps deliberately carry a prose no-AC disclaimer instead of Gherkin scenarios, so without the tag the checker flags a false error.
|
|
124
124
|
4. **Create child tasks only for executable investigations.** `spur task create "<title>" --feature <feature-id>` for each sharp question **an implementer can answer without the operator** — research, prototype, inventory, measurement. Anything needing the operator's judgment goes to **## Open questions** instead. Ticket types (see below) determine which skill resolves the tasks.
|
|
125
125
|
5. **Wire blocking edges.** After all tickets exist (they need IDs before they can reference each other), set dependencies via `spur task update`. Wiring sorts tickets into the frontier (open, unblocked, unclaimed) and the blocked.
|
|
126
126
|
6. **Populate the fog.** Everything you can't yet specify stays in **### Not yet specified** — sketch it as loosely or as fully as the view allows. Don't pre-slice fog into ticket-sized pieces; one patch may graduate into several tickets, or none.
|