@gobing-ai/spur 0.3.98 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/config/config.global.yaml +17 -0
- package/config/decisions/task-pipeline.yaml +55 -0
- package/config/plugin-scripts.json +15 -0
- package/config/rules/boundary/sp-runtime-path.yaml +2 -0
- package/config/rules/quality/coverage-gate.yaml +5 -3
- package/config/rules/strict/runtime-boundaries.yaml +11 -0
- package/config/rules/structure/protected-files.yaml +3 -0
- package/config/templates/task/brainstorm.md +1 -1
- package/config/templates/task/feature-impl.md +1 -1
- package/config/templates/task/issue.md +1 -1
- package/config/templates/task/meta.md +1 -1
- package/config/templates/task/review.md +1 -1
- package/config/templates/task/standard.md +1 -1
- package/config/workflows/task-pipeline.yaml +178 -18
- package/package.json +9 -9
- package/plugins/sp/README.md +14 -1
- package/plugins/sp/agents/super-reviewer.md +25 -11
- package/plugins/sp/commands/dev-changelog.md +5 -4
- package/plugins/sp/commands/dev-review-session.md +12 -7
- package/plugins/sp/commands/dev-run.md +5 -2
- package/plugins/sp/commands/dev-runall.md +5 -2
- package/plugins/sp/hooks/agent-lifecycle.ts +155 -0
- package/plugins/sp/hooks/fleet-guest-stop.ts +180 -0
- package/plugins/sp/hooks/hooks.json +45 -1
- package/plugins/sp/hooks/pi/guard-extension.ts +21 -0
- package/plugins/sp/lib/history-anatomy.generated.mjs +5 -0
- package/plugins/sp/lib/idea-handoff.generated.mjs +68 -10
- package/plugins/sp/lib/inline-run.generated.d.mts +1 -0
- package/plugins/sp/lib/inline-run.generated.mjs +83 -20
- package/plugins/sp/lib/residual-scan.generated.d.mts +5 -0
- package/plugins/sp/lib/residual-scan.generated.mjs +45 -5
- package/plugins/sp/lib/transcript.ts +160 -0
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/history-anatomy-cache.mjs +5 -0
- package/plugins/sp/scripts/inline-run-setup.mjs +21 -20
- package/plugins/sp/scripts/inline-run-setup.ts +25 -31
- package/plugins/sp/scripts/persist-out-check.mjs +164 -0
- package/plugins/sp/scripts/persist-out-check.ts +185 -0
- package/plugins/sp/scripts/residual-scan.mjs +52 -15
- package/plugins/sp/scripts/residual-scan.ts +11 -11
- package/plugins/sp/scripts/run-summary.mjs +286 -0
- package/plugins/sp/scripts/run-summary.ts +241 -0
- package/plugins/sp/scripts/session-timeline.mjs +234 -0
- package/plugins/sp/scripts/session-timeline.ts +152 -0
- package/plugins/sp/scripts/task-diffstat.mjs +17 -5
- package/plugins/sp/scripts/task-diffstat.ts +25 -4
- package/plugins/sp/skills/code-verification/SKILL.md +8 -0
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +17 -1
- package/plugins/sp/skills/fleet-join/SKILL.md +63 -0
- package/plugins/sp/skills/session-review/SKILL.md +46 -24
- package/plugins/sp/skills/spur-cli/SKILL.md +1 -0
- package/plugins/sp/skills/spur-cli/references/agent.md +108 -6
- package/plugins/sp/skills/spur-cli/references/decision.md +97 -0
- package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +22 -0
- package/plugins/sp/skills/spur-composer/SKILL.md +27 -1
- package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +40 -7
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +28 -16
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +278 -42
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +17 -1
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +16 -4
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +139 -10
- package/plugins/sp/skills/spur-dev/references/planning-workflow.md +6 -0
- package/schemas/decision-catalog.schema.json +226 -0
- package/schemas/spur-config.schema.json +25 -0
- package/spur.js +8856 -2564
- package/web/_astro/BoardApp.Bzr4OXrW.js +324 -0
- package/web/_astro/{BoardApp.Dflq5a8x.js → BoardApp.VeoxxLBy.js} +1 -1
- package/web/_astro/{TaskDetail.CK7oXddP.js → TaskDetail.LAyR_Pvn.js} +1 -1
- package/web/_astro/{arc.BIqkDATc.js → arc.f4e0OYLg.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.D0B3SvWv.js → architectureDiagram-3BPJPVTR.SSIvxi-Z.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.C2YyKQmL.js → blockDiagram-GPEHLZMM.DSXIHOFf.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.DmtTYrt_.js → c4Diagram-AAUBKEIU.DG9Ubu85.js} +1 -1
- package/web/_astro/channel.CUFdtWhm.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.HsRXbh7l.js → chunk-2J33WTMH.C7QU88Qh.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.obE_P-R3.js → chunk-4BX2VUAB.26Z94xBj.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.CjWTF5mz.js → chunk-55IACEB6.DYRiBkqw.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.DMo799Ax.js → chunk-727SXJPM.D5Vfv5pe.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.-cE6JOKZ.js → chunk-AQP2D5EJ.DXHV47bN.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.D6Rw6HIk.js → chunk-FMBD7UC4.D1suAfOs.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.d5rBD73Z.js → chunk-ND2GUHAM.D6BmdHEY.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.r3ox40Q3.js → chunk-QZHKN3VN.rnPU-Ak-.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.CGGbvjjJ.js → classDiagram-4FO5ZUOK.CvEfXIo1.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.CGGbvjjJ.js → classDiagram-v2-Q7XG4LA2.CvEfXIo1.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.CmKtPd4l.js → cose-bilkent-S5V4N54A.DkMisLSm.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.QNg5GXs8.js → cynefin-OW5HDTMX.C77Q-pF6.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.B1SabsjC.js → dagre-BM42HDAG.YaV8ce27.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.RtDbNj89.js → diagram-2AECGRRQ.CUyGEKge.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.IzZpuM-Y.js → diagram-5GNKFQAL.CRgd8dgj.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.DPTF_fgv.js → diagram-KO2AKTUF.Bmzys9hq.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.CCyza39P.js → diagram-LMA3HP47.BvmJY4Lw.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.BXAi8zcZ.js → diagram-OG6HWLK6.0hEhD30d.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.DUDX7_MR.js → erDiagram-TEJ5UH35.Bjt8lrnm.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.C63QTh3N.js → flowDiagram-I6XJVG4X.CJZTjRDV.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.CnlZJSwG.js → ganttDiagram-6RSMTGT7._VqoVafc.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.CTqIrOPC.js → gitGraphDiagram-PVQCEYII.CEDb7tVo.js} +1 -1
- package/web/_astro/index.Dg_3bOaJ.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.BYMiDFh9.js → infoDiagram-5YYISTIA.CgwifPpK.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.CRiQmI-z.js → ishikawaDiagram-YF4QCWOH.B99wx2J1.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.YQgWBsEU.js → journeyDiagram-JHISSGLW.2hRwJbEo.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.1ECnrrK1.js → kanban-definition-UN3LZRKU.BahJkKNC.js} +1 -1
- package/web/_astro/{linear.DzVA71yT.js → linear.DguUUe6V.js} +1 -1
- package/web/_astro/{mermaid.core.BK7P2Sr_.js → mermaid.core.CvgfmqVr.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.BvXyWZK7.js → mindmap-definition-RKZ34NQL.B3qEA3x9.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.BiXk1TW3.js → pieDiagram-4H26LBE5.FlWA-_ru.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.krvBH8lh.js → quadrantDiagram-W4KKPZXB.9bVn35X7.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.ih1gL25c.js → requirementDiagram-4Y6WPE33.ChykIhct.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.CpUxkv4u.js → sankeyDiagram-5OEKKPKP.Z2Y3hkYb.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.clAfeN7y.js → sequenceDiagram-3UESZ5HK.J7JsHWDt.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.DpXWb8-m.js → stateDiagram-AJRCARHV.DAPbiCSS.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.ClZb9GIH.js → stateDiagram-v2-BHNVJYJU.D2jvSi0q.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.BLcX5dh7.js → timeline-definition-PNZ67QCA.psW2ci29.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.oGgpKMpx.js → vennDiagram-CIIHVFJN.DlILYVmg.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.BPHtMtt-.js → wardleyDiagram-YWT4CUSO.xNWwLJnn.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.BNZOPYSk.js → xychartDiagram-2RQKCTM6.EU_BuH7V.js} +1 -1
- package/web/board-runtime.json +1 -1
- package/web/index.html +2 -2
- package/web/_astro/BoardApp.CYbe-OTb.js +0 -322
- package/web/_astro/channel.JuKVyuhZ.js +0 -1
- package/web/_astro/index.CbNz17Sx.css +0 -1
|
@@ -142,7 +142,10 @@ the human/native presentation layer — labels are display addresses only, never
|
|
|
142
142
|
safety checks succeed, create/adopt and `cd` into the execution tree; confirm absolute cwd,
|
|
143
143
|
branch, base SHA, and ownership. An invalid/empty target, unsupported mode, ambiguous ownership,
|
|
144
144
|
or stale target stops without creating a tree or discarding work. All subsequent tools, agents,
|
|
145
|
-
corpus writes, and run artifacts use the confirmed execution tree
|
|
145
|
+
corpus writes, and run artifacts use the confirmed execution tree — **re-selected per call, not
|
|
146
|
+
inherited**: a host shell call starts in an arbitrary directory and carries no cwd from the
|
|
147
|
+
previous call, so every later call runs inside the **Per-call execution-tree pin** protocol
|
|
148
|
+
below (task 1058), which consumes the identity confirmed here.
|
|
146
149
|
8. **Publish the workflow inventory (R4), BEFORE reading the YAML.** Resolve the selected workflow
|
|
147
150
|
through the same project/bundled resolver as execution and run
|
|
148
151
|
`spur workflow show <resolved-file> --no-logo --format todo --json`. Validate the projection with
|
|
@@ -156,6 +159,11 @@ the human/native presentation layer — labels are display addresses only, never
|
|
|
156
159
|
resolved definition identity against the bound `__definitionDigest`; a mismatch is identity drift
|
|
157
160
|
and fails closed (step 8 already caught projection-side drift; this re-checks the same definition
|
|
158
161
|
the interpreter will execute).
|
|
162
|
+
- **Runtime-written vars are re-read, never cached.** A var a later state writes (`proofDigest`,
|
|
163
|
+
`proofDigestNow`, `taskSpecPath`, `featureSpecPath`, `taskPriority`, `mode`, `gateFindings`) is a
|
|
164
|
+
run artifact, not a YAML default: re-resolve it from `.spur/run/`/`.spur/memory/runs/` at every
|
|
165
|
+
state boundary, and never let a host-side copy of the invocation vars shadow it — a stale empty
|
|
166
|
+
`proofDigest` silently fails the bound `run.artifact` equivalence and the `record` proof guard.
|
|
159
167
|
- **Layer 2** = the active state's `onEnter` actions (`kind` + resolved `input`/`command`), from
|
|
160
168
|
the YAML read here, shown only for the active state.
|
|
161
169
|
- **Refresh cadence** = stage boundaries only (when the current state changes after a transition),
|
|
@@ -186,6 +194,12 @@ the human/native presentation layer — labels are display addresses only, never
|
|
|
186
194
|
This is required for the normal `testing → done` provenance guard. Planning pipelines have no
|
|
187
195
|
task lifecycle link and skip this task-specific action.
|
|
188
196
|
|
|
197
|
+
**Run it from the task's execution tree.** Every tree has an isolated `.spur` DB, so a link
|
|
198
|
+
recorded from the invoking tree never satisfies a worktree's guard — the exact failure that
|
|
199
|
+
forced audited `--provenance-bypass` on tasks 1029/1030 (E93). Under `--worktree`, enter the
|
|
200
|
+
worktree first and run this command (and every lifecycle-touching command: `task record`,
|
|
201
|
+
`task update <wbs> testing|done`) from the worktree root.
|
|
202
|
+
|
|
189
203
|
### Idea-pipeline quick start (0887 R1/R2)
|
|
190
204
|
|
|
191
205
|
Minimum files to read for `/sp:dev-idea` inline runs — then drive `idea-pipeline.yaml`:
|
|
@@ -215,6 +229,91 @@ Expected artifacts per stage (all run-scoped under `.spur/run/<run-id>-*`):
|
|
|
215
229
|
| ready-prepare | `-idea-ready.json` |
|
|
216
230
|
| handoff-finalize | `-idea-handoff.md` |
|
|
217
231
|
|
|
232
|
+
## Per-call execution-tree pin (task 1058)
|
|
233
|
+
|
|
234
|
+
A host shell tool call may start in an arbitrary directory; no earlier call's `cd` is input to the
|
|
235
|
+
next call. During inline run `95522d21` that drift silently landed corpus writes in the invoking
|
|
236
|
+
tree instead of the run worktree. Every host-session corpus command — reads, writes, absolute
|
|
237
|
+
section-input files and output artifacts — therefore selects the confirmed execution tree **within
|
|
238
|
+
the same tool call**, in a self-contained subshell. The identity is never re-derived: it is the
|
|
239
|
+
tree path, branch and resolved Spur invocation confirmed at bootstrap isolation (step 7) and, for
|
|
240
|
+
batch worktrees, recorded in the WT-3 marker (execution-batch.md). The resolved invocation is
|
|
241
|
+
field 4 of the dispatch payload — never a bare `spur`.
|
|
242
|
+
|
|
243
|
+
Template — every host shell call follows this shape; paths are quoted because trees may contain
|
|
244
|
+
spaces:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
(
|
|
248
|
+
cd -- "$SPUR_TREE" || { echo "tree missing: expected $SPUR_TREE" >&2; exit 1; }
|
|
249
|
+
ACTUAL_TREE="$(pwd -P)"
|
|
250
|
+
[ "$ACTUAL_TREE" = "$SPUR_TREE" ] || { echo "tree mismatch: expected $SPUR_TREE, got $ACTUAL_TREE" >&2; exit 1; }
|
|
251
|
+
GIT_TOP="$(git rev-parse --show-toplevel)" || exit 1
|
|
252
|
+
[ "$GIT_TOP" = "$SPUR_TREE" ] || { echo "toplevel mismatch: expected $SPUR_TREE, got $GIT_TOP" >&2; exit 1; }
|
|
253
|
+
ACTUAL_BRANCH="$(git branch --show-current)"
|
|
254
|
+
[ "$ACTUAL_BRANCH" = "$SPUR_BRANCH" ] || { echo "branch mismatch: expected $SPUR_BRANCH, got $ACTUAL_BRANCH" >&2; exit 1; }
|
|
255
|
+
exec <resolved-spur-invocation> task show <wbs> --json # the already-resolved CLI command
|
|
256
|
+
)
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Rules (R1):
|
|
260
|
+
|
|
261
|
+
- **Pin reads as well as writes.** `task show`, `feature show`, `workflow show` and `task check`
|
|
262
|
+
use the same subshell — a read from the wrong tree is a wrong answer, not a harmless one.
|
|
263
|
+
- **Validate the returned `filePath`.** A pinned `task show --json` must return a `filePath`
|
|
264
|
+
inside the expected configured corpus/tree. Configured task folders may intentionally live
|
|
265
|
+
outside the default `docs/tasks` — check membership in the configured folder set, not one
|
|
266
|
+
literal path.
|
|
267
|
+
- **Prove sections with a fresh pinned read.** After a section write, re-run a pinned
|
|
268
|
+
`task show <wbs> --json` and assert the new body there; a heading-only grep is not proof of
|
|
269
|
+
section contents.
|
|
270
|
+
- **Resolve section input/output paths before changing tree.** `--section <name> --from-file
|
|
271
|
+
<path>`, `answerFile`/`expectFile` and run artifacts are resolved against the confirmed tree
|
|
272
|
+
(dispatch field 5) before the subshell `cd`s.
|
|
273
|
+
- **Fail closed before writing.** A failed `cd` or any identity mismatch exits nonzero naming
|
|
274
|
+
expected vs actual, before the CLI runs — there is no designated exit code; consumers read
|
|
275
|
+
nonzero and the named expected/actual trees. A PWD assertion alone cannot select a tree: `cd`
|
|
276
|
+
first, then verify the physical path and repository identity.
|
|
277
|
+
- **Native tool cwd options are additive.** A tool-level cwd/`cd` pin may additionally set the
|
|
278
|
+
call's directory, but never replaces the identity checks above.
|
|
279
|
+
- **Host-only scope.** Engine `shell` actions already bind `context.workdir`
|
|
280
|
+
(`packages/app/src/workflow/actions/shell.ts:98`) — leave them alone. No service-wide
|
|
281
|
+
`process.chdir`, no new public flag, no helper framework.
|
|
282
|
+
|
|
283
|
+
The `spur …` command examples in this driver (run setup, workflow inventory, `task run-link`,
|
|
284
|
+
fingerprint capture, record/done sequencing) abbreviate the pinned subshell form for readability;
|
|
285
|
+
each executes inside the template above with the confirmed tree, branch and resolved invocation
|
|
286
|
+
substituted. A dispatched subagent inherits the same duty: its own tool calls may each start in
|
|
287
|
+
an arbitrary directory, so every shell call the delegate makes re-pins the execution tree with
|
|
288
|
+
the identity supplied in the dispatch payload — never re-derived.
|
|
289
|
+
|
|
290
|
+
## Chunked implement dispatch contract (cap-limited worker models, task 1066)
|
|
291
|
+
|
|
292
|
+
Cap-limited worker executors die on monolithic implement briefs: the output cap is consumed by
|
|
293
|
+
one giant thinking block before any file action lands. Recognized failure signature —
|
|
294
|
+
consecutive implement dispatches end with `stopReason=length` and **zero output** (no tool
|
|
295
|
+
call, no file write), optionally with a `contact_supervisor` call mid-death. The trigger is
|
|
296
|
+
the observed signature, never a model name: any executor that exhibits it is treated as
|
|
297
|
+
cap-limited for the rest of the run.
|
|
298
|
+
|
|
299
|
+
Before dispatching an `implement` stage to a worker executor:
|
|
300
|
+
|
|
301
|
+
1. **Chunk the brief.** Split the implement scope into small, independent dispatches ordered
|
|
302
|
+
by dependency (e.g. test cases → doc rewrite → task sections), one bounded objective per
|
|
303
|
+
dispatch. Never pack the whole task plan into one dispatch.
|
|
304
|
+
2. **Cap thinking.** Instruct the worker to keep every thinking block under ~150 words and act
|
|
305
|
+
between thoughts; never plan the whole task in one block.
|
|
306
|
+
3. **Fallback ladder.** A worker still dying with the signature: re-chunk the remaining scope
|
|
307
|
+
smaller once; still failing → execute the stage host-inline (the driver's inline stage
|
|
308
|
+
fallback) and record the fallback in the run log.
|
|
309
|
+
|
|
310
|
+
Evidence (2026-10-02, task 1059 implement stage, runall-D63-2ebbd97c): 4/4 single-shot
|
|
311
|
+
implement dispatches died `stopReason=length` on a 16k-output-cap worker; the same scope in
|
|
312
|
+
three chunked dispatches with the thinking cap succeeded 3/3. See the pitfalls 2026-10-02
|
|
313
|
+
entry and `.spur/memory/runs/runall-D63-2ebbd97c.md`.
|
|
314
|
+
|
|
315
|
+
## Comprehensive-check retention and evidence (R7/R8)
|
|
316
|
+
|
|
218
317
|
## Comprehensive-check retention and evidence (R7/R8)
|
|
219
318
|
|
|
220
319
|
**R7 — comprehensive checks stay at their owning boundaries.** Quick readiness and plan projection are
|
|
@@ -248,9 +347,13 @@ or terminal close. Measure its actual duration and preserve its declared failure
|
|
|
248
347
|
|
|
249
348
|
```bash
|
|
250
349
|
bun "$SETUP_SCRIPT" --action --run-id "$RUN_ID" --node <state-id> --kind <action-kind> \
|
|
251
|
-
--status <done|failed> --ok <true|false> --duration-ms <measured-ms>
|
|
350
|
+
--status <done|failed> --ok <true|false> --duration-ms <measured-ms> [--estimated]
|
|
252
351
|
```
|
|
253
352
|
|
|
353
|
+
`--duration-ms` is the wall clock measured around the action. Pass `--estimated` when you did
|
|
354
|
+
**not** time it — for example a duration reconstructed after a subagent returned — so the row is
|
|
355
|
+
labelled instead of presented as measured. `--estimated` is valid only with `--action`.
|
|
356
|
+
|
|
254
357
|
For a multi-action state, `--actions-file` may emit the measured boundaries together before leaving
|
|
255
358
|
that state. Follow [Structured trace emission](#structured-trace-emission-adr-117-task-0868) for the
|
|
256
359
|
payload, delegated `decide` emission and best-effort failure handling. Never retry a failed batch
|
|
@@ -263,6 +366,14 @@ Action semantics come from the YAML and the workflow action contract:
|
|
|
263
366
|
- `note` — append the expanded message to the inline run log.
|
|
264
367
|
- `doctor.probe` — run the declared Spur doctor once, persist its status file, and apply any
|
|
265
368
|
`setVars` result (including a resolved executor) before the next action or state.
|
|
369
|
+
**Role-map mode has no inline surface (task 1088 session finding).** A `doctor.probe` that
|
|
370
|
+
declares `roles` resolves them through `AgentService.resolve`, which is reachable only in-process
|
|
371
|
+
(the engine composition injects the service); no CLI verb exposes it, so the host session cannot
|
|
372
|
+
reproduce `__executor.<role>` pins. The driver therefore probes the host-session agent instead,
|
|
373
|
+
writes that verdict to the declared status file, and records one run-log line naming the
|
|
374
|
+
substitution. Nothing downstream degrades: `__executor.<role>` pins are consumed by the
|
|
375
|
+
subprocess dispatch path, and the inline path executes its stages in the host session without
|
|
376
|
+
reading them. Never fabricate pins to satisfy the shape, and never skip the status file.
|
|
266
377
|
- `file.read.into-var` — read the declared file into the declared run variable before subsequent
|
|
267
378
|
actions/guards.
|
|
268
379
|
- `hitl.confirm` — under `profile=auto`, follow the YAML's auto-skip transition. Otherwise pause,
|
|
@@ -296,7 +407,8 @@ Action semantics come from the YAML and the workflow action contract:
|
|
|
296
407
|
`verify-verdict`: verdict `PASS`), `proofBinding: current` is honored against a freshly captured
|
|
297
408
|
proof digest — capture it with `bun "$SETUP_SCRIPT" --fingerprint --task-file <task path>
|
|
298
409
|
[--feature-file <feature path>]`, the same entry point used at Run setup, which prints the engine
|
|
299
|
-
`sha256:<hex>` digest. Run it
|
|
410
|
+
`sha256:<hex>` digest. Run it inside a per-call pin subshell for the confirmed execution tree
|
|
411
|
+
(the cwd feeds the git-tree half of the digest)
|
|
300
412
|
and pass the same `--feature-file` the run folded in — omitting it, or running from elsewhere,
|
|
301
413
|
yields a different digest and the mismatch surfaces later as a refused `run.artifact`
|
|
302
414
|
registration — and the run-scoped review-completion marker exists — then appends one provenance
|
|
@@ -314,6 +426,10 @@ Action semantics come from the YAML and the workflow action contract:
|
|
|
314
426
|
|
|
315
427
|
**Native-subagent dispatch (R2 eligibility, evaluated before each action):**
|
|
316
428
|
|
|
429
|
+
Before dispatching an `implement` stage to a worker executor, apply the chunked implement
|
|
430
|
+
dispatch contract (§ Chunked implement dispatch contract): chunk the brief, cap thinking
|
|
431
|
+
blocks, and know the fallback ladder before spending a dispatch.
|
|
432
|
+
|
|
317
433
|
1. The invocation is one of the two interactive inline full-pipeline surfaces (`dev-run --mode full`
|
|
318
434
|
or sequential `dev-runall`) and the resolved selector is inline — i.e. `--agent` **omitted**
|
|
319
435
|
(0687 R1 default) or `--agent inline` passed explicitly (0687 R2 generalized from omit-only).
|
|
@@ -391,7 +507,10 @@ boundary, and a delegate left to re-derive them re-derives them against its own
|
|
|
391
507
|
that invocation and MUST NOT rely on a bare `spur`: a competing `spur` earlier on the delegate's
|
|
392
508
|
PATH otherwise wins. Setting `SPUR_BIN` alone does **not** change bare-command resolution — only
|
|
393
509
|
using the supplied invocation does. Spur-owned scripted calls take it through the existing
|
|
394
|
-
`--spur-bin` flag rather than a new mechanism.
|
|
510
|
+
`--spur-bin` flag rather than a new mechanism. A delegate's tool calls may each start in an
|
|
511
|
+
arbitrary directory, so every shell call the delegate makes re-pins the execution tree per the
|
|
512
|
+
**Per-call execution-tree pin** protocol using this supplied identity — never an inherited cwd
|
|
513
|
+
and never a re-derivation.
|
|
395
514
|
5. The **resolved absolute output path** (`answerFile`/`expectFile`, resolved as above) and the
|
|
396
515
|
**owning stage's artifact contract** — for a verify stage, the compact contract below.
|
|
397
516
|
6. **The implement-stage acceptance-evidence requirement** — for the implement stage and any stage
|
|
@@ -526,23 +645,29 @@ The driver reaches it through the existing run delegate (`$SETUP_SCRIPT`,
|
|
|
526
645
|
|
|
527
646
|
```bash
|
|
528
647
|
bun "$SETUP_SCRIPT" --action --run-id "$RUN_ID" --node <state-id> --kind <action-kind> \
|
|
529
|
-
--status <done|failed> --ok <true|false> --duration-ms <measured-ms>
|
|
648
|
+
--status <done|failed> --ok <true|false> --duration-ms <measured-ms> [--estimated]
|
|
530
649
|
```
|
|
531
650
|
|
|
532
651
|
`<state-id>` is the current YAML state id (the `node`), `<action-kind>` the YAML action kind
|
|
533
652
|
(`agent.run`, `shell`, `note`, `doctor.probe`, …). `--status` is `done` when the action settled
|
|
534
653
|
under its declared error policy and `failed` otherwise; `--duration-ms` is the wall clock the
|
|
535
|
-
driver measured around the action
|
|
536
|
-
|
|
537
|
-
|
|
654
|
+
driver measured around the action, and `--estimated` marks a duration the driver did **not**
|
|
655
|
+
time (a value reconstructed after the action returned). The row's provenance stamp rides
|
|
656
|
+
`action_runs.result_json` as `{provenance:'host-reported', estimated}`, and the projection
|
|
657
|
+
exposes it per attempt — `provenance` is `host-reported` for every inline row and `unknown`
|
|
658
|
+
for an engine-written or pre-stamp row, so the Board can label a host-reported duration
|
|
659
|
+
instead of presenting it as measured. This writes the `action_runs` row (node, kind, status,
|
|
660
|
+
`ok`, `duration_ms`, `run_id`) the engine would have written, so the run's rows are queryable
|
|
661
|
+
by run id (`spur workflow progress <run-id>`, `ActionRunDao`) without reading the text log.
|
|
662
|
+
The writer
|
|
538
663
|
back-dates the row's `started_at` from its own `completed_at` minus the measured duration
|
|
539
664
|
(0887 R8), so `completed_at − started_at == duration_ms` exactly; a back-date failure is
|
|
540
665
|
recorded (`action.backdate`) and never affects the run.
|
|
541
666
|
|
|
542
667
|
- **A state with several actions (1007 R5)** — emit the whole state's boundaries in one call
|
|
543
668
|
instead of one `--action` invocation per action. Write a JSON array
|
|
544
|
-
(`[{node,kind,status,ok,durationMs}, …]` —
|
|
545
|
-
file and pass it with `--actions-file`:
|
|
669
|
+
(`[{node,kind,status,ok,durationMs,estimated?}, …]` — the `--action` fields, `estimated`
|
|
670
|
+
optional and `false` when absent) to a temp file and pass it with `--actions-file`:
|
|
546
671
|
|
|
547
672
|
```bash
|
|
548
673
|
bun "$SETUP_SCRIPT" --actions-file <actions.json> --run-id "$RUN_ID"
|
|
@@ -590,6 +715,8 @@ The driver reaches it through the existing run delegate (`$SETUP_SCRIPT`,
|
|
|
590
715
|
`--status` is the declared terminal state's verdict, not a guess: a run that reached a terminal
|
|
591
716
|
state is `done`; a run halted by a failing action under its error policy is `failed`. On success
|
|
592
717
|
the close reports the recorded evidence: `{"ok":true,"runId":…,"actionRows":<n>}`.
|
|
718
|
+
After the close, the invoking command prints the measured
|
|
719
|
+
[execution summary](dev-operations.md#execution-summary) for `$RUN_ID` unless `--no-summary`.
|
|
593
720
|
|
|
594
721
|
**Zero-row done closes are a named failure (task 0975 R2).** A run closed `done` with **zero**
|
|
595
722
|
`action_runs` rows (`actionRows:0`) finalizes the row but exits `1` with
|
|
@@ -636,6 +763,8 @@ tasks (0617, 0619) because the sections were hand-written **before** the verdict
|
|
|
636
763
|
```bash
|
|
637
764
|
# verdict artifact first (shape: {wbs, verdict, requirements:[{id,status,evidence}], checks:[], source})
|
|
638
765
|
# then the record hop; then re-write Testing/Solution if record's backfill is thinner than intended.
|
|
766
|
+
# Both calls are per-call pinned (task 1058): the record hop selects the execution tree inside
|
|
767
|
+
# the same tool call — never through a cwd a previous call happened to leave behind.
|
|
639
768
|
spur task update <wbs> wip --no-lifecycle
|
|
640
769
|
spur task record <wbs> --solution-from-diff --transition testing
|
|
641
770
|
```
|
|
@@ -43,6 +43,12 @@ creation.
|
|
|
43
43
|
spur feature create "<name>" [--parent <id>]
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
Without `--parent`, choose one before creating: list the tree (`spur feature list --json`) and nest the
|
|
47
|
+
feature under the existing feature that owns the surface or its design satellite. A new root feature
|
|
48
|
+
is a last resort that needs an explicit operator decision. When a parent is full (the 9-child limit),
|
|
49
|
+
nest under the closest owning child, and use `spur feature move <id> --parent <id> --dry-run` to
|
|
50
|
+
relocate an existing feature.
|
|
51
|
+
|
|
46
52
|
The feature file lands in `docs/features/<ID>_<slug>.md`. Immediately author the `## Goal`
|
|
47
53
|
(single sentence) and `## Scope` (in/out bullets).
|
|
48
54
|
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"title": "AI decision catalog",
|
|
4
|
+
"description": "Declarative decision catalog for @gobing-ai/ts-ai-decision (task 0089). Strict: unknown keys are rejected at every level. Cross-field checks (fallback vocabulary, ${params.*} references, defaults, reserved instructions parameter) run at load time.",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["version", "decisions"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"$schema": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "Bundled package-specifier ref to this schema; validated when present."
|
|
12
|
+
},
|
|
13
|
+
"version": {
|
|
14
|
+
"const": 1
|
|
15
|
+
},
|
|
16
|
+
"defaults": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"additionalProperties": false,
|
|
19
|
+
"properties": {
|
|
20
|
+
"maker": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"pattern": "^[a-z][a-z0-9-]*$",
|
|
23
|
+
"description": "Registered maker name (built-ins: typesafe | fm-local | laya-local)."
|
|
24
|
+
},
|
|
25
|
+
"model": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"description": "Forwarded as the ask-level model; omit for the backend default."
|
|
28
|
+
},
|
|
29
|
+
"minConfidence": {
|
|
30
|
+
"type": "number",
|
|
31
|
+
"minimum": 0,
|
|
32
|
+
"maximum": 1
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"decisions": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"minProperties": 1,
|
|
39
|
+
"propertyNames": {
|
|
40
|
+
"pattern": "^[a-z][a-z0-9_-]*$"
|
|
41
|
+
},
|
|
42
|
+
"additionalProperties": {
|
|
43
|
+
"$ref": "#/$defs/decision"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"$defs": {
|
|
48
|
+
"jsonValue": {
|
|
49
|
+
"anyOf": [
|
|
50
|
+
{
|
|
51
|
+
"type": "string"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"type": "number"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"type": "boolean"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"type": "null"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
"type": "array",
|
|
64
|
+
"items": {
|
|
65
|
+
"$ref": "#/$defs/jsonValue"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"type": "object",
|
|
70
|
+
"additionalProperties": {
|
|
71
|
+
"$ref": "#/$defs/jsonValue"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
"jevEntry": {
|
|
77
|
+
"description": "A Jev entry: description string, JSON object, JSON array, or null (left undescribed). Description string leaves support ${params.*} templating.",
|
|
78
|
+
"anyOf": [
|
|
79
|
+
{
|
|
80
|
+
"type": "string"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"type": "null"
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"type": "array",
|
|
87
|
+
"items": {
|
|
88
|
+
"$ref": "#/$defs/jsonValue"
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"type": "object",
|
|
93
|
+
"additionalProperties": {
|
|
94
|
+
"$ref": "#/$defs/jsonValue"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
"paramType": {
|
|
100
|
+
"enum": ["string", "number", "boolean", "enum", "json"]
|
|
101
|
+
},
|
|
102
|
+
"param": {
|
|
103
|
+
"anyOf": [
|
|
104
|
+
{
|
|
105
|
+
"$ref": "#/$defs/paramType"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"type": "object",
|
|
109
|
+
"additionalProperties": false,
|
|
110
|
+
"required": ["type"],
|
|
111
|
+
"properties": {
|
|
112
|
+
"type": {
|
|
113
|
+
"$ref": "#/$defs/paramType"
|
|
114
|
+
},
|
|
115
|
+
"default": {},
|
|
116
|
+
"values": {
|
|
117
|
+
"type": "array",
|
|
118
|
+
"items": {
|
|
119
|
+
"type": "string"
|
|
120
|
+
},
|
|
121
|
+
"minItems": 1,
|
|
122
|
+
"description": "Accepted strings for type: enum (at least one)."
|
|
123
|
+
},
|
|
124
|
+
"description": {
|
|
125
|
+
"type": "string"
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
"choiceCriteria": {
|
|
132
|
+
"type": "object",
|
|
133
|
+
"minProperties": 2,
|
|
134
|
+
"additionalProperties": {
|
|
135
|
+
"$ref": "#/$defs/jevEntry"
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
"scoreCriteria": {
|
|
139
|
+
"type": "array",
|
|
140
|
+
"minItems": 2,
|
|
141
|
+
"items": {
|
|
142
|
+
"$ref": "#/$defs/jevEntry"
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
"noulCriteria": {
|
|
146
|
+
"type": "object",
|
|
147
|
+
"additionalProperties": false,
|
|
148
|
+
"properties": {
|
|
149
|
+
"true": {
|
|
150
|
+
"$ref": "#/$defs/jevEntry"
|
|
151
|
+
},
|
|
152
|
+
"false": {
|
|
153
|
+
"$ref": "#/$defs/jevEntry"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
"decision": {
|
|
158
|
+
"type": "object",
|
|
159
|
+
"additionalProperties": false,
|
|
160
|
+
"required": ["type", "fallback"],
|
|
161
|
+
"properties": {
|
|
162
|
+
"type": {
|
|
163
|
+
"enum": ["choice", "score", "noul"]
|
|
164
|
+
},
|
|
165
|
+
"description": {
|
|
166
|
+
"type": "string",
|
|
167
|
+
"description": "Documentation only; never sent to the model."
|
|
168
|
+
},
|
|
169
|
+
"instructions": {
|
|
170
|
+
"$ref": "#/$defs/jevEntry",
|
|
171
|
+
"description": "Templated Jev entry; defaults to \"${params.instructions}\"."
|
|
172
|
+
},
|
|
173
|
+
"parameters": {
|
|
174
|
+
"type": "object",
|
|
175
|
+
"propertyNames": {
|
|
176
|
+
"pattern": "^[a-z][a-zA-Z0-9_]*$"
|
|
177
|
+
},
|
|
178
|
+
"additionalProperties": {
|
|
179
|
+
"$ref": "#/$defs/param"
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
"criteria": {
|
|
183
|
+
"description": "choice: label map (>= 2 labels); score: ordered rubric (>= 2 levels); noul: optional { true?, false? }.",
|
|
184
|
+
"anyOf": [
|
|
185
|
+
{
|
|
186
|
+
"$ref": "#/$defs/choiceCriteria"
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
"$ref": "#/$defs/scoreCriteria"
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"$ref": "#/$defs/noulCriteria"
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
},
|
|
196
|
+
"fallback": {
|
|
197
|
+
"description": "choice: a declared label; score: integer level in [0, levels-1]; noul: boolean.",
|
|
198
|
+
"anyOf": [
|
|
199
|
+
{
|
|
200
|
+
"type": "string"
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
"type": "number"
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"type": "boolean"
|
|
207
|
+
}
|
|
208
|
+
]
|
|
209
|
+
},
|
|
210
|
+
"minConfidence": {
|
|
211
|
+
"type": "number",
|
|
212
|
+
"minimum": 0,
|
|
213
|
+
"maximum": 1
|
|
214
|
+
},
|
|
215
|
+
"maker": {
|
|
216
|
+
"type": "string",
|
|
217
|
+
"pattern": "^[a-z][a-z0-9-]*$",
|
|
218
|
+
"description": "Registered maker name; checked by the hub, not the loader."
|
|
219
|
+
},
|
|
220
|
+
"model": {
|
|
221
|
+
"type": "string"
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
@@ -287,6 +287,31 @@
|
|
|
287
287
|
}
|
|
288
288
|
}
|
|
289
289
|
},
|
|
290
|
+
"decisions": {
|
|
291
|
+
"type": "object",
|
|
292
|
+
"description": "Decision-catalog resolution and DecisionMaker selection (feature P / ADR-134). The whole section is optional.",
|
|
293
|
+
"properties": {
|
|
294
|
+
"paths": {
|
|
295
|
+
"type": "array",
|
|
296
|
+
"items": {
|
|
297
|
+
"type": "string"
|
|
298
|
+
},
|
|
299
|
+
"description": "Extra catalog folders for the registered decision layer (absolute, project-relative, or 'bundled:<sub>' against the installed package's config root)."
|
|
300
|
+
},
|
|
301
|
+
"maker": {
|
|
302
|
+
"type": "string",
|
|
303
|
+
"pattern": "^[a-z][a-z0-9-]*$",
|
|
304
|
+
"description": "Default DecisionMaker for every decision point (built-ins: typesafe | fm-local | laya-local). Must be registered; an unknown name is a config error, never a silent fallback."
|
|
305
|
+
},
|
|
306
|
+
"makers": {
|
|
307
|
+
"type": "object",
|
|
308
|
+
"additionalProperties": {
|
|
309
|
+
"type": "string"
|
|
310
|
+
},
|
|
311
|
+
"description": "Per-decision-point maker overrides, keyed by decision id; outrank decisions.maker."
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
},
|
|
290
315
|
"redaction": {
|
|
291
316
|
"type": "object",
|
|
292
317
|
"properties": {
|