@gobing-ai/spur 0.3.41 → 0.3.43
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 +15 -0
- package/README.md +15 -1
- package/config/templates/AGENTS.md +5 -3
- package/config/workflows/task-pipeline.yaml +113 -12
- package/package.json +15 -12
- package/plugins/README.md +655 -0
- package/plugins/sp/README.md +621 -0
- package/plugins/sp/agents/expert-spur.md +109 -0
- package/plugins/sp/agents/super-coder.md +126 -0
- package/plugins/sp/agents/super-planner.md +283 -0
- package/plugins/sp/agents/super-reviewer.md +154 -0
- package/plugins/sp/commands/dev-arch.md +30 -0
- package/plugins/sp/commands/dev-brainstorm.md +40 -0
- package/plugins/sp/commands/dev-changelog.md +28 -0
- package/plugins/sp/commands/dev-daily.md +29 -0
- package/plugins/sp/commands/dev-debug.md +29 -0
- package/plugins/sp/commands/dev-dogfood.md +33 -0
- package/plugins/sp/commands/dev-featurechange.md +130 -0
- package/plugins/sp/commands/dev-find-conflict.md +51 -0
- package/plugins/sp/commands/dev-find-issue.md +76 -0
- package/plugins/sp/commands/dev-find-next.md +60 -0
- package/plugins/sp/commands/dev-fixall.md +33 -0
- package/plugins/sp/commands/dev-gitmsg.md +28 -0
- package/plugins/sp/commands/dev-gtd.md +97 -0
- package/plugins/sp/commands/dev-handover.md +26 -0
- package/plugins/sp/commands/dev-idea.md +50 -0
- package/plugins/sp/commands/dev-next.md +31 -0
- package/plugins/sp/commands/dev-parallel.md +30 -0
- package/plugins/sp/commands/dev-plan.md +46 -0
- package/plugins/sp/commands/dev-refine.md +48 -0
- package/plugins/sp/commands/dev-refineall.md +65 -0
- package/plugins/sp/commands/dev-refresh.md +32 -0
- package/plugins/sp/commands/dev-reverse.md +32 -0
- package/plugins/sp/commands/dev-review.md +31 -0
- package/plugins/sp/commands/dev-run.md +63 -0
- package/plugins/sp/commands/dev-runall.md +83 -0
- package/plugins/sp/commands/dev-simplify.md +39 -0
- package/plugins/sp/commands/dev-unit.md +29 -0
- package/plugins/sp/commands/dev-verify.md +48 -0
- package/plugins/sp/commands/dev-verifyall.md +72 -0
- package/plugins/sp/commands/dev-wrap.md +48 -0
- package/plugins/sp/commands/dev-wrapall.md +51 -0
- package/plugins/sp/commands/rule-add.md +18 -0
- package/plugins/sp/commands/rule-refine.md +18 -0
- package/plugins/sp/commands/rule-scan.md +18 -0
- package/plugins/sp/commands/spur-init.md +22 -0
- package/plugins/sp/commands/workflow-add.md +18 -0
- package/plugins/sp/commands/workflow-refine.md +18 -0
- package/plugins/sp/hooks/agent-hint.ts +60 -0
- package/plugins/sp/hooks/careful-guard.ts +147 -0
- package/plugins/sp/hooks/context-post-tool.ts +325 -0
- package/plugins/sp/hooks/context-session-start.ts +182 -0
- package/plugins/sp/hooks/context-session-stop.ts +95 -0
- package/plugins/sp/hooks/hooks.json +51 -0
- package/plugins/sp/hooks/pi/guard-extension.ts +341 -0
- package/plugins/sp/hooks/task-write-guard.ts +84 -0
- package/plugins/sp/plugin.json +8 -0
- package/plugins/sp/scripts/batch-preflight.ts +204 -0
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +606 -0
- package/plugins/sp/scripts/daily-summary/logger.ts +28 -0
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +363 -0
- package/plugins/sp/scripts/dogfood-testing/validate-report.ts +168 -0
- package/plugins/sp/scripts/feature-sync-bounded.ts +451 -0
- package/plugins/sp/scripts/stage-registry-adapter.ts +1341 -0
- package/plugins/sp/scripts/task-size-precheck.ts +202 -0
- package/plugins/sp/scripts/validate-commands.ts +651 -0
- package/plugins/sp/scripts/validate-flag-contracts.ts +875 -0
- package/plugins/sp/skills/brainstorm/SKILL.md +361 -0
- package/plugins/sp/skills/brainstorm/agents/openai.yaml +14 -0
- package/plugins/sp/skills/brainstorm/examples/ideation-example.md +209 -0
- package/plugins/sp/skills/brainstorm/metadata.openclaw +7 -0
- package/plugins/sp/skills/brainstorm/references/grilling-interview.md +68 -0
- package/plugins/sp/skills/brainstorm/references/workflows.md +255 -0
- package/plugins/sp/skills/branch-workflow/SKILL.md +109 -0
- package/plugins/sp/skills/branch-workflow/references/branch-lifecycle.md +69 -0
- package/plugins/sp/skills/branch-workflow/references/worktree-patterns.md +100 -0
- package/plugins/sp/skills/code-implementation/SKILL.md +203 -0
- package/plugins/sp/skills/code-implementation/references/debugging.md +64 -0
- package/plugins/sp/skills/code-implementation/references/implementation-patterns.md +113 -0
- package/plugins/sp/skills/code-improvement/SKILL.md +231 -0
- package/plugins/sp/skills/code-improvement/references/deepening-signals.md +205 -0
- package/plugins/sp/skills/code-review/SKILL.md +112 -0
- package/plugins/sp/skills/code-review/references/receiving-code-review.md +70 -0
- package/plugins/sp/skills/code-review/references/review-lenses.md +117 -0
- package/plugins/sp/skills/code-review/references/self-review-checklist.md +53 -0
- package/plugins/sp/skills/code-review/references/verification-before-completion.md +68 -0
- package/plugins/sp/skills/code-simplification/SKILL.md +225 -0
- package/plugins/sp/skills/code-testing/SKILL.md +108 -0
- package/plugins/sp/skills/code-testing/references/stacks/bun-ts.md +165 -0
- package/plugins/sp/skills/code-testing/references/stacks/go.md +88 -0
- package/plugins/sp/skills/code-testing/references/stacks/python.md +117 -0
- package/plugins/sp/skills/code-testing/references/test-loop-breaker.md +37 -0
- package/plugins/sp/skills/code-testing/references/test-output-discipline.md +52 -0
- package/plugins/sp/skills/code-testing/references/unit-testing.md +212 -0
- package/plugins/sp/skills/code-verification/SKILL.md +535 -0
- package/plugins/sp/skills/code-verification/references/code-improvement.md +39 -0
- package/plugins/sp/skills/code-verification/references/secu-review.md +94 -0
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +141 -0
- package/plugins/sp/skills/conflict-finding/SKILL.md +254 -0
- package/plugins/sp/skills/conflict-finding/references/authority-resolution.md +258 -0
- package/plugins/sp/skills/conflict-finding/references/comparison-protocol.md +277 -0
- package/plugins/sp/skills/conflict-finding/references/finding-contract.md +346 -0
- package/plugins/sp/skills/conflict-finding/references/remediation-routing.md +276 -0
- package/plugins/sp/skills/daily-summary/SKILL.md +171 -0
- package/plugins/sp/skills/daily-summary/agents/openai.yaml +13 -0
- package/plugins/sp/skills/doc-evolve/SKILL.md +175 -0
- package/plugins/sp/skills/doc-evolve/references/operations.md +93 -0
- package/plugins/sp/skills/dogfood-testing/SKILL.md +608 -0
- package/plugins/sp/skills/dogfood-testing/references/monitor-ledger.md +211 -0
- package/plugins/sp/skills/dogfood-testing/references/report-template.md +351 -0
- package/plugins/sp/skills/doubt-driven-development/SKILL.md +136 -0
- package/plugins/sp/skills/functional-review/SKILL.md +364 -0
- package/plugins/sp/skills/functional-review/references/verdict-schema.md +133 -0
- package/plugins/sp/skills/indexed-context/SKILL.md +164 -0
- package/plugins/sp/skills/issue-finding/SKILL.md +442 -0
- package/plugins/sp/skills/issue-finding/agents/openai.yaml +15 -0
- package/plugins/sp/skills/issue-finding/examples/expected-findings.json +27 -0
- package/plugins/sp/skills/issue-finding/examples/session-test-loop.jsonl +23 -0
- package/plugins/sp/skills/issue-finding/references/session-formats.md +151 -0
- package/plugins/sp/skills/next-feature/SKILL.md +125 -0
- package/plugins/sp/skills/next-feature/references/handoff-routing.md +92 -0
- package/plugins/sp/skills/next-feature/references/proposal-contract.md +49 -0
- package/plugins/sp/skills/next-feature/references/ranking-rubric.md +52 -0
- package/plugins/sp/skills/next-feature/references/signal-derivation.md +67 -0
- package/plugins/sp/skills/next-router/SKILL.md +181 -0
- package/plugins/sp/skills/next-router/references/messages.md +90 -0
- package/plugins/sp/skills/next-router/references/routing-table.md +216 -0
- package/plugins/sp/skills/parallel-execution/SKILL.md +148 -0
- package/plugins/sp/skills/parallel-execution/references/dispatch-surface.md +109 -0
- package/plugins/sp/skills/parallel-execution/references/fan-out-patterns.md +101 -0
- package/plugins/sp/skills/parallel-execution/references/result-synthesis.md +94 -0
- package/plugins/sp/skills/reverse-engineering/SKILL.md +375 -0
- package/plugins/sp/skills/reverse-engineering/agents/openai.yaml +18 -0
- package/plugins/sp/skills/source-driven-development/SKILL.md +124 -0
- package/plugins/sp/skills/spec-decomposition/SKILL.md +117 -0
- package/plugins/sp/skills/spec-decomposition/references/decomposition.md +539 -0
- package/plugins/sp/skills/spur-cli/SKILL.md +133 -0
- package/plugins/sp/skills/spur-cli/references/agent.md +179 -0
- package/plugins/sp/skills/spur-cli/references/features/acceptance-criteria.md +82 -0
- package/plugins/sp/skills/spur-cli/references/features/hierarchy-mece.md +177 -0
- package/plugins/sp/skills/spur-cli/references/features/roadmap-priority.md +84 -0
- package/plugins/sp/skills/spur-cli/references/features/verbs.md +122 -0
- package/plugins/sp/skills/spur-cli/references/features.md +234 -0
- package/plugins/sp/skills/spur-cli/references/init.md +119 -0
- package/plugins/sp/skills/spur-cli/references/message.md +98 -0
- package/plugins/sp/skills/spur-cli/references/rules/authoring-rules.md +212 -0
- package/plugins/sp/skills/spur-cli/references/rules/fine-tuning.md +148 -0
- package/plugins/sp/skills/spur-cli/references/rules/operations.md +228 -0
- package/plugins/sp/skills/spur-cli/references/rules/validation-and-extension.md +129 -0
- package/plugins/sp/skills/spur-cli/references/rules.md +209 -0
- package/plugins/sp/skills/spur-cli/references/serve.md +58 -0
- package/plugins/sp/skills/spur-cli/references/tasks/l3-guard-cheatsheet.md +98 -0
- package/plugins/sp/skills/spur-cli/references/tasks/section-editing.md +95 -0
- package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +361 -0
- package/plugins/sp/skills/spur-cli/references/tasks.md +280 -0
- package/plugins/sp/skills/spur-cli/references/team.md +133 -0
- package/plugins/sp/skills/spur-cli/references/workflows/authoring-workflows.md +226 -0
- package/plugins/sp/skills/spur-cli/references/workflows/operations.md +211 -0
- package/plugins/sp/skills/spur-cli/references/workflows/validation-and-extension.md +129 -0
- package/plugins/sp/skills/spur-cli/references/workflows.md +355 -0
- package/plugins/sp/skills/spur-dev/SKILL.md +224 -0
- package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +196 -0
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +680 -0
- package/plugins/sp/skills/spur-dev/references/decision-brief.md +80 -0
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +546 -0
- package/plugins/sp/skills/spur-dev/references/done-housekeeping.md +164 -0
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +790 -0
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +352 -0
- package/plugins/sp/skills/spur-dev/references/feature-link-helper.md +191 -0
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +417 -0
- package/plugins/sp/skills/spur-dev/references/gate-checklists.md +171 -0
- package/plugins/sp/skills/spur-dev/references/glossary.md +95 -0
- package/plugins/sp/skills/spur-dev/references/idea-evaluation.md +80 -0
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +120 -0
- package/plugins/sp/skills/spur-dev/references/planning-workflow.md +302 -0
- package/plugins/sp/skills/spur-dev/references/product-planning.md +206 -0
- package/plugins/sp/skills/spur-dev/references/section-batching.md +24 -0
- package/plugins/sp/skills/sys-architecture/SKILL.md +155 -0
- package/plugins/sp/skills/sys-architecture/references/decision-method.md +137 -0
- package/plugins/sp/skills/sys-architecture/references/upkeep-survey.md +75 -0
- package/plugins/sp/skills/sys-debugging/SKILL.md +228 -0
- package/plugins/sp/skills/sys-debugging/references/debugging-protocol.md +62 -0
- package/plugins/sp/skills/test-driven-development/SKILL.md +248 -0
- package/plugins/sp/skills/wayfinder/SKILL.md +292 -0
- package/spur.js +6035 -4963
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: validation-and-extension
|
|
3
|
+
description: Validate workflow definitions and extend the engine — validate semantics, custom action and guard runners, the trust-gated extension loader, and CLI-vs-library capability gaps.
|
|
4
|
+
see_also:
|
|
5
|
+
- spur-cli
|
|
6
|
+
- operations
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Validation & Extension
|
|
10
|
+
|
|
11
|
+
Two concerns: trusting a definition (`validate`) and growing the engine's vocabulary (custom
|
|
12
|
+
actions/guards and extension modules).
|
|
13
|
+
|
|
14
|
+
## Validation
|
|
15
|
+
|
|
16
|
+
`spur workflow validate <file> [--no-schema] --json` runs two layers:
|
|
17
|
+
|
|
18
|
+
1. **Structural schema** — the state-machine or transition-flow JSON schema. Catches missing required
|
|
19
|
+
keys, wrong types, an unquoted `$schema`, or flow intent (`nodes`/`edges`) parsed against the
|
|
20
|
+
state-machine schema because `kind: transition-flow` was omitted.
|
|
21
|
+
2. **Semantic invariants** — beyond what the schema can express: every `transition`/`edge` `from`/`to`
|
|
22
|
+
references a declared state/node; terminal states/nodes are reachable; `${vars.x}` has a matching
|
|
23
|
+
`vars` entry; `${env.X}` is listed in `env.allow`.
|
|
24
|
+
|
|
25
|
+
`--no-schema` skips only the `$schema` ref resolution (useful offline or for inline definitions),
|
|
26
|
+
keeping the structural + semantic checks. Classify a failure before fixing:
|
|
27
|
+
|
|
28
|
+
| Symptom | Layer | Typical cause | Fix |
|
|
29
|
+
| ------- | ----- | ------------- | --- |
|
|
30
|
+
| `parse error` near `$schema` | schema | unquoted `@`-leading value | quote the `$schema` string |
|
|
31
|
+
| validates as state-machine but you wrote nodes/edges | schema | missing `kind: transition-flow` | set `kind` explicitly |
|
|
32
|
+
| `unknown state/node` | semantic | a transition/edge target doesn't exist | fix the `to`/`from` id |
|
|
33
|
+
| terminal unreachable | semantic | no path to a `terminalStates`/`terminalNodes` entry | add the missing transition/edge |
|
|
34
|
+
| `${env.X}` empty at runtime | semantic-ish | `X` not in `env.allow` | add it to `env.allow` |
|
|
35
|
+
|
|
36
|
+
The error classes the library throws map to these: `WorkflowValidationError` (schema/semantic/template),
|
|
37
|
+
`FSMError` (runtime driver — missing state/node, invalid target), `RunCollisionError` (duplicate
|
|
38
|
+
`runId`). Definition errors throw; *run* failures (a failing action/guard) come back as
|
|
39
|
+
`status: 'failed'` and do not throw.
|
|
40
|
+
|
|
41
|
+
## Custom actions and guards
|
|
42
|
+
|
|
43
|
+
When the built-ins (`note`, `shell`, `always`, `action-ok`) are not enough, register domain runners on
|
|
44
|
+
the host. An action's `kind` string is how a workflow references it.
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
import { WorkflowEngineHost } from '@gobing-ai/ts-dual-workflow-engine';
|
|
48
|
+
|
|
49
|
+
const host = new WorkflowEngineHost();
|
|
50
|
+
|
|
51
|
+
// Custom action — receives resolved options + run context, returns an ActionResult
|
|
52
|
+
host.registerAction({
|
|
53
|
+
kind: 'send-email',
|
|
54
|
+
async execute(options, context) {
|
|
55
|
+
await mailer.send(String(options.to), String(options.subject));
|
|
56
|
+
return { ok: true }; // { ok: false, error } fails the action; { ok: true, terminal: true } ends the run
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Custom guard — returns a boolean
|
|
61
|
+
host.registerGuard({
|
|
62
|
+
kind: 'isBusinessHours',
|
|
63
|
+
async evaluate() {
|
|
64
|
+
const hour = new Date().getHours();
|
|
65
|
+
return hour >= 9 && hour < 17;
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Once registered, any definition can use `kind: send-email` (in `onEnter`/`onExit`/`node.action`) or
|
|
71
|
+
`kind: isBusinessHours` (in a `guard`/`condition`). The host tracks each registration's origin
|
|
72
|
+
(`builtin` / `extension` / `core`) — query with `host.actionOrigin(kind)` / `host.guardOrigin(kind)`.
|
|
73
|
+
|
|
74
|
+
> Inline `registerAction`/`registerGuard` is **not** gated — it is in-process code the caller already
|
|
75
|
+
> controls. Only the *module loader* (next section) is trust-gated.
|
|
76
|
+
|
|
77
|
+
## Extension loading (trust-gated)
|
|
78
|
+
|
|
79
|
+
For modules that bundle multiple actions/guards, use `loadWorkflowExtensionsIntoHost`. The trust gate
|
|
80
|
+
is **fail-closed**: `allowExtensions` defaults to `false`, and a declared-but-not-allowed extension
|
|
81
|
+
**throws before any import** — never silently dropped.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import { loadWorkflowExtensionsIntoHost, WorkflowEngineHost } from '@gobing-ai/ts-dual-workflow-engine';
|
|
85
|
+
|
|
86
|
+
const host = new WorkflowEngineHost();
|
|
87
|
+
|
|
88
|
+
await loadWorkflowExtensionsIntoHost(
|
|
89
|
+
host,
|
|
90
|
+
[{ kind: 'actions', absPath: '/abs/path/to/extension.ts', sourceName: 'my-config' }],
|
|
91
|
+
{
|
|
92
|
+
allowExtensions: true, // required — disabled by default
|
|
93
|
+
moduleLoader: (absPath) => import(absPath), // caller owns loading; tests use a stub
|
|
94
|
+
logger: { warn: (msg) => console.warn(msg) },// optional override warnings
|
|
95
|
+
},
|
|
96
|
+
);
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
An extension module default-exports `{ name, actions?: [...], guards?: [...] }`. A ref with
|
|
100
|
+
`kind: 'actions'` registers only the module's `actions[]` (guards in the same module are ignored, and
|
|
101
|
+
vice versa). Security properties:
|
|
102
|
+
|
|
103
|
+
- `allowExtensions` is fail-closed — refs present without the flag throw before import.
|
|
104
|
+
- Extension paths are validated; `..` traversal is rejected.
|
|
105
|
+
- The caller supplies `moduleLoader`; the loader has no ambient code-loading capability of its own.
|
|
106
|
+
|
|
107
|
+
**Extensions execute arbitrary code.** Treat an extension path like a dependency: only load modules you
|
|
108
|
+
trust, and never enable `allowExtensions` for paths derived from untrusted input.
|
|
109
|
+
|
|
110
|
+
## CLI vs. library capability gaps
|
|
111
|
+
|
|
112
|
+
The `spur workflow` CLI surfaces the lifecycle verbs (`validate`, `run`, `list`, `trace`, `continue`,
|
|
113
|
+
`cancel`, `clean`) over the default-host engine. Several engine capabilities are **library-only** —
|
|
114
|
+
reach for the library (`@gobing-ai/ts-dual-workflow-engine`) directly when you need them:
|
|
115
|
+
|
|
116
|
+
| Capability | CLI | Library |
|
|
117
|
+
| ---------- | --- | ------- |
|
|
118
|
+
| Validate / run / list definitions | ✅ | ✅ |
|
|
119
|
+
| Trace run history / continue HITL / cancel / clean orphans | ✅ | ✅ |
|
|
120
|
+
| Custom action/guard runners | ✅ (built-ins only) | ✅ (`registerAction`/`registerGuard`) |
|
|
121
|
+
| Extension modules | — | ✅ (`loadWorkflowExtensionsIntoHost`) |
|
|
122
|
+
| DB persistence + programmatic `listRuns()` | (via configured adapter + `trace`) | ✅ (`DbWorkflowPersistenceAdapter`) |
|
|
123
|
+
| Event-bus observability (progress bars, dashboards) | partial (CLI step reporter on sync human runs) | ✅ (`WorkflowEngineEvents` via `WorkflowRunOptions.events`) |
|
|
124
|
+
| OTel traces / structured logs | (emitted) | ✅ (`RunLifecycle`) |
|
|
125
|
+
| Programmatic schema parsing | — | ✅ (`WorkflowDefSchema`, `validateWorkflowDef`) |
|
|
126
|
+
|
|
127
|
+
When a workflow needs a capability the CLI doesn't expose, that is a signal to build a small host-side
|
|
128
|
+
integration in the consuming app (registering the runner, wiring the adapter/event bus), not to
|
|
129
|
+
work around it in the YAML. Keep domain behavior in action/guard runners, not in workflow parsing.
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spur-cli-workflows
|
|
3
|
+
description: "spur-cli noun reference: operate `spur workflow` across its full lifecycle — choose the right execution mode, author state-machine and transition-flow workflows, validate definitions, run them, read run traces, and refine existing flows. Wraps the dual-mode FSM/transition-flow runtime (`@gobing-ai/ts-dual-workflow-engine`)."
|
|
4
|
+
see_also:
|
|
5
|
+
- spur-cli
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# spur workflow — the dual-mode workflow CLI
|
|
9
|
+
|
|
10
|
+
`spur workflow` runs declarative YAML workflows (powered by `@gobing-ai/ts-dual-workflow-engine`) that
|
|
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 the first act of
|
|
13
|
+
any new workflow is choosing between them. Get the mode right and the rest of authoring follows the
|
|
14
|
+
schema; get it wrong and you fight the engine.
|
|
15
|
+
|
|
16
|
+
Operating a workflow well is a full lifecycle — choose the mode, author the YAML, validate it, run it,
|
|
17
|
+
read the trace, and refine. This skill covers all of it.
|
|
18
|
+
|
|
19
|
+
## Choose the execution mode first
|
|
20
|
+
|
|
21
|
+
This is the defining decision and has no analogue in `spur rule`. **Every new workflow starts here.**
|
|
22
|
+
The engine runs two kinds; they use different schemas, different keys, and a different mental model.
|
|
23
|
+
Switching kinds later is a rewrite, not an edit — so decide deliberately and, in `add`, confirm.
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
Is the run defined by ONE "current state" that picks its next state by evaluating
|
|
27
|
+
ordered guards, and may loop back on itself (e.g. implement → check → fix → check …)?
|
|
28
|
+
│ yes │ no
|
|
29
|
+
▼ ▼
|
|
30
|
+
state-machine Does the run advance through a DAG of nodes,
|
|
31
|
+
(initialState / states[] each node carrying an action, following edges
|
|
32
|
+
with onEnter|onExit / forward — fan-out, gates, decisions — and
|
|
33
|
+
top-level transitions[] rarely looping (e.g. read → validate → write)?
|
|
34
|
+
with guard) │ yes
|
|
35
|
+
▼
|
|
36
|
+
transition-flow
|
|
37
|
+
(initialNode / nodes[] typed
|
|
38
|
+
action|gate|parallel|decision with action /
|
|
39
|
+
edges[] with condition)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Discriminator table** (lifted from the two JSON schemas — authoritative, not heuristic):
|
|
43
|
+
|
|
44
|
+
| Question | → `state-machine` | → `transition-flow` |
|
|
45
|
+
| -------- | ----------------- | ------------------- |
|
|
46
|
+
| Mental model | One current state; guard-driven next-state | DAG of nodes advancing along edges |
|
|
47
|
+
| Where actions live | On states: `onEnter` / `onExit` | On nodes: `node.action` |
|
|
48
|
+
| Branching | Ordered `transitions[]` + `guard` (first pass wins) | `edges[]` + `condition` (first pass wins); unconditional edges allowed |
|
|
49
|
+
| Loops / retries | **Natural** — a transition points back to a prior state | Possible but DAG-oriented; loops are awkward |
|
|
50
|
+
| Node typing | none (states are untyped) | `type: action\|gate\|parallel\|decision` |
|
|
51
|
+
| Required keys | `name, initialState, states, transitions` | `kind, name, initialNode, nodes, edges` |
|
|
52
|
+
| `kind` field | optional (defaults to state-machine) | **required** — `kind: transition-flow` |
|
|
53
|
+
| Canonical example | implement→check→fix loop (`.spur/workflows/basic.yaml` — soft status-file probe + bounded fixall) | read→validate→transform→write pipeline |
|
|
54
|
+
|
|
55
|
+
**Heuristic:** loops / retries / one-active-state → **state-machine**; pipeline / fan-out / action-per-node → **transition-flow**.
|
|
56
|
+
|
|
57
|
+
When intent is ambiguous, default to **state-machine** (the simpler, default kind) and say so — but
|
|
58
|
+
in `add`, surface the recommendation **with its reason and the rejected alternative**, and confirm
|
|
59
|
+
before authoring. Full procedure: [workflows/authoring-workflows.md](workflows/authoring-workflows.md).
|
|
60
|
+
|
|
61
|
+
## When to use
|
|
62
|
+
|
|
63
|
+
Use this skill to:
|
|
64
|
+
|
|
65
|
+
- **Author a workflow** — turn a described process into a validated, dry-run-verified YAML definition
|
|
66
|
+
in the right mode. → authoring-workflows.md
|
|
67
|
+
- **Validate before trusting** — schema + semantic-check a workflow file (references, terminal
|
|
68
|
+
reachability, template vars) before running it.
|
|
69
|
+
- **Run a workflow** — execute a definition and read its run trace (states/nodes entered, transitions
|
|
70
|
+
taken, terminal status).
|
|
71
|
+
- **Refine an existing workflow** — fix a stuck guard, add a state/node, retune `iterationBound`,
|
|
72
|
+
re-scope variables / `env.allow`, with the smallest change. → workflows/operations.md
|
|
73
|
+
- **Extend the engine** — register a custom action/guard runner or a trust-gated extension module when
|
|
74
|
+
the built-ins (`note`, `shell`, `always`, `action-ok`) fall short. → workflows/validation-and-extension.md
|
|
75
|
+
|
|
76
|
+
## Operations
|
|
77
|
+
|
|
78
|
+
The skill's logic divides by **whether the LLM adds value**:
|
|
79
|
+
|
|
80
|
+
- **Direct CLI** (`validate`, `run`, `list`, `trace`, `continue`, `cancel`, `clean`) — deterministic,
|
|
81
|
+
single-verb commands. Run them straight. A slash-command wrapper would only forward flags and add
|
|
82
|
+
drift; **there is no command for these — use the CLI**. The skill still drives them in natural
|
|
83
|
+
language (interpreting a failed validate, reading a run trace).
|
|
84
|
+
- **Agent-driven** (`add`, `refine`) — convert fuzzy human intent into a reliable sequence the CLI
|
|
85
|
+
cannot express as one verb. `add` chooses the mode and authors a new workflow; `refine` tunes an
|
|
86
|
+
existing one. These are the operations worth a slash command, and the skill owns all their logic.
|
|
87
|
+
Full procedures: [workflows/operations.md](workflows/operations.md).
|
|
88
|
+
|
|
89
|
+
| Operation | Backed by | Input | Output (done-when) |
|
|
90
|
+
| --------- | --------- | ----- | ------------------ |
|
|
91
|
+
| `validate` | `spur workflow validate` (CLI) | `<file> [--no-schema]` | Schema + semantic verdict |
|
|
92
|
+
| `run` | `spur workflow run` (CLI) | `<file> [--run-id <id>] [--vars <json>] [--dry-run] [--async] [--no-plan] [--quiet/--silent/--verbose] [--detail <level>] [--trace-file] [--no-log] [--steer]` | Terminal state reached (sync) or run started (async); trace readable |
|
|
93
|
+
| `continue` | `spur workflow continue` (CLI) | `[run-id] [--yes] [--answer <yes\|no\|cancel>]` | Resume a paused HITL run (omit id -> most recent paused); `--answer` injects a gate answer before guard re-evaluation |
|
|
94
|
+
| `cancel` | `spur workflow cancel` (CLI) | `<run-id>` | Single non-terminal run marked failed (SIGTERM async worker when live) |
|
|
95
|
+
| `clean` | `spur workflow clean` (CLI) | `[--older-than <min>] [--force] [--logs] [--dry-run]` | Bulk-finalize stale `running`/`pending` runs as failed **and** reclaim retained run logs older than `workflow.logRetentionDays` (30d default) |
|
|
96
|
+
| `list` | `spur workflow list` (CLI) | — | Available workflow **YAML definition files** (not run records) |
|
|
97
|
+
| `trace` | `spur workflow trace` (CLI) | `[run-id] [--workflow <n>] [--status <s>] [--since <iso>] [--last <n>] [--follow] [--poll <ms>] [--output]` | Run history list or per-run timeline |
|
|
98
|
+
| `add` | agent procedure | `"<nl-description>" [--kind <state-machine\|transition-flow>] [--file <path>]` | **Mode chosen (confirmed)** → first reconciled against existing workflows (extend an existing flow rather than duplicate) → YAML authored in real schema shape → **validated AND dry-run** (reaches the expected terminal state) → [add](workflows/operations.md#add) |
|
|
99
|
+
| `refine` | agent procedure | `<workflow-file> [--intent "<goal>"] [--dry-run]` | Smallest change meeting the intent, re-validated and re-dry-run; `--dry-run` emits a diff only → [refine](workflows/operations.md#refine) |
|
|
100
|
+
|
|
101
|
+
`add` and `refine` are not CLI verbs. They compose `validate` + `run` around a generated/edited YAML
|
|
102
|
+
definition and both end in the same **validate-and-dry-run** core
|
|
103
|
+
([operations.md](workflows/operations.md#sub-procedure-validate-and-dry-run)) so a tuned workflow is
|
|
104
|
+
verified exactly like an authored one. They also share the **find-existing-workflow** core
|
|
105
|
+
([operations.md](workflows/operations.md#sub-procedure-find-existing-workflow)): `add` runs it up front
|
|
106
|
+
(don't duplicate an existing flow), `refine` runs it to locate the target. `add` additionally runs the
|
|
107
|
+
**mode-selection gate** before authoring — the one human-in-the-loop step unique to workflows.
|
|
108
|
+
|
|
109
|
+
## The harness loop (choose → author → validate → dry-run → read trace)
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
choose mode (decision tree above)
|
|
113
|
+
│
|
|
114
|
+
▼
|
|
115
|
+
author YAML — real schema shape for that mode; quote $schema
|
|
116
|
+
│
|
|
117
|
+
▼
|
|
118
|
+
spur workflow validate <file> --json ← schema + semantic gate
|
|
119
|
+
│
|
|
120
|
+
valid? ──no──▶ read errors → fix the definition → re-validate
|
|
121
|
+
│ yes
|
|
122
|
+
▼
|
|
123
|
+
spur workflow run <file> --dry-run --run-id <throwaway> --json ← dry-run
|
|
124
|
+
│
|
|
125
|
+
status authoritative for pass/fail? ──no──▶ read trace,
|
|
126
|
+
(done = success; failed = failure) fix the offending
|
|
127
|
+
│ yes AND finalState === <expected> state/node/guard, re-run
|
|
128
|
+
▼
|
|
129
|
+
trust the workflow
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Reader contract (0425):** `status` alone is authoritative for pass/fail. A run that lands in a
|
|
133
|
+
declared `failureStates` terminal reports `status: 'failed'` (CLI exit non-zero) even when
|
|
134
|
+
`finalState` is a named terminal like `failed` or `cancelled`. Do **not** treat
|
|
135
|
+
`status === 'done' && finalState === 'failed'` as a success — that was the pre-0425 silent-success
|
|
136
|
+
hazard. Use `finalState` only to identify *which* terminal was reached after `status` has been
|
|
137
|
+
checked. Absent `failureStates`, every terminal still finalizes as `done` (backward compatible).
|
|
138
|
+
|
|
139
|
+
Two signals, two purposes: `validate` proves the definition is *well-formed and self-consistent*;
|
|
140
|
+
`run --dry-run` proves it *walks* — reaching the intended terminal state along the intended path
|
|
141
|
+
without executing side-effecting actions. A workflow you have not dry-run is a workflow you do not
|
|
142
|
+
trust. Always use `--json` when an agent consumes the result.
|
|
143
|
+
|
|
144
|
+
### Step 1: Validate
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
spur workflow validate ./workflows/approval.yaml --json
|
|
148
|
+
spur workflow validate ./workflows/import.yaml --no-schema --json # skip the $schema ref, keep semantic checks
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`validate` runs the structural Zod schema **and** semantic invariants (referenced states/nodes exist,
|
|
152
|
+
terminal states reachable, template vars resolve). A non-zero exit means **not well-formed** — fix the
|
|
153
|
+
definition, not the runner.
|
|
154
|
+
|
|
155
|
+
### Step 2: Dry-run
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
spur workflow run ./workflows/approval.yaml --dry-run --run-id dryrun-$(date +%s) --json
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Prefer **`--dry-run`** so actions are not executed. Use a throwaway `--run-id` so the dry-run does
|
|
162
|
+
not collide with a real run (duplicate ids raise `RunCollisionError`). Read **`status` first**
|
|
163
|
+
(authoritative for pass/fail), then `finalState` (which terminal). A failed run returns
|
|
164
|
+
`status: 'failed'` in the result — it does **not** throw; read the trace
|
|
165
|
+
(`spur workflow trace <run-id>`) to find the offending step.
|
|
166
|
+
|
|
167
|
+
### Step 3: Read the trace and fix
|
|
168
|
+
|
|
169
|
+
Inspect which states/nodes were entered and which transitions/edges were taken. A run that stalls
|
|
170
|
+
short of the expected terminal state points at a guard/condition that never passed, a mistyped target,
|
|
171
|
+
or an `iterationBound` exhausted by a runaway loop. Fix the **specific** definition flaw — no drive-by
|
|
172
|
+
restructuring — then re-run. Loop until the run reaches the expected terminal state.
|
|
173
|
+
|
|
174
|
+
### Run output and observability flags
|
|
175
|
+
|
|
176
|
+
The `run` verb has seven output/observability flags beyond the core `--dry-run`/`--async`/`--no-plan`.
|
|
177
|
+
The six human-output flags are ignored under `--json` (machine output stays byte-identical);
|
|
178
|
+
`--no-log` applies regardless of output mode (it controls the side-effect log file, not the stdout shape):
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
spur workflow run ./workflows/approval.yaml --quiet # final summary only
|
|
182
|
+
spur workflow run ./workflows/approval.yaml --silent # errors only (non-zero exit on failure)
|
|
183
|
+
spur workflow run ./workflows/approval.yaml --verbose # transitions + correlation diagnostics
|
|
184
|
+
spur workflow run ./workflows/approval.yaml --detail minimal # tersest human output
|
|
185
|
+
spur workflow run ./workflows/approval.yaml --trace-file # persist redacted JSONL trace
|
|
186
|
+
spur workflow run ./workflows/approval.yaml --no-log # opt out of the consolidated .spur/run/<RUNID>.log
|
|
187
|
+
spur workflow run ./workflows/approval.yaml --steer # interactive steering on stdin
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
- **`--quiet`**, **`--silent`**, **`--verbose`** are mutually constraining: `--quiet` and `--verbose`
|
|
191
|
+
are exclusive (exit `2`); `--silent` cannot combine with either (exit `2`).
|
|
192
|
+
- **`--detail <level>`** sets human verbosity: `minimal` (state changes only), `invocation` (default;
|
|
193
|
+
per-step headers), `full` (transitions + correlation). `--verbose` is shorthand for `--detail full`.
|
|
194
|
+
- **`--trace-file`** appends a redacted, schema-versioned JSONL trace under `.spur/runs/workflow/`
|
|
195
|
+
for post-run analysis - independent of human/JSON output.
|
|
196
|
+
- **`--no-log`** opts out of writing the consolidated all-in-one run log (`.spur/run/<RUNID>.log`).
|
|
197
|
+
By default the log is written **and retained** after the run ends; this flag skips it entirely
|
|
198
|
+
(propagates to the `--async` detached worker). No `--keep-log` / delete-by-default exists.
|
|
199
|
+
- **`--steer`** is synchronous and in-process: it cannot combine with `--json` or `--async` (exit `2`).
|
|
200
|
+
It accepts steering commands on stdin at declared action boundaries for interactive control.
|
|
201
|
+
|
|
202
|
+
## Command surface
|
|
203
|
+
|
|
204
|
+
```
|
|
205
|
+
spur workflow validate <file> [--no-schema] [--json]
|
|
206
|
+
spur workflow run <file> [--run-id <id>] [--vars <json>] [--dry-run] [--async] [--no-plan] [--quiet/--silent/--verbose] [--detail <level>] [--trace-file] [--no-log] [--steer] [--json]
|
|
207
|
+
spur workflow continue [run-id] [--yes] [--answer <yes|no|cancel>] [--json]
|
|
208
|
+
spur workflow cancel <run-id> [--json]
|
|
209
|
+
spur workflow clean [--older-than <minutes>] [--force] [--logs] [--dry-run] [--json]
|
|
210
|
+
spur workflow list [--json]
|
|
211
|
+
spur workflow trace [run-id] [--workflow <name>] [--status <s>] [--since <iso>] [--last <n>] [--follow] [--poll <ms>] [--output] [--json]
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
| Flag (on `run`) | Effect |
|
|
215
|
+
| --------------- | ------ |
|
|
216
|
+
| `--vars <json>` | Per-run variable overrides (JSON object). Merged over the workflow's `vars`. Values must be strings. User vars win over injected defaults (`spurBin`). |
|
|
217
|
+
| `--dry-run` | Validate and walk transitions **without** executing actions. |
|
|
218
|
+
| `--async` | Start in the background and exit with `runId`; monitor via `spur workflow trace <run-id>`. |
|
|
219
|
+
| `--no-plan` | Suppress the human run-start plan preview (sync human runs only; ignored under `--json`/`--async`). |
|
|
220
|
+
| `--quiet` | Suppress plan and per-step progress; keep the final summary. |
|
|
221
|
+
| `--silent` | Suppress all routine output; errors still set a non-zero exit status. |
|
|
222
|
+
| `--verbose` | Include transitions and correlation diagnostics in human progress (implies `--detail full`). |
|
|
223
|
+
| `--detail <level>` | Human detail level: `minimal`, `invocation` (default), or `full`. |
|
|
224
|
+
| `--trace-file` | Append a redacted schema-versioned JSONL trace under `.spur/runs/workflow/`. |
|
|
225
|
+
| `--no-log` | Opt out of writing the consolidated `.spur/run/<RUNID>.log` (retained by default; propagates to `--async` workers). |
|
|
226
|
+
| `--steer` | Accept in-process steering commands on stdin at declared action boundaries (sync only; incompatible with `--json`/`--async`). |
|
|
227
|
+
|
|
228
|
+
`validate` and `run` exit non-zero on failure (`run` exits non-zero when the final status is not
|
|
229
|
+
`done`). `list` prints **workflow definition files** available on disk. For run history use `trace`:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
spur workflow list --json
|
|
233
|
+
spur workflow trace --last 10 --json
|
|
234
|
+
spur workflow trace <run-id> --json
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Follow a live run to terminal (human streaming mode):
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
spur workflow trace <run-id> --follow # stream until terminal (default 1000ms poll)
|
|
241
|
+
spur workflow trace <run-id> --follow --poll 500 # poll every 500ms
|
|
242
|
+
spur workflow trace <run-id> --follow --output # stream .spur/run/<RUNID>.log instead of the DB timeline
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
- **`--follow`** replays a run timeline and polls persisted state until it becomes terminal. It
|
|
246
|
+
requires a `run-id` (exit `2` without one) and cannot combine with `--json` (exit `2` - it is a
|
|
247
|
+
human streaming mode).
|
|
248
|
+
- **`--poll <ms>`** sets the follow polling interval (default `1000`, minimum `50`; exit `2` otherwise).
|
|
249
|
+
- **`--output`** swaps the follow source from the structured DB timeline to the consolidated all-in-one
|
|
250
|
+
log (`.spur/run/<RUNID>.log`, tail -f equivalent), streaming new lines as they land and exiting at
|
|
251
|
+
terminal status. It requires `--follow` and a `run-id`, is a human stream (rejects `--json`), and is
|
|
252
|
+
a **distinct source** — it never interleaves with the DB timeline. If the log never appears (e.g. the
|
|
253
|
+
run was started with `--no-log`), a clear message is printed at terminal status rather than hanging.
|
|
254
|
+
No `spur workflow monitor` verb exists; `--output` is the log-streaming surface.
|
|
255
|
+
|
|
256
|
+
HITL pause/resume: a run that hits a HITL action pauses; resume with `spur workflow continue [run-id]`
|
|
257
|
+
(`--yes` skips confirmation). A headless `hitl.confirm` persists a default `no` before pausing -
|
|
258
|
+
use `--answer yes|no|cancel` to inject the operator's gate answer before guard re-evaluation (0433).
|
|
259
|
+
`--answer` is distinct from `--yes`: `--yes` skips the CLI resume prompt, `--answer` sets the HITL
|
|
260
|
+
gate answer. Cancel one live/paused run with `cancel <run-id>`; bulk-finalize orphans stuck in
|
|
261
|
+
`running`/`pending` with `clean` (`--older-than` default 30 minutes, or `--force`).
|
|
262
|
+
|
|
263
|
+
**Schema resolution parity (0431):** `validate` and `run` both load the workflow through
|
|
264
|
+
`WorkflowAppService` with the same `embeddedSchemaOptions()` map the CLI injects for
|
|
265
|
+
`@gobing-ai/spur/schemas/...` refs. `run` pre-loads then calls the engine with the loaded def
|
|
266
|
+
(not bare `runFile` → node resolution). Schema errors under an embedded map never cite a
|
|
267
|
+
`node_modules` path. Prefer a project `agent.default` (or explicit `--vars '{"agent":…}'`) when
|
|
268
|
+
redirecting `agent.run` stages (ADR-047).
|
|
269
|
+
|
|
270
|
+
### `clean` — housekeeping scopes (0429)
|
|
271
|
+
|
|
272
|
+
`spur workflow clean` is the housekeeping one-liner: it runs **both** scopes unless scoped by flag.
|
|
273
|
+
|
|
274
|
+
- **Stale-run finalization** (existing): bulk-finalizes orphaned `running`/`pending` runs as `failed`.
|
|
275
|
+
`--older-than <minutes>` (default 30) and `--force` (all non-terminal regardless of age) apply here only.
|
|
276
|
+
- **Run-log reclamation** (0429): removes retained `.spur/run/<RUNID>.log` files whose mtime is older
|
|
277
|
+
than `workflow.logRetentionDays` in `.spur/config.yaml` (default 30 days; integer days, not minutes).
|
|
278
|
+
Age is the only gate; best-effort deletes never abort the rest. Never touches
|
|
279
|
+
`.spur/runs/workflow/<RUNID>.jsonl` or `*-partial.md`.
|
|
280
|
+
- **`--logs`** scopes to log reclamation only (skips stale-run finalization). `--dry-run` applies to
|
|
281
|
+
both scopes (lists what would be removed, writes nothing). `--json` returns
|
|
282
|
+
`{ olderThanMinutes, dryRun, cleaned, logs: { retentionDays, dryRun, reclaimed, failures } }` (with
|
|
283
|
+
`--logs`, the reclamation object alone).
|
|
284
|
+
## Behavior
|
|
285
|
+
|
|
286
|
+
This skill behaves as an **author** (choose mode → write a correct definition → prove it runs) feeding
|
|
287
|
+
a **pipeline** (validate → dry-run → read trace → refine). It also covers tuning existing workflows and
|
|
288
|
+
extending the engine with custom actions/guards. It does not execute the orchestrated *work* itself —
|
|
289
|
+
the workflow's actions (`shell`, custom runners) do that; this skill builds and operates the workflow.
|
|
290
|
+
|
|
291
|
+
## Gotchas
|
|
292
|
+
|
|
293
|
+
1. **`kind` is required for transition-flow, optional for state-machine.** A missing `kind` silently
|
|
294
|
+
parses as state-machine. If you authored flow intent (`nodes`/`edges`) without `kind: transition-flow`,
|
|
295
|
+
validation fails confusingly against the wrong schema. Always set `kind` explicitly for flows.
|
|
296
|
+
2. **`$schema` must be quoted** — it starts with `@`, which YAML reserves. Unquoted is a parse error.
|
|
297
|
+
(Same trap as `spur rule`.)
|
|
298
|
+
3. **Guard / edge declaration order matters.** The first passing guard (state-machine) or condition
|
|
299
|
+
(transition-flow) wins. Order the specific case before the fallback. Simple shell checks can use
|
|
300
|
+
`action-ok` before an unconditional retry edge; multi-condition gates prefer a **soft status-file
|
|
301
|
+
probe** (always exit 0) with ordered shell guards for PASS / FAIL / exhausted (see
|
|
302
|
+
`.spur/workflows/basic.yaml` and `task-pipeline.yaml` quality-gate hop).
|
|
303
|
+
4. **`env.allow` is an allowlist.** `${env.X}` resolves only if `X` is listed under `env.allow`;
|
|
304
|
+
otherwise it resolves empty. A workflow that "loses" an environment value usually forgot to allow it.
|
|
305
|
+
5. **Extensions are fail-closed.** Custom action/guard *modules* require `allowExtensions: true`; a
|
|
306
|
+
declared-but-not-allowed extension throws **before any import** — never silently dropped. Inline
|
|
307
|
+
`host.registerAction`/`registerGuard` need no flag; only the module loader is gated.
|
|
308
|
+
6. **A failed run does not throw.** Action/guard failures come back as `WorkflowRunResult` with
|
|
309
|
+
`status: 'failed'`, preserving the run record. Read the trace; don't expect an exception. (Definition
|
|
310
|
+
errors — schema/semantic/missing-state — *do* throw: `WorkflowValidationError` / `FSMError` / `RunCollisionError`.)
|
|
311
|
+
7. **Bound your loops.** A state-machine with a retry cycle needs `iterationBound` or it can run away.
|
|
312
|
+
The driver fails the run with `iteration-bound-exceeded` once the bound is crossed.
|
|
313
|
+
8. **`onError` is library/runtime-only for now.** The TypeScript engine supports
|
|
314
|
+
`action.onError ?? workflow.defaultOnError ?? runOptions.onError ?? 'fail'`, but the bundled JSON
|
|
315
|
+
schemas used by quoted `$schema` validation do not yet include `onError` / `defaultOnError`.
|
|
316
|
+
Do not author those fields in normal CLI YAML unless you intentionally validate with `--no-schema`
|
|
317
|
+
and own the compatibility tradeoff.
|
|
318
|
+
9. **Do not rely on a workspace `node_modules/@gobing-ai/spur` symlink for schema truth.**
|
|
319
|
+
Validate/run use the embedded map when the CLI provides it. A manual
|
|
320
|
+
`ln -sfn … node_modules/@gobing-ai/spur` is a session workaround, not a fix (0431).
|
|
321
|
+
10. **`--answer` does not imply `--yes`.** Omitting `run-id` without `--yes` still prompts to
|
|
322
|
+
confirm which paused run to resume, even when `--answer yes` is set.
|
|
323
|
+
|
|
324
|
+
## Additional Resources
|
|
325
|
+
|
|
326
|
+
- [workflows/operations.md](workflows/operations.md) — the operation procedures (validate/run/list/add/refine),
|
|
327
|
+
the shared find-existing-workflow and validate-and-dry-run cores, and the mode-selection gate. The
|
|
328
|
+
entry point for slash-command delegation.
|
|
329
|
+
- [workflows/authoring-workflows.md](workflows/authoring-workflows.md) — author a workflow: mode
|
|
330
|
+
selection in depth, per-mode real YAML shapes, built-in actions/guards, template variables, the
|
|
331
|
+
validate-and-dry-run core, expected-terminal-state assertion.
|
|
332
|
+
- [workflows/validation-and-extension.md](workflows/validation-and-extension.md) — validate semantics,
|
|
333
|
+
custom action/guard runners, the trust-gated extension loader, and CLI-vs-library capability gaps.
|
|
334
|
+
- `@gobing-ai/ts-dual-workflow-engine` README — authoritative library reference (both drivers,
|
|
335
|
+
RunLifecycle, persistence, the full event map, every built-in capability).
|
|
336
|
+
- `.spur/workflows/basic.yaml` — the canonical state-machine implement→soft-check→fixall loop
|
|
337
|
+
(status-file branching + `qualityGateMaxFixAttempts`); copy real shapes from here.
|
|
338
|
+
- `.spur/workflows/task-pipeline.yaml` — full production pipeline (precheck, quality gate, HITL,
|
|
339
|
+
verify, record) when you need the complete reliability pattern set.
|
|
340
|
+
|
|
341
|
+
## Platform Notes
|
|
342
|
+
|
|
343
|
+
### Claude Code
|
|
344
|
+
Run `spur workflow` via the Bash tool. During development the CLI entry is a `.ts` file that runs only
|
|
345
|
+
under Bun: `bun run apps/cli/src/index.ts workflow validate <file> --json`. The installed `spur` binary
|
|
346
|
+
works once built.
|
|
347
|
+
|
|
348
|
+
### Codex / OpenClaw / OpenCode / Antigravity
|
|
349
|
+
Run `spur workflow ...` via the Bash tool; parse `--json` output programmatically. Arguments are passed
|
|
350
|
+
directly on the command line.
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
**Template type**: technique
|
|
355
|
+
**Purpose**: Operate `spur workflow` across its full lifecycle — choose the execution mode, author, validate, run, and refine dual-mode workflows
|