@gobing-ai/spur 0.3.99 → 0.4.1
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/gates.yaml +36 -0
- package/config/decisions/history-anatomy.yaml +35 -0
- package/config/decisions/idea-pipeline.yaml +38 -0
- package/config/decisions/task-pipeline.yaml +55 -0
- package/config/plugin-scripts.json +25 -0
- package/config/rules/boundary/sp-runtime-path.yaml +2 -0
- package/config/rules/docs/sourced-api-claims.yaml +60 -0
- package/config/rules/quality/coverage-gate.yaml +5 -3
- package/config/rules/recommended-pre-check.yaml +8 -3
- package/config/rules/strict/runtime-boundaries.yaml +11 -0
- package/config/rules/structure/protected-files.yaml +3 -0
- package/config/rules/structure/scratch-evidence-pointer.yaml +35 -0
- package/config/script-placement-baseline.json +2 -2
- package/config/workflows/feature-lifecycle.yaml +9 -2
- package/config/workflows/history-anatomy.yaml +22 -0
- package/config/workflows/idea-pipeline.yaml +75 -0
- package/config/workflows/task-pipeline.yaml +368 -49
- package/config/workflows/wrapup-pipeline.yaml +106 -13
- package/package.json +9 -9
- package/plugins/sp/README.md +25 -2
- package/plugins/sp/agents/super-planner.md +11 -0
- package/plugins/sp/agents/super-reviewer.md +39 -11
- package/plugins/sp/bunfig.toml +8 -0
- package/plugins/sp/commands/dev-brainstorm.md +29 -16
- package/plugins/sp/commands/dev-changelog.md +5 -4
- package/plugins/sp/commands/dev-find-way.md +80 -0
- package/plugins/sp/commands/dev-idea.md +6 -0
- package/plugins/sp/commands/dev-parallel.md +8 -2
- package/plugins/sp/commands/dev-plan.md +6 -0
- package/plugins/sp/commands/dev-review-session.md +14 -7
- package/plugins/sp/commands/dev-run.md +17 -4
- package/plugins/sp/commands/dev-runall.md +21 -10
- package/plugins/sp/hooks/agent-lifecycle.ts +155 -0
- package/plugins/sp/hooks/context-session-start.ts +1 -1
- 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 +72 -11
- package/plugins/sp/lib/inline-run-app.ts +28 -0
- package/plugins/sp/lib/inline-run.generated.d.mts +6 -0
- package/plugins/sp/lib/inline-run.generated.mjs +364 -46
- package/plugins/sp/lib/plan-projection.generated.d.mts +28 -0
- package/plugins/sp/lib/plan-projection.generated.mjs +255 -0
- package/plugins/sp/lib/quality-gate.generated.d.mts +47 -1
- package/plugins/sp/lib/quality-gate.generated.mjs +30489 -82
- package/plugins/sp/lib/residual-scan.generated.d.mts +19 -0
- package/plugins/sp/lib/residual-scan.generated.mjs +66 -0
- package/plugins/sp/lib/run-summary-core.ts +561 -0
- package/plugins/sp/lib/transcript.ts +275 -0
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/references/roles.md +1 -1
- package/plugins/sp/scripts/batch-plan.mjs +335 -0
- package/plugins/sp/scripts/batch-plan.ts +100 -0
- package/plugins/sp/scripts/batch-preflight.mjs +51 -3
- package/plugins/sp/scripts/batch-preflight.ts +72 -1
- package/plugins/sp/scripts/commit-guard.mjs +216 -0
- package/plugins/sp/scripts/commit-guard.ts +249 -0
- package/plugins/sp/scripts/history-anatomy-cache.mjs +5 -0
- package/plugins/sp/scripts/inline-run-dispatch.mjs +130 -0
- package/plugins/sp/scripts/inline-run-dispatch.ts +135 -0
- package/plugins/sp/scripts/inline-run-setup.mjs +563 -91
- package/plugins/sp/scripts/inline-run-setup.ts +65 -97
- package/plugins/sp/scripts/persist-out-check.mjs +90 -41
- package/plugins/sp/scripts/persist-out-check.ts +112 -57
- package/plugins/sp/scripts/quality-gate.mjs +30407 -73
- package/plugins/sp/scripts/quality-gate.ts +21 -5
- package/plugins/sp/scripts/residual-scan.mjs +88 -38
- package/plugins/sp/scripts/residual-scan.ts +35 -38
- package/plugins/sp/scripts/run-summary.mjs +693 -0
- package/plugins/sp/scripts/run-summary.ts +105 -0
- package/plugins/sp/scripts/session-timeline.mjs +331 -0
- package/plugins/sp/scripts/session-timeline.ts +204 -0
- package/plugins/sp/scripts/wrapup-steps.mjs +150 -2
- package/plugins/sp/scripts/wrapup-steps.ts +203 -1
- package/plugins/sp/skills/brainstorm/SKILL.md +9 -47
- package/plugins/sp/skills/branch-workflow/SKILL.md +1 -0
- package/plugins/sp/skills/branch-workflow/references/worktree-patterns.md +1 -0
- package/plugins/sp/skills/code-implementation/SKILL.md +48 -1
- package/plugins/sp/skills/code-verification/SKILL.md +8 -4
- package/plugins/sp/skills/code-verification/references/secu-review.md +3 -0
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +68 -0
- package/plugins/sp/skills/fleet-join/SKILL.md +63 -0
- package/plugins/sp/skills/session-review/SKILL.md +77 -24
- package/plugins/sp/skills/source-driven-development/SKILL.md +5 -0
- package/plugins/sp/skills/spec-decomposition/references/decomposition.md +5 -3
- package/plugins/sp/skills/spur-cli/SKILL.md +6 -2
- package/plugins/sp/skills/spur-cli/references/agent.md +124 -17
- package/plugins/sp/skills/spur-cli/references/decision.md +98 -0
- package/plugins/sp/skills/spur-cli/references/message.md +7 -7
- package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +41 -3
- package/plugins/sp/skills/spur-cli/references/tasks.md +10 -7
- package/plugins/sp/skills/spur-cli/references/workflows/authoring-workflows.md +7 -0
- package/plugins/sp/skills/spur-cli/references/workflows.md +7 -2
- package/plugins/sp/skills/spur-composer/SKILL.md +28 -2
- package/plugins/sp/skills/spur-dev/SKILL.md +18 -14
- package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +6 -3
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +49 -14
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +51 -18
- package/plugins/sp/skills/spur-dev/references/execution-batch-continuation.md +68 -0
- package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +194 -0
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +113 -963
- package/plugins/sp/skills/spur-dev/references/execution-parallel-isolation.md +130 -0
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +20 -0
- package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +471 -0
- package/plugins/sp/skills/spur-dev/references/execution-worktree-setup.md +379 -0
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +51 -9
- package/plugins/sp/skills/spur-dev/references/glossary.md +2 -2
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +361 -156
- package/plugins/sp/skills/spur-dev/references/planning-workflow.md +6 -0
- package/plugins/sp/skills/spur-dev/references/structured-trace-emission.md +178 -0
- package/plugins/sp/skills/wayfinder/SKILL.md +10 -14
- package/schemas/decision-catalog.schema.json +226 -0
- package/schemas/spur-config.schema.json +25 -0
- package/schemas/state-machine-workflow.schema.json +23 -0
- package/spur.js +28165 -19051
- package/web/_astro/{BoardApp.i4eh_fHA.js → BoardApp.D66k_s_T.js} +1 -1
- package/web/_astro/BoardApp.D6sjAJr_.js +324 -0
- package/web/_astro/{TaskDetail.C5Cu8LdB.js → TaskDetail.BynLnMNc.js} +1 -1
- package/web/_astro/{arc.Bu5XpBEz.js → arc._BUadrdH.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.9vHQTvhF.js → architectureDiagram-3BPJPVTR.Dh7xS1Pp.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.Cn2Xl7D6.js → blockDiagram-GPEHLZMM.DFDVpfXm.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.7o0P_QDo.js → c4Diagram-AAUBKEIU.V7_3_glh.js} +1 -1
- package/web/_astro/channel.DZVfto-r.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.C3zZFsNt.js → chunk-2J33WTMH.De1yx7Jz.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.CNy_M7fO.js → chunk-4BX2VUAB.BDhHFuxx.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.2TiPnLBL.js → chunk-55IACEB6.DDLkWnmC.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.5CGSyKAp.js → chunk-727SXJPM.CwfiLnA7.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.w-ZIgfsK.js → chunk-AQP2D5EJ.CtGDmUnM.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.BkcEyhKa.js → chunk-FMBD7UC4.BOLjpw73.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.mxKKbBG4.js → chunk-ND2GUHAM.BDhQls6O.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.cyA0z9aT.js → chunk-QZHKN3VN.76qBynDF.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.CqtUzwhX.js → classDiagram-4FO5ZUOK.N0mpPHT0.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.CqtUzwhX.js → classDiagram-v2-Q7XG4LA2.N0mpPHT0.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.BExPTopI.js → cose-bilkent-S5V4N54A.CKknFfjB.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.Bq2i10Cn.js → cynefin-OW5HDTMX.Cw3ZWBAh.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.By_HsOuv.js → dagre-BM42HDAG.D3yIKCRD.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.BtU0b7hz.js → diagram-2AECGRRQ.D51dO83Z.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.D00ztMdT.js → diagram-5GNKFQAL.Bb-FViDj.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.DMVJWaUN.js → diagram-KO2AKTUF.D3mN8S6x.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.Cxqrjg5-.js → diagram-LMA3HP47.dMMyPcbJ.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.2lz_1B5h.js → diagram-OG6HWLK6.AzbvzY4C.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.BVQEuJ3T.js → erDiagram-TEJ5UH35.BR5I4OZb.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.Qlyo90ky.js → flowDiagram-I6XJVG4X.DFqvhGkn.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.DRwb8HHZ.js → ganttDiagram-6RSMTGT7.ByJbhH6J.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.YfoPthfS.js → gitGraphDiagram-PVQCEYII.CInHjQR3.js} +1 -1
- package/web/_astro/index.COh_kBxN.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.DvFREck1.js → infoDiagram-5YYISTIA.Cm0dGVjJ.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.BzCj9Q0-.js → ishikawaDiagram-YF4QCWOH.C3nOP6Sj.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.BDMT_AFb.js → journeyDiagram-JHISSGLW.4IvLa2MW.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.D6Fh3gj9.js → kanban-definition-UN3LZRKU.DS87Cl-k.js} +1 -1
- package/web/_astro/{linear.Dxw00fGQ.js → linear.MzmRrNER.js} +1 -1
- package/web/_astro/{mermaid.core.CsoxUWa0.js → mermaid.core.fK-WNswS.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.CN8oeHLu.js → mindmap-definition-RKZ34NQL.BqipOHhY.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.C7ADqQ_7.js → pieDiagram-4H26LBE5.0nBiPqFG.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.Cw9lKyHP.js → quadrantDiagram-W4KKPZXB.Szj7Zpx5.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.B1ni-z4q.js → requirementDiagram-4Y6WPE33.D4UR6TWo.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.BhexdjVr.js → sankeyDiagram-5OEKKPKP.DAE7BdOs.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.C3Rm7dpQ.js → sequenceDiagram-3UESZ5HK.BP9ZVq25.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.CkuMt27u.js → stateDiagram-AJRCARHV.BQW7mIxk.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.Dc_aazUA.js → stateDiagram-v2-BHNVJYJU.PbK4nDih.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.ja7sUFhH.js → timeline-definition-PNZ67QCA.Ci_GvUpo.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.BxIshX_K.js → vennDiagram-CIIHVFJN.DNw1Y5qx.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.DfHlC0sh.js → wardleyDiagram-YWT4CUSO.CwlkaaNe.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.DSnhHwCh.js → xychartDiagram-2RQKCTM6.MI6ZuiH_.js} +1 -1
- package/web/board-runtime.json +1 -1
- package/web/index.html +2 -2
- package/web/_astro/BoardApp.BVp-LYi6.js +0 -322
- package/web/_astro/channel.6IFIKBhu.js +0 -1
- package/web/_astro/index.CbNz17Sx.css +0 -1
|
@@ -19,6 +19,17 @@ the interpreter and trace instructions must match. Never hand-edit the installed
|
|
|
19
19
|
|
|
20
20
|
**Retirement criterion (0755 R5, D8 decision D7):** the per-task interpreter retires once the engine covers per-task execution for `/sp:dev-runall` with real terminal runs **and** the parity check (this doc's documented action/guard set ≡ the resolved action/guard set of every `.spur/workflows/*.yaml`) is green. Recording the criterion is part of this task; acting on it is not — that is a separate A3-gate decision.
|
|
21
21
|
|
|
22
|
+
## Driver state checklist (re-read this after a compaction)
|
|
23
|
+
|
|
24
|
+
- **Run id** — the run row `inline-run-setup` persisted (`.spur/run/<run-id>-inline-setup.json`
|
|
25
|
+
sidecar, `.spur/memory/runs/<run-id>.md` + `.state.json`); never re-derive it from the transcript.
|
|
26
|
+
- **Markers under `.spur/run/`** — `<run-id>-inline-setup.json` (frozen invocation identity),
|
|
27
|
+
`<run-id>-tree-start.json` (commit-guard fingerprint), `<wbs>-verdict.json` (per-stage verdict),
|
|
28
|
+
`<wbs>-question.md` / `<wbs>-escalation.md` (operator pause), `<run-id>-event-trace.md`.
|
|
29
|
+
- **Re-read per state** — this file (Run setup → YAML interpreter → Record & done),
|
|
30
|
+
[structured-trace-emission.md](structured-trace-emission.md) at the first trace event.
|
|
31
|
+
- **Authoritative state** — the run record and verdict artifacts, never the host todo list.
|
|
32
|
+
|
|
22
33
|
## Supported action and guard set (0755 R2 parity contract)
|
|
23
34
|
|
|
24
35
|
The action and guard kinds this driver implements. The parity check
|
|
@@ -51,7 +62,7 @@ command, skill, script, or second workflow.
|
|
|
51
62
|
|
|
52
63
|
**Inline HITL defer semantics (0911):** bundled pipeline human gates ship `decision: {mode:
|
|
53
64
|
never}`, so the inline driver always prompts the operator for those gates regardless of
|
|
54
|
-
`
|
|
65
|
+
`workflows.hitlDecisionMaker` — the decision policy never answers inline pipeline gates. A local
|
|
55
66
|
workflow override (`.spur/workflows/*.yaml`) may declare `mode: evidence`, but the driver does
|
|
56
67
|
not claim full inline parity for the DecisionMaker path: evidence-mode auto-answering inside the
|
|
57
68
|
inline driver is an explicit non-goal (subprocess runs own that behavior). No hot reload: config
|
|
@@ -59,16 +70,18 @@ and YAML changes require restarting the invoking process.
|
|
|
59
70
|
|
|
60
71
|
## Run setup
|
|
61
72
|
|
|
62
|
-
**Shared startup contract (task 0814 R1/R3/R4/R6/R7).** The order is
|
|
63
|
-
**
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
73
|
+
**Shared startup contract (task 0814 R1/R3/R4/R6/R7; publish-first 1105 R1).** The order is
|
|
74
|
+
load-bearing: publish the **generated plan** first — resolve the selected workflow through the same
|
|
75
|
+
project/bundled resolver as execution, run `spur workflow show <resolved-file> --no-logo --format
|
|
76
|
+
todo --json`, and publish the `.plan` rows verbatim (the projection reads the definition and executes
|
|
77
|
+
nothing, so it is safe before readiness and isolation); run quick deterministic readiness (admission,
|
|
78
|
+
not an implementation certificate) next; when `--worktree` is valid, create/adopt and switch to the
|
|
79
|
+
execution tree; then bind the published plan to the run's persisted `__definitionDigest` — and only
|
|
80
|
+
then read the full YAML for comprehensive/model work. Comprehensive checks stay at their owning
|
|
81
|
+
boundaries and run after the plan is visible and after isolation when requested (R7); quick readiness
|
|
82
|
+
and plan projection dispatch zero models and execute zero workflow actions (R8). Labels come from the
|
|
83
|
+
projection (or `columnLabel` in `packages/app/src/workflow/step-reporter.ts` for the `steps[]`
|
|
84
|
+
fallback) — labels are display addresses only, never an execution key.
|
|
72
85
|
|
|
73
86
|
1. Resolve the command inputs, `--auto`, and any explicit `--vars` — **without reading the selected
|
|
74
87
|
YAML yet**. An explicit non-inline executor selection chooses the subprocess workflow path.
|
|
@@ -93,6 +106,20 @@ the human/native presentation layer — labels are display addresses only, never
|
|
|
93
106
|
|| { echo "inline run setup failed closed — checker not found; run 'superskill install sp --marketplace gobing-ai/spur'" >&2; exit 1; }
|
|
94
107
|
```
|
|
95
108
|
|
|
109
|
+
**Commit fingerprint (1129 R1).** Fingerprint the tree the task commit will land in — before the
|
|
110
|
+
first task write — because Record & done stages through this snapshot, so a concurrent writer's
|
|
111
|
+
file cannot ride into the task commit. The artifact is cwd-relative under `.spur/run/` (gitignored
|
|
112
|
+
per tree), so the snapshot and the commit it guards must name the SAME tree: without `--worktree`
|
|
113
|
+
take it here in the setup window; with `--worktree` take it immediately **after step 7's
|
|
114
|
+
isolation, from the worktree root** (still before the first task write) — a snapshot taken here
|
|
115
|
+
would land in the invoking tree, and the worktree's commit would find none (1129 review N1).
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
RUN_ID="<marker-id>" # the WT-3 marker's id (execution-batch.md), or the run id allocated at setup
|
|
119
|
+
GUARD=plugins/sp/scripts/commit-guard.ts; [ -f config/plugin-scripts.json -a -f "$GUARD" ] || GUARD="$(superskill script path sp commit-guard.mjs 2>/dev/null)";
|
|
120
|
+
bun "$GUARD" start --run "$RUN_ID" # <this tree>/.spur/run/$RUN_ID-tree-start.json = { head, dirty[] }
|
|
121
|
+
```
|
|
122
|
+
|
|
96
123
|
The delegate uses the source app service when the SPUR_BIN chain identifies a checkout;
|
|
97
124
|
otherwise it loads the generated application bundle shipped with the plugin. Both setup paths
|
|
98
125
|
obtains the selected definition from the existing CLI `workflow show --format todo --json`
|
|
@@ -128,10 +155,21 @@ the human/native presentation layer — labels are display addresses only, never
|
|
|
128
155
|
`session` and the Codex key `session_id` (in that order). If neither is available, allocate
|
|
129
156
|
`host-session-<run-id>` and record that fallback in the log; provenance must never be blank or
|
|
130
157
|
guessed from an executor subprocess.
|
|
131
|
-
5. **Publish the
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
158
|
+
5. **Publish the generated plan (R1, 1105) — the run's first action.** Run `spur workflow show
|
|
159
|
+
<resolved-file> --no-logo --format todo --json` and publish the projection into the host todo list
|
|
160
|
+
before any expensive check:
|
|
161
|
+
- **Annotated workflow (the normal case):** publish every `.plan` row's `text` **verbatim** —
|
|
162
|
+
`A Prepare`, `A1 Quick readiness`, `A2 Prepare Git`, `A3 Publish plan`, `B Implement`, … — in
|
|
163
|
+
plan order. Never hand-write rows, never copy the YAML state table into prose, and never
|
|
164
|
+
renumber: the labels carry identity.
|
|
165
|
+
- **Unannotated workflow (`.plan` is `null`):** publish the flat `steps[]` inventory in
|
|
166
|
+
declaration order with `columnLabel` labels and their `initial` / `terminal` / `failure` /
|
|
167
|
+
`pause` / `loopBack` / `conditional` markers. Never re-derive this list from the YAML.
|
|
168
|
+
- **No workflow plan at all** (skill-only refine/verify batches): keep the existing host
|
|
169
|
+
preparation rows; do not fabricate a workflow YAML.
|
|
170
|
+
|
|
171
|
+
Publishing executes nothing, so it safely precedes readiness and isolation, which keep their
|
|
172
|
+
existing order below.
|
|
135
173
|
6. **Quick deterministic readiness (R2), before isolation.** Evaluate `quickReadiness` from
|
|
136
174
|
`plugins/sp/scripts/batch-preflight.ts` with the operation, status, filtered-set size, the
|
|
137
175
|
matrix-selected required/present sections, and content-policy findings. Record the outcome
|
|
@@ -146,19 +184,21 @@ the human/native presentation layer — labels are display addresses only, never
|
|
|
146
184
|
inherited**: a host shell call starts in an arbitrary directory and carries no cwd from the
|
|
147
185
|
previous call, so every later call runs inside the **Per-call execution-tree pin** protocol
|
|
148
186
|
below (task 1058), which consumes the identity confirmed here.
|
|
149
|
-
8. **
|
|
150
|
-
|
|
151
|
-
`spur workflow show <resolved-file> --no-logo --format todo --json`. Validate the projection with
|
|
152
|
-
`parseWorkflowInventory` and bind it to the run's persisted `__definitionDigest` with
|
|
187
|
+
8. **Bind the published plan to the run digest (R4) — plan row A3.** Validate the step-5 projection
|
|
188
|
+
with `parseWorkflowInventory` and bind it to the run's persisted `__definitionDigest` with
|
|
153
189
|
`assertInventoryIdentity` — a drift or projection failure stops the run before any
|
|
154
|
-
comprehensive/model work, never executing with a misleading plan.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
the active state. Never re-derive this list from the YAML.
|
|
190
|
+
comprehensive/model work, never executing with a misleading plan. The A rows published at step 5
|
|
191
|
+
track this setup order: A1 = quick readiness (step 6), A2 = Git/isolation (step 7), A3 = this
|
|
192
|
+
binding; mark each row completed as its step settles.
|
|
158
193
|
9. **Read the selected YAML and overlay its `vars` defaults** with the invocation values. Compare the
|
|
159
194
|
resolved definition identity against the bound `__definitionDigest`; a mismatch is identity drift
|
|
160
195
|
and fails closed (step 8 already caught projection-side drift; this re-checks the same definition
|
|
161
196
|
the interpreter will execute).
|
|
197
|
+
- **Runtime-written vars are re-read, never cached.** A var a later state writes (`proofDigest`,
|
|
198
|
+
`proofDigestNow`, `taskSpecPath`, `featureSpecPath`, `taskPriority`, `mode`, `gateFindings`) is a
|
|
199
|
+
run artifact, not a YAML default: re-resolve it from `.spur/run/`/`.spur/memory/runs/` at every
|
|
200
|
+
state boundary, and never let a host-side copy of the invocation vars shadow it — a stale empty
|
|
201
|
+
`proofDigest` silently fails the bound `run.artifact` equivalence and the `record` proof guard.
|
|
162
202
|
- **Layer 2** = the active state's `onEnter` actions (`kind` + resolved `input`/`command`), from
|
|
163
203
|
the YAML read here, shown only for the active state.
|
|
164
204
|
- **Refresh cadence** = stage boundaries only (when the current state changes after a transition),
|
|
@@ -171,15 +211,25 @@ the human/native presentation layer — labels are display addresses only, never
|
|
|
171
211
|
ended 0/11 with precheck and implement still open).
|
|
172
212
|
- **Source of truth** = the CLI projection for layer 1; the YAML read here for layer 2.
|
|
173
213
|
Never hand-copy or hand-derive the state list into the driver, a command, a skill, or a script.
|
|
174
|
-
- **Stable labels (task 0814 R5
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
214
|
+
- **Stable labels (task 0814 R5; A–Z / 1–9 rule, 1105 R1).** Labels come from the published
|
|
215
|
+
`.plan`: single letters A–Z for top-level rows, digits 1–9 for children under one letter. The
|
|
216
|
+
projection enforces that cap instead of rolling to two-letter labels (`planLetter` throws past
|
|
217
|
+
Z — split into waves or stop; never render `AA`-style addresses). An `on-entry` state is
|
|
218
|
+
inserted as the next digit the moment the host enters it (`insertOnEntry` in
|
|
219
|
+
`packages/app/src/workflow/plan-projection.ts`), appended after its letter's existing children;
|
|
220
|
+
re-entering an already-published loop state keeps its label and appends the re-entry note
|
|
221
|
+
` — attempt N`. Labels never replace the canonical step id, and are re-derived identically on
|
|
222
|
+
retry/resume against the same definition.
|
|
223
|
+
- **Truthful progress (task 0814 R6; frozen status mapping, 1105 R1).** Publish pending/active
|
|
224
|
+
state before the visible item starts, then update it immediately after the observed item
|
|
225
|
+
completes and before the next item starts. Frozen status mapping: `pending → pending`,
|
|
226
|
+
`active → in_progress`, `completed → completed`,
|
|
227
|
+
`skipped|failed|unattempted|blocked → pending` + ` [<outcome>]` — the four engine-decided
|
|
228
|
+
outcomes stay visible on a pending row, never cleared. Keep completed, skipped, failed, blocked,
|
|
229
|
+
paused, and unattempted outcomes distinct; never mark skipped/conditional work completed merely
|
|
230
|
+
to clear the UI. Re-entry appends ` — attempt N`. If the host has no suitable native todo tool,
|
|
231
|
+
or it fails, use the `renderProgressMarkdown` fallback with the same labels and truth — never a
|
|
232
|
+
fabricated successful tool invocation.
|
|
183
233
|
10. For task execution only, record lifecycle provenance before entering the FSM:
|
|
184
234
|
|
|
185
235
|
```bash
|
|
@@ -195,6 +245,25 @@ the human/native presentation layer — labels are display addresses only, never
|
|
|
195
245
|
worktree first and run this command (and every lifecycle-touching command: `task record`,
|
|
196
246
|
`task update <wbs> testing|done`) from the worktree root.
|
|
197
247
|
|
|
248
|
+
### Host todo update styles (1105 R2)
|
|
249
|
+
|
|
250
|
+
The same published plan is updated differently per host family. Both styles keep plan order and are
|
|
251
|
+
bookkeeping for the one projection — never a second source of truth:
|
|
252
|
+
|
|
253
|
+
| Host | Tool(s) | Update style |
|
|
254
|
+
| --- | --- | --- |
|
|
255
|
+
| Claude Code | `TaskCreate` / `TaskUpdate` | per-item — create each item once, then update by id; append inserted (`on-entry`) steps at the end (the label carries identity, so list position is display only) |
|
|
256
|
+
| pi | `todo` | per-item — same create-once/update-by-id discipline |
|
|
257
|
+
| Codex | `update_plan` | full-list — rewrite the whole list in plan order on every update |
|
|
258
|
+
| Gemini | `write_todos` | full-list — rewrite the whole list in plan order |
|
|
259
|
+
| OpenCode | `todowrite` | full-list — rewrite the whole list in plan order |
|
|
260
|
+
| Grok | `todo_write` | full-list — rewrite the whole list in plan order |
|
|
261
|
+
| omp | host todo tool | maps plan letters to `phase` |
|
|
262
|
+
| any host without a native tool | `renderProgressMarkdown` | Markdown fallback with the same labels and truth |
|
|
263
|
+
|
|
264
|
+
Per-item hosts update the one row whose outcome changed; full-list hosts re-render every row from the
|
|
265
|
+
current plan state. Both render the frozen status mapping above identically.
|
|
266
|
+
|
|
198
267
|
### Idea-pipeline quick start (0887 R1/R2)
|
|
199
268
|
|
|
200
269
|
Minimum files to read for `/sp:dev-idea` inline runs — then drive `idea-pipeline.yaml`:
|
|
@@ -271,6 +340,10 @@ Rules (R1):
|
|
|
271
340
|
first, then verify the physical path and repository identity.
|
|
272
341
|
- **Native tool cwd options are additive.** A tool-level cwd/`cd` pin may additionally set the
|
|
273
342
|
call's directory, but never replaces the identity checks above.
|
|
343
|
+
- **Dispatch cwd is mandatory when the tool accepts one.** A subagent dispatch whose tool exposes a
|
|
344
|
+
`cwd` (pi `subagent`) passes the execution-tree cwd. Omitted, the child starts in the host tree:
|
|
345
|
+
it can edit main, and the timeout watchdog diffs the wrong tree (H15 2026-10-07: "changed
|
|
346
|
+
tracked files: none" on a worker that changed 5 worktree files).
|
|
274
347
|
- **Host-only scope.** Engine `shell` actions already bind `context.workdir`
|
|
275
348
|
(`packages/app/src/workflow/actions/shell.ts:98`) — leave them alone. No service-wide
|
|
276
349
|
`process.chdir`, no new public flag, no helper framework.
|
|
@@ -309,8 +382,6 @@ entry and `.spur/memory/runs/runall-D63-2ebbd97c.md`.
|
|
|
309
382
|
|
|
310
383
|
## Comprehensive-check retention and evidence (R7/R8)
|
|
311
384
|
|
|
312
|
-
## Comprehensive-check retention and evidence (R7/R8)
|
|
313
|
-
|
|
314
385
|
**R7 — comprehensive checks stay at their owning boundaries.** Quick readiness and plan projection are
|
|
315
386
|
admission and visibility, not a substitute for the owning gates. After the plan is visible and after
|
|
316
387
|
isolation when requested, retain the full task/feature integrity, size, evidence-channel,
|
|
@@ -342,9 +413,13 @@ or terminal close. Measure its actual duration and preserve its declared failure
|
|
|
342
413
|
|
|
343
414
|
```bash
|
|
344
415
|
bun "$SETUP_SCRIPT" --action --run-id "$RUN_ID" --node <state-id> --kind <action-kind> \
|
|
345
|
-
--status <done|failed> --ok <true|false> --duration-ms <measured-ms>
|
|
416
|
+
--status <done|failed> --ok <true|false> --duration-ms <measured-ms> [--estimated]
|
|
346
417
|
```
|
|
347
418
|
|
|
419
|
+
`--duration-ms` is the wall clock measured around the action. Pass `--estimated` when you did
|
|
420
|
+
**not** time it — for example a duration reconstructed after a subagent returned — so the row is
|
|
421
|
+
labelled instead of presented as measured. `--estimated` is valid only with `--action`.
|
|
422
|
+
|
|
348
423
|
For a multi-action state, `--actions-file` may emit the measured boundaries together before leaving
|
|
349
424
|
that state. Follow [Structured trace emission](#structured-trace-emission-adr-117-task-0868) for the
|
|
350
425
|
payload, delegated `decide` emission and best-effort failure handling. Never retry a failed batch
|
|
@@ -354,9 +429,30 @@ Action semantics come from the YAML and the workflow action contract:
|
|
|
354
429
|
|
|
355
430
|
- `shell` — run the expanded command in the project working tree with resolved vars exported as
|
|
356
431
|
environment variables. A non-zero result follows the action's existing failure policy.
|
|
432
|
+
**Long commands must not hold the host call (session finding 2026-10-09).** A `shell` action whose
|
|
433
|
+
command can outlive the host's per-call limit — the full quality gate, a repo-wide suite, any
|
|
434
|
+
`bun run spur-check`-class gate — is started **detached and polled**, never held in one foreground
|
|
435
|
+
call: redirect to a run-scoped file and disown it
|
|
436
|
+
(`nohup <cmd> > .spur/run/<wbs>-gate.out 2>&1 < /dev/null & disown`), then observe it in bounded
|
|
437
|
+
calls (`pgrep`, the declared `.status` file, the log tail). A call killed at the cap leaves the
|
|
438
|
+
substrate's own FAIL receipt — for the quality gate, `.spur/run/<wbs>-check-receipt.json` — and the
|
|
439
|
+
recheck's `no-progress` guard honours it, so a cap-kill reads as a red gate the next time. Preserve
|
|
440
|
+
the signal-induced receipt under a distinct name (`<wbs>-check-receipt.false-fail-<signal>.json`)
|
|
441
|
+
and say so in the run log before re-running: never delete it silently, and never report a
|
|
442
|
+
cap-killed attempt as a genuine gate failure. Measured this session: one foreground gate call
|
|
443
|
+
(25.6 min host-wide lock wait + 7.4 min runtime) was SIGTERM-killed at the host cap and produced
|
|
444
|
+
exactly that false FAIL receipt.
|
|
357
445
|
- `note` — append the expanded message to the inline run log.
|
|
358
446
|
- `doctor.probe` — run the declared Spur doctor once, persist its status file, and apply any
|
|
359
447
|
`setVars` result (including a resolved executor) before the next action or state.
|
|
448
|
+
**Role-map mode has no inline surface (task 1088 session finding).** A `doctor.probe` that
|
|
449
|
+
declares `roles` resolves them through `AgentService.resolve`, which is reachable only in-process
|
|
450
|
+
(the engine composition injects the service); no CLI verb exposes it, so the host session cannot
|
|
451
|
+
reproduce `__executor.<role>` pins. The driver therefore probes the host-session agent instead,
|
|
452
|
+
writes that verdict to the declared status file, and records one run-log line naming the
|
|
453
|
+
substitution. Nothing downstream degrades: `__executor.<role>` pins are consumed by the
|
|
454
|
+
subprocess dispatch path, and the inline path executes its stages in the host session without
|
|
455
|
+
reading them. Never fabricate pins to satisfy the shape, and never skip the status file.
|
|
360
456
|
- `file.read.into-var` — read the declared file into the declared run variable before subsequent
|
|
361
457
|
actions/guards.
|
|
362
458
|
- `hitl.confirm` — under `profile=auto`, follow the YAML's auto-skip transition. Otherwise pause,
|
|
@@ -381,8 +477,10 @@ Action semantics come from the YAML and the workflow action contract:
|
|
|
381
477
|
`answerFile`; assert `expectFile`; honor `escalationFile` (a non-empty file after exit 0 means
|
|
382
478
|
the agent paused on an operator question — treat the attempt as succeeded-with-escalation and
|
|
383
479
|
skip `requireDiff` for that attempt only); enforce `requireDiff` against a pre-action git snapshot,
|
|
384
|
-
including the task-scope guard; honor declared error policy.
|
|
385
|
-
applicable because the host session has no independent kill boundary
|
|
480
|
+
including the task-scope guard; honor declared error policy. A host-inline action records
|
|
481
|
+
`timeoutMs` as not applicable because the host session has no independent kill boundary; a
|
|
482
|
+
dispatched subagent passes it to the host when the dispatch tool accepts a timeout (see
|
|
483
|
+
§ Timeout boundary).
|
|
386
484
|
- `run.artifact` — the engine's ledger registration has **no inline execution surface** (0808 R4).
|
|
387
485
|
The inline equivalent is a documented **registration-equivalent convention**: before the record
|
|
388
486
|
state mutates the task, the host validates the same refusal conditions inline — the declared
|
|
@@ -453,6 +551,49 @@ reformulate the prose into a command, spawn a subagent for it, or silently promo
|
|
|
453
551
|
Beyond the deterministic `estimate_hours` floor in condition 5, no token estimate, model heuristic,
|
|
454
552
|
or configuration switch is added.
|
|
455
553
|
|
|
554
|
+
**Contingent fallback (task 1134 R1/R2) — capacity is not capability.** That fallback is correct for
|
|
555
|
+
capability-shaped failures and **wrong when the exhausted resource is the host session's own
|
|
556
|
+
provider quota**: re-selecting the same exhausted provider cannot succeed by construction, and the
|
|
557
|
+
run collapses into one serial host worker with no executor diversity. So classify the pre-dispatch
|
|
558
|
+
failure before falling back:
|
|
559
|
+
|
|
560
|
+
```bash
|
|
561
|
+
DISPATCH_SCRIPT=plugins/sp/scripts/inline-run-dispatch.ts; [ -f config/plugin-scripts.json -a -f "$DISPATCH_SCRIPT" ] || DISPATCH_SCRIPT="$(superskill script path sp inline-run-dispatch.mjs 2>/dev/null)"
|
|
562
|
+
bun "$DISPATCH_SCRIPT" --dispatch-failure --run-id "$RUN_ID" --stage <state-id> --decision <escalate|stop> \
|
|
563
|
+
--text-file <bounded failure record> [--executor <name>] [--agent <a>] [--model <m>]
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
The helper wraps the upstream `@gobing-ai/ts-ai-runner` classifier (Spur matches **no** provider
|
|
567
|
+
code or quota vocabulary of its own) and writes `.spur/run/<run-id>-dispatch-fallback.json` as
|
|
568
|
+
`{stage, class, reason?, resetAt?, decision, executor, attribution, observedAt}`. Read `.class` from
|
|
569
|
+
that file — never judge the prose yourself.
|
|
570
|
+
|
|
571
|
+
- **`class: capability`** (generic 429, rate limit, overload, auth failure, timeout, missing
|
|
572
|
+
permission, missing capability, non-dispatch-eligible prose, below the size floor) → today's
|
|
573
|
+
fallback, unchanged: execute the stage once in the host session and log
|
|
574
|
+
`stage <id> executed inline in session <session-id>`.
|
|
575
|
+
- **`class: capacity`** (a confirmed exhausted usage allowance or credit balance) → the stage is
|
|
576
|
+
**never** re-executed in the orchestrating host session. Hand it to the subprocess dispatch path
|
|
577
|
+
(`--agent auto|name` resolves an executor through the registry and can pick another rung); when
|
|
578
|
+
that path is unavailable, terminate the run at `failed` with `terminalReason: failed-agent`,
|
|
579
|
+
naming the executor and the record's `resetAt` when it carries one. Record
|
|
580
|
+
`stage <id> capacity exhaustion: <executor> <reason> reset <resetAt|unknown> — escalate|stop`.
|
|
581
|
+
|
|
582
|
+
**Attribution before dispatch (task 1134 R3).** Immediately before each inline `agent.run` stage,
|
|
583
|
+
record the executor identity the stage will use, so a later classified exhaustion can be attributed
|
|
584
|
+
to a rung configuration can disable:
|
|
585
|
+
|
|
586
|
+
```bash
|
|
587
|
+
bun "$DISPATCH_SCRIPT" --attribution --run-id "$RUN_ID" --stage <state-id> \
|
|
588
|
+
[--executor <name>] [--agent <name>] [--model <m>] # omit --executor for the no-attribution marker
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
This appends one line to `.spur/run/<run-id>-attribution.jsonl` and one run-log line
|
|
592
|
+
(`stage <id> attribution: <executor|none>`). When no named executor resolves, pass no `--executor`:
|
|
593
|
+
the ledger records the explicit `executor: null` marker — never a silent drop. An unattributed stage
|
|
594
|
+
is observable but cannot write configuration (executor-availability §4), and `spur agent doctor
|
|
595
|
+
--json` counts those stages as `inlineFailFast.unattributedStages`.
|
|
596
|
+
|
|
456
597
|
**Pre-dispatch permission check (2026-09-15 subagent-dispatch evaluation).** Claude Code exposes no
|
|
457
598
|
dry-run permission API, so the contract is **name-capabilities + fail-fast blocker** — never a
|
|
458
599
|
permission-probing subsystem. Before dispatching a stage, the driver names the stage's required
|
|
@@ -498,13 +639,30 @@ boundary, and a delegate left to re-derive them re-derives them against its own
|
|
|
498
639
|
**owning stage's artifact contract** — for a verify stage, the compact contract below.
|
|
499
640
|
6. **The implement-stage acceptance-evidence requirement** — for the implement stage and any stage
|
|
500
641
|
whose YAML action declares `requireDiff`. A `requireDiff` stage is the one whose delegate writes
|
|
501
|
-
the deliverable, so its handoff carries
|
|
642
|
+
the deliverable, so its handoff carries four components:
|
|
502
643
|
(a) **the task's AC identities verbatim** — the exact `Scenario:` titles / checklist text read by
|
|
503
644
|
the driver from the task file, never paraphrased;
|
|
504
645
|
(b) **required evidence** — the pasted output of the narrow targeted tests the delegate ran, and
|
|
505
646
|
a `file:line` change map written into the task's `## Solution` section;
|
|
506
647
|
(c) the reminder that **a delegate success message is not evidence** — post-join validation reads
|
|
507
|
-
the artifacts and the diff, not the delegate's claims
|
|
648
|
+
the artifacts and the diff, not the delegate's claims;
|
|
649
|
+
(d) **the public-surface probe requirement (task 1135 R1/R2/R6).** Before the stage reports done
|
|
650
|
+
it runs at least one end-to-end probe of each new or changed **public surface** — a CLI
|
|
651
|
+
verb/flag, an HTTP route, or a plugin script entry — against a throwaway project root (never the
|
|
652
|
+
repo corpus, never the execution tree's tracked files), and reports the exact command plus its
|
|
653
|
+
observed output as acceptance evidence. A service-layer test suite is **not** a substitute. The
|
|
654
|
+
probe is bounded: 120 s per invocation, output capped at 8 KiB, no network beyond the CLI under
|
|
655
|
+
test, and a probe failure is evidence, never a corpus mutation.
|
|
656
|
+
A stage that changed no public surface returns instead the single line
|
|
657
|
+
`probe: not-applicable (no public surface changed)`.
|
|
658
|
+
The stage's own changed-path static self-check (task 1135 R8) also runs here: `spur rule run`
|
|
659
|
+
with the configured pre-check preset over the changed paths, `bun run script-contract-check`
|
|
660
|
+
when a plugin script was touched, and the paired-script parity test when `package.json` changed —
|
|
661
|
+
so an env-var-hygiene violation, a stale generated twin, or a half-updated paired chain is
|
|
662
|
+
named by the stage rather than discovered by the pipeline `test` hop.
|
|
663
|
+
**A stage that returns neither probe evidence nor the not-applicable line is not silently
|
|
664
|
+
complete (task 1135 R2): the driver records the stage `done` with a `probe-missing` marker in
|
|
665
|
+
the run log and carries the same marker into that task's batch-report row.**
|
|
508
666
|
Component (a) is read from the task by the driver; this extends the payload contract and is NOT a
|
|
509
667
|
new YAML key.
|
|
510
668
|
|
|
@@ -516,6 +674,26 @@ execution tree, and an ad-hoc git worktree created for a comparison is removed b
|
|
|
516
674
|
reports. The G65 batch's implement dispatches left an 867 MB worktree plus a trail of `/tmp` scratch
|
|
517
675
|
files that outlived the run (2026-09-15).
|
|
518
676
|
|
|
677
|
+
**Delegate search discipline.** A delegate searches with bounded scope: `rg -g '!node_modules' …`
|
|
678
|
+
(or an explicit narrow path), never `grep -r .` / `grep -rn --include=*` from a repository root —
|
|
679
|
+
those walk `node_modules` and `.git` and can burn many minutes at full CPU in the wrong tree
|
|
680
|
+
(measured 2026-10-09: a delegate's `grep -rn --include=* .` from the invoking tree root ran 4+ min
|
|
681
|
+
before the orchestrator killed it). The dispatch payload's execution-tree cwd is the only tree a
|
|
682
|
+
delegate searches or edits; a search it cannot bound is a search it should not run.
|
|
683
|
+
|
|
684
|
+
**Driver scratch lives with the run (task 1136 R6).** Every driver scratch file — action-batch
|
|
685
|
+
JSON, captured stage output, temporary status/answer files — lives under `.spur/run/<run-id>/`,
|
|
686
|
+
never `/tmp` or `$TMPDIR`: the OS may reap a temp directory mid-run, and a reaped scratch file
|
|
687
|
+
surfaces as an unrelated downstream failure. Run-scoped files directly under `.spur/run/` keep
|
|
688
|
+
their existing `<run-id>-*` naming (the `no-env-files` rule still bans `.env` names).
|
|
689
|
+
|
|
690
|
+
**Declared vars are checked before a shell action runs (task 1136 R6).** Before executing a `shell`
|
|
691
|
+
action whose command references a declared workflow var (for example `$qualityGateCmd`), the driver
|
|
692
|
+
confirms the var is exported and non-empty; when it is not, the action fails by name —
|
|
693
|
+
`shell action requires $qualityGateCmd but it is empty/unset` — and the command never runs. The
|
|
694
|
+
check is scoped to vars the workflow declares with a value: legitimately empty optional vars
|
|
695
|
+
(`$featureSpecPath` for an orphan task, `$gateFindings` on a green gate) pass unchanged. This turnsa silently empty `$qualityGateCmd` into a named failure instead of a shell that runs nothing.
|
|
696
|
+
|
|
519
697
|
**Verify-stage artifact contract.** A verify handoff names
|
|
520
698
|
[`code-verification/references/verdict-schema.md`](../../code-verification/references/verdict-schema.md)
|
|
521
699
|
as the canonical answer schema and carries this compact form verbatim:
|
|
@@ -529,6 +707,9 @@ Evidence Type = test | command | static-ref | manual-review | llm-judge | n/a
|
|
|
529
707
|
AC rows use the task's exact AC identities (verbatim `Scenario:` titles / checklist text).
|
|
530
708
|
A behavioral AC marked MET requires executable evidence (test | command);
|
|
531
709
|
static-ref or llm-judge alone cannot carry it.
|
|
710
|
+
Evidence anchors are repo-root-relative (`packages/app/src/foo.ts:42`); a basename
|
|
711
|
+
(`foo.ts:42`) is an unresolved anchor and blocks the done projection (`L4.anchor-unresolved`) —
|
|
712
|
+
see `code-verification/references/verdict-schema.md` § Basename-only anchors are unresolved anchors.
|
|
532
713
|
```
|
|
533
714
|
|
|
534
715
|
**AC table shape (0948 R9).** The AC table must be exactly **4 columns** and **cell 3 must hold a
|
|
@@ -580,11 +761,41 @@ grading its own work defeats the stage. A continuation stage also dispatches fre
|
|
|
580
761
|
same-task subagent exists (the earlier stage ran host-inline or below the dispatch floor), when a
|
|
581
762
|
host-owned gate sat between the stages, or when the platform cannot address completed subagents.
|
|
582
763
|
|
|
583
|
-
**
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
764
|
+
**Implement and test-fix workers never run the full gate (task 1127 R8 / task 1142 R3).** Since 1127 the full gate
|
|
765
|
+
takes a host-wide lock (`SPUR_GATE_LOCK_DIR`, default `~/.config/spur/run/full-gate.lock`):
|
|
766
|
+
concurrent full-gate runs serialize, and a manual `spur-check` (wrapped via
|
|
767
|
+
`scripts/commands/gate-lock.ts`) holds the same lock. A dispatched **implement** or `test-fix` child
|
|
768
|
+
that re-ran the full gate would queue behind every other gate on the host and burn its dispatch
|
|
769
|
+
timeout waiting instead of working. Workers use the light tier as their iteration check (task 1142 R3) in exact order:
|
|
770
|
+
1. the **changed-path matrix** (`code-implementation/SKILL.md` § Changed-path targeted checks) over the paths they touched;
|
|
771
|
+
2. the resolved `quality-gate` script in `light` mode (source-repo probe, else the installed twin) (biome, per-workspace typecheck, rule presets, related tests).
|
|
772
|
+
Neither step takes the full-gate lock. The pipeline's `test` and `test-recheck` hops remain the only full gate. Carry
|
|
773
|
+
`--gate-log <path>` through to the child **verbatim**: never paraphrase it into "re-run the named
|
|
774
|
+
gate command". That paraphrase is not cosmetic — it dropped this rule in the H15 run (task
|
|
775
|
+
Background, seq 232) and sent a worker into the locked gate. The pipeline's next `test` (recheck)
|
|
776
|
+
stage re-acquires the lock and delivers the deciding verdict; that deciding run may wait for a live
|
|
777
|
+
holder, so a queued worker's effective budget is dispatch timeout minus observed lock queue wait,
|
|
778
|
+
not the timeout alone.
|
|
779
|
+
|
|
780
|
+
**Null or timed-out dispatch (task 1134 R9).** The dispatch bound stays the existing YAML
|
|
781
|
+
`timeoutMs` (`implementTimeoutMs` / `stepTimeoutMs`), passed to the host dispatch as today — R9 adds
|
|
782
|
+
no new bound. A dispatch that returns **no result**, or that hits that bound, is a **recorded stage
|
|
783
|
+
failure**, not a fallback trigger: record the stage failed with `dispatch: null-result|timeout`,
|
|
784
|
+
follow the stage's normal failure edge (implement's default `fail` policy routes the run to
|
|
785
|
+
`failed`), and start **no** host-inline attempt of that stage. R1–R4 cover a *pre-dispatch* capacity
|
|
786
|
+
refusal (the executor must shift); R9 covers the *dispatched-but-unproductive* case.
|
|
787
|
+
|
|
788
|
+
**Timeout boundary (task 0727, amended by task 1108):** when the host's dispatch tool accepts a
|
|
789
|
+
per-dispatch timeout (pi: subagent `timeoutMs`, default 30 min — pi-subagents `docs/tool-reference.md`
|
|
790
|
+
line 97), the driver passes the stage's resolved YAML
|
|
791
|
+
`timeoutMs` (implement → `implementTimeoutMs`) and records `host timeout <ms> (yaml timeoutMs)`.
|
|
792
|
+
When it does not, a dispatched subagent is governed by
|
|
793
|
+
**the host platform's subagent limit, not the YAML timeoutMs**, and the driver records
|
|
794
|
+
`host timeout <ms> (platform subagent limit)`. `timeoutMs` stays not-applicable for host-inline
|
|
795
|
+
execution only. Either way the driver must
|
|
796
|
+
**record the governing timeout boundary and its source before dispatch** in the run log. Rationale:
|
|
797
|
+
the operator sets one budget in one place; a host silently imposing its own 30 min default is what
|
|
798
|
+
killed the P1 implement dispatches (1096, 1099). If the dispatch reaches that
|
|
588
799
|
boundary, **a dispatch timeout is a started-subagent failure**: the no-replay rule above and the
|
|
589
800
|
stage's declared YAML error policy govern (implement's default `fail` policy routes the run to
|
|
590
801
|
`failed`); it is never a host re-execution. Recovery follows the
|
|
@@ -607,131 +818,48 @@ Run-log stamps (task 0727): every appended line is prefixed with an **ISO-8601 U
|
|
|
607
818
|
(`YYYY-MM-DDTHH:MM:SSZ`, e.g. `2026-08-31T17:51:11Z`); the exact-template provenance lines above
|
|
608
819
|
keep their exact content after the stamp prefix. This normalization is contractual:
|
|
609
820
|
**bare local-clock stamps are prohibited** — a hand-appended `[stage 12:31]` form mixes timezones
|
|
610
|
-
in one file and makes the run unauditable (task 0726
|
|
821
|
+
in one file and makes the run unauditable (task 0726).
|
|
611
822
|
|
|
612
823
|
## Structured trace emission (ADR-117, task 0868)
|
|
613
824
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
observability is a property of the run, so the inline driver owes the same structured trace the
|
|
617
|
-
engine subprocess writes — and it owes it through the **same writer**, never a parallel
|
|
618
|
-
implementation. The shared writer is `WorkflowActionTraceWriter`
|
|
619
|
-
(`packages/app/src/workflow/action-trace.ts`): the same decorator the engine composition installs
|
|
620
|
-
around `DbWorkflowPersistenceAdapter`, so the two surfaces call one emission path and one run-row
|
|
621
|
-
closure path and cannot drift.
|
|
825
|
+
Read [structured-trace-emission.md](structured-trace-emission.md) when the first trace event is
|
|
826
|
+
emitted — the action vocabulary, the emit shape and the parse/close contract.
|
|
622
827
|
|
|
623
|
-
|
|
624
|
-
`plugins/sp/scripts/inline-run-setup.ts`) — no new entry point, no second resolution chain:
|
|
828
|
+
## Post-gate tree freeze (task 1135 R3/R4/R5)
|
|
625
829
|
|
|
626
|
-
|
|
627
|
-
|
|
830
|
+
The quality gate's `proofDigest` certifies a specific tree. Two rules protect it, and both are
|
|
831
|
+
**ordering plus early detection** — a host session cannot intercept a file write, so the contract is:
|
|
628
832
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
`duration_ms`, `run_id`) the engine would have written, so the run's rows are queryable by run id
|
|
639
|
-
(`spur workflow progress <run-id>`, `ActionRunDao`) without reading the text log. The writer
|
|
640
|
-
back-dates the row's `started_at` from its own `completed_at` minus the measured duration
|
|
641
|
-
(0887 R8), so `completed_at − started_at == duration_ms` exactly; a back-date failure is
|
|
642
|
-
recorded (`action.backdate`) and never affects the run.
|
|
643
|
-
|
|
644
|
-
- **A state with several actions (1007 R5)** — emit the whole state's boundaries in one call
|
|
645
|
-
instead of one `--action` invocation per action. Write a JSON array
|
|
646
|
-
(`[{node,kind,status,ok,durationMs}, …]` — same fields the `--action` flags carry) to a temp
|
|
647
|
-
file and pass it with `--actions-file`:
|
|
648
|
-
|
|
649
|
-
```bash
|
|
650
|
-
bun "$SETUP_SCRIPT" --actions-file <actions.json> --run-id "$RUN_ID"
|
|
651
|
-
```
|
|
833
|
+
1. **Every tree edit belongs to `implement`, or to the bounded `test-fix` remediation hop** — stray-file
|
|
834
|
+
cleanup, anchor qualification, contract pins, doc edits, and task-section writes other than the
|
|
835
|
+
pipeline-owned `## Testing`/`## Review`/`## Solution`. After `test` captures `proofDigest`, no
|
|
836
|
+
other state edits the tree. A genuine later correction goes through `test-fix → test-recheck`,
|
|
837
|
+
which re-captures the digest and re-snapshots the tree state.
|
|
838
|
+
2. **Review joins the proof bracket.** The pipeline now compares `proofDigestNow` against
|
|
839
|
+
`proofDigest` at **review** entry as well as verify and record, so a drifted tree stops the run
|
|
840
|
+
before a review dispatch is spent. The mismatch routes through the existing tripwire to `failed`;
|
|
841
|
+
there is no new re-certify path.
|
|
652
842
|
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
- **A `decide` action (0941)** — the driver never executes the DecisionMaker itself; it delegates
|
|
664
|
-
to the same app runner the engine registers, which writes the resultFile row (schemaVersion 1)
|
|
665
|
-
and returns the decision, then the delegate records the `action_runs` row (`kind=decide`)
|
|
666
|
-
through the same writer as every other action:
|
|
843
|
+
**A mismatch names the drifted paths (R5).** At gate entry the pipeline snapshots the non-corpus tree
|
|
844
|
+
state (`git status --porcelain=v1 -uall` plus `git diff --name-only HEAD`, excluding
|
|
845
|
+
`docs/tasks*` / `docs/features*`) into `.spur/run/<run-id>-gate-paths.txt`. On a mismatch the
|
|
846
|
+
`proof.fingerprint` action diffs the current state against that file and reports the changed paths in
|
|
847
|
+
both the tripwire event and the run log (`proof-compare-failed … drifted paths: …`), so the operator
|
|
848
|
+
reads *which file* moved, not only the `D1`/`D2` pair. An **untracked** file outside those exclusions
|
|
849
|
+
is part of the certified set: the digest's git-tree half stages everything outside the excluded
|
|
850
|
+
globs, so a stray file added after the gate is real drift and must be named (the behaviour is
|
|
851
|
+
documented beside `DEFAULT_EXCLUDE_GLOBS` in `proof-input-fingerprint.ts`).
|
|
667
852
|
|
|
668
|
-
|
|
669
|
-
bun "$SETUP_SCRIPT" --decide --run-id "$RUN_ID" --node <state-id> --options-json <options-file>
|
|
670
|
-
```
|
|
671
|
-
|
|
672
|
-
The options JSON mirrors the YAML `decide` options (`id`, `method: choice|noul`, `question`,
|
|
673
|
-
`choices`/`default`, optional `evidence`, optional `minConfidence`, `resultFile`); paths resolve
|
|
674
|
-
against the project workdir. The decision never pauses and never fails the run for model
|
|
675
|
-
problems: a degraded outcome (feature switch off, no backend, error, timeout, low confidence)
|
|
676
|
-
prints `ok:true` with `degraded:true`, the declared `reason`, and `value = default`, and exits
|
|
677
|
-
`0` — route on the resultFile's `.value` with the declared file guards. Every row carries
|
|
678
|
-
`source: model|default` (0976 R2), and the delegate appends
|
|
679
|
-
`decide node=<id> value=<v> source=<s> reason=<r>` to the run log, so a declared-default
|
|
680
|
-
fallback is never read as a model decision. Only an invalid options
|
|
681
|
-
schema exits `1` (fail closed), and usage errors exit `2`. The `action_runs` trace row is
|
|
682
|
-
best-effort exactly like `--action`.
|
|
683
|
-
|
|
684
|
-
- **At the run's declared terminal state** — before the driver reports the run complete, close the
|
|
685
|
-
row so a successful inline run is never left non-terminal for `spur workflow clean` to reap as
|
|
686
|
-
stale:
|
|
687
|
-
|
|
688
|
-
```bash
|
|
689
|
-
bun "$SETUP_SCRIPT" --close --run-id "$RUN_ID" --status <done|failed|paused>
|
|
690
|
-
```
|
|
853
|
+
## Record & done sequencing
|
|
691
854
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
the close reports the recorded evidence: `{"ok":true,"runId":…,"actionRows":<n>}`.
|
|
695
|
-
|
|
696
|
-
**Zero-row done closes are a named failure (task 0975 R2).** A run closed `done` with **zero**
|
|
697
|
-
`action_runs` rows (`actionRows:0`) finalizes the row but exits `1` with
|
|
698
|
-
`{"ok":false,"code":"NO_ACTION_ROWS","actionRows":0}` — a run that claims success without a
|
|
699
|
-
single recorded action is exactly the untraced-runs gap ADR-117 closes, so the driver must
|
|
700
|
-
surface the code in its final report instead of reporting a clean close. The failure must NOT
|
|
701
|
-
be repaired by backfilling rows: the run row is already terminal, and hand-written rows are
|
|
702
|
-
forbidden (ADR-117) — record the finding and let the task's evidence show the gap. A `failed`
|
|
703
|
-
close with zero rows stays a clean `0` (a halt before the first boundary legitimately records
|
|
704
|
-
nothing), and any `done` close with `actionRows ≥ 1` exits `0`.
|
|
705
|
-
|
|
706
|
-
**Best-effort at the action boundary only (ADR-117).** An `--action` persistence failure is
|
|
707
|
-
recorded — the delegate appends a `trace-emission-failed` line to `.spur/memory/runs/<run-id>.md` and
|
|
708
|
-
prints `{"ok":false}` on stdout — and the run continues to its declared terminal state; the
|
|
709
|
-
delegate exits `0` for that outcome and the driver must never treat an emission failure as a run
|
|
710
|
-
failure, retry it in a loop, or substitute a hand-written row. The run-row closure (`--close`) is
|
|
711
|
-
bookkeeping, not trace emission, and is **not** best-effort: a missing run row or a persistence
|
|
712
|
-
failure exits `1` with `{"ok":false}` and a named error (a missing row also carries
|
|
713
|
-
`code:"RUN_NOT_FOUND"`), because a silently `running` row is exactly the stale state
|
|
714
|
-
`spur workflow clean` reaps as `failed`. Exit `2` means the invocation itself was malformed
|
|
715
|
-
(missing `--node`/`--kind`/`--status`/`--ok`, a miscased `--ok`, a missing or malformed
|
|
716
|
-
`--duration-ms`, or an unsafe run id) and must be corrected, not ignored.
|
|
717
|
-
|
|
718
|
-
Emission is not optional and not deferred: an inline run that skips it reintroduces the
|
|
719
|
-
1,011-untraced-runs gap ADR-117 exists to close.
|
|
720
|
-
|
|
721
|
-
Transition guards are not advisory. Execute the declared guard exactly, in order, with the same
|
|
722
|
-
resolved variables and artifacts. `--no-lifecycle` remains bookkeeping only; the YAML's task checks,
|
|
723
|
-
verdict gate, record step, and done guard all remain authoritative.
|
|
724
|
-
|
|
725
|
-
## Record & done sequencing (dogfood 2026-08-21, feature A3)
|
|
726
|
-
|
|
727
|
-
Order matters for the `testing → done` hop. The A3 batch hit the same clobbering spiral on two
|
|
728
|
-
tasks (0617, 0619) because the sections were hand-written **before** the verdict artifact existed:
|
|
855
|
+
Order matters for the `testing → done` hop: the sections must not be hand-written **before** the
|
|
856
|
+
verdict artifact exists (tasks 0617, 0619 — the clobbering spiral).
|
|
729
857
|
|
|
730
858
|
1. **Write the verdict artifact first.** `spur task record --solution-from-diff --transition testing`
|
|
731
859
|
reads `.spur/run/<wbs>-verdict.json` (default attempt output) on every invocation and atomically retains the recorded verdict under `.spur/memory/evidence/`. A missing or malformed artifact
|
|
732
860
|
yields **UNKNOWN**: bare Testing receives a "No requirements recorded" stub, while already-authored
|
|
733
|
-
Testing is preserved. `--solution-from-diff` backfills only a bare Solution. The
|
|
734
|
-
|
|
861
|
+
Testing is preserved. `--solution-from-diff` backfills only a bare Solution. The clobbering above is
|
|
862
|
+
the historical behavior, corrected by the authored-Testing safeguard (tasks 0617, 0619). Creating the
|
|
735
863
|
artifact first (PASS, with requirement rows keyed by scenario title) remains the standard order;
|
|
736
864
|
re-running record after a real verdict arrives refreshes Testing from that verdict.
|
|
737
865
|
|
|
@@ -750,6 +878,12 @@ tasks (0617, 0619) because the sections were hand-written **before** the verdict
|
|
|
750
878
|
2. **Done-probe before done.** Run the check projected to `done` (`spur task check <wbs> --as done`
|
|
751
879
|
via the `TaskCheckService` probe pattern) — it surfaces `L3.unchecked-checklist` (flip `- [ ]` → `- [x]`)
|
|
752
880
|
and `L3.required-section-placeholder` before the transition, not after.
|
|
881
|
+
**Anchor repair is the YAML implement stage's job, not the driver's** (task 1109 R4):
|
|
882
|
+
`task-pipeline.yaml` runs `$spurBin task migrate-anchors --wbs $wbs --json ; exit 0` after the
|
|
883
|
+
format step and before `test` captures the proof digest, so unique basename anchors are already
|
|
884
|
+
repo-relative by the time this probe runs. The driver does not hand-patch anchors: anything the
|
|
885
|
+
scoped pass leaves (ambiguous basenames, line drift, `L4.anchor-subject-mismatch`) is reported by
|
|
886
|
+
this probe and belongs to the implementer to re-anchor.
|
|
753
887
|
3. **Solution change-map anchor rule (L4.anchor-subject-mismatch).** A Solution change-map table must
|
|
754
888
|
list **one `file:line` per row**. A ·-joined paragraph makes every anchor's "subject" the other
|
|
755
889
|
anchors and trips the L4 subject check. Since 0804 R9, subject extraction ignores complete parsed
|
|
@@ -757,6 +891,77 @@ tasks (0617, 0619) because the sections were hand-written **before** the verdict
|
|
|
757
891
|
(`docs/help/cmd_example.md:12`) is checked exactly like any other row — cite an **existing file**
|
|
758
892
|
with a **valid line or line range** whose content names the requirement's subject. A real absent
|
|
759
893
|
symbol, nonexistent file or invalid range still reports; never replace a citable row with prose.
|
|
894
|
+
4. **Stage through the commit guard (1129 R2/R3/R5).** The per-task commit that follows the record
|
|
895
|
+
hop stages only the files THIS run wrote — `git add -A` and `git add .` are **forbidden** in
|
|
896
|
+
every driver commit step (they staged a concurrent session's files in `a94f9f431` and
|
|
897
|
+
`36f274590`). `check` first and record a non-empty `foreign` list in the run report; `stage`
|
|
898
|
+
exits 2 when it refuses a listed path (foreign) and 3 when a target is unmerged or still carries
|
|
899
|
+
conflict markers — both mean "resolve it, do not commit":
|
|
900
|
+
|
|
901
|
+
```bash
|
|
902
|
+
GUARD=plugins/sp/scripts/commit-guard.ts; [ -f config/plugin-scripts.json -a -f "$GUARD" ] || GUARD="$(superskill script path sp commit-guard.mjs 2>/dev/null)";
|
|
903
|
+
bun "$GUARD" check --run "$RUN_ID"
|
|
904
|
+
bun "$GUARD" stage --run "$RUN_ID" -- <files this run wrote> \
|
|
905
|
+
|| { echo "record halt: commit-guard refused a path (foreign, unmerged or conflict-marked); nothing committed" >&2; exit 1; }
|
|
906
|
+
git commit -m "<type>(<scope>): <wbs> <summary>"
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
5. **Print the run's execution summary (1146).** The terminal `--close` returns `summaryFile`; print
|
|
910
|
+
that file's contents verbatim under an `### Execution summary` heading, and pass `--no-summary`
|
|
911
|
+
through to `--close` when the operator asked for it. The window is the run row's `started_at` →
|
|
912
|
+
close time — the driver records no `SINCE` and assembles no measurement of its own
|
|
913
|
+
(structured-trace-emission.md § the close step owns the contract).
|
|
914
|
+
|
|
915
|
+
## Running the full gate (task 0872 / 1127 R7)
|
|
916
|
+
|
|
917
|
+
The full gate serializes behind a host-wide lock and legitimately runs 10-25 minutes. Two rules keep
|
|
918
|
+
it from becoming a hazard (both violated once on 2026-10-10, blocking every other session for ~11
|
|
919
|
+
minutes and aborting the run's own verification):
|
|
920
|
+
|
|
921
|
+
- **Never wrap it in an external `timeout`, and never treat its runtime as a hang.** An outer bound
|
|
922
|
+
that fires mid-suite leaves the holder alive holding the lock while the caller believes it is gone.
|
|
923
|
+
- **Run it detached and poll the log**, so losing the foreground call cannot lose the holder:
|
|
924
|
+
|
|
925
|
+
```bash
|
|
926
|
+
nohup bash -c 'bun run spur-check > /tmp/gate.log 2>&1; echo "EXIT=$?" >> /tmp/gate.log' &
|
|
927
|
+
```
|
|
928
|
+
|
|
929
|
+
- **Never kill a gate holder whose pid is alive.** The waiter line `waiting for holder (… held Nms)`
|
|
930
|
+
is printed only after `isProcessAlive(held.claim.pid)` succeeded, so a printed holder is a live one
|
|
931
|
+
by construction; only the lock's own dead-pid reclaim is authorised to clear a claim.
|
|
932
|
+
|
|
933
|
+
## Runtime interface contracts (from run evidence)
|
|
934
|
+
|
|
935
|
+
Each item below cost a driver cycle to discover. They are contracts, not tips.
|
|
936
|
+
|
|
937
|
+
- **`--decide --options-json <file>` takes one option object, never an array.** Shape:
|
|
938
|
+
`{id, decision, params?, evidence? | instructions?, resultFile}`; `evidence` and `instructions` are
|
|
939
|
+
mutually exclusive (`CatalogDecideOptionsSchema`). A JSON array parses as neither dialect and
|
|
940
|
+
surfaces as `decide: invalid options — Invalid input` with no field detail. `resultFile` resolves
|
|
941
|
+
against the confirmed tree's cwd, so pass a worktree-relative path such as
|
|
942
|
+
`.spur/run/<wbs>-triage.decision`.
|
|
943
|
+
- **`task-diffstat.ts` reads `wbs` from the environment and writes an artifact.** Invoke it as
|
|
944
|
+
`wbs=<wbs> node "$(superskill script path sp task-diffstat.mjs)"`; stdout stays empty, so assert success on
|
|
945
|
+
`.spur/run/<wbs>-diffstat.json` and never on command output. Never the bare unguarded
|
|
946
|
+
source-repo path form — the script-contract check forbids it on a shipped surface because it
|
|
947
|
+
shadows the installed twin in a consumer.
|
|
948
|
+
- **The verify answer grammar is exact.** One `Verdict:` line, one `Confidence:` line, then
|
|
949
|
+
`### Per-Requirement Traceability` (`| Req | Status | Evidence |`) and
|
|
950
|
+
`### Acceptance Criteria Verification` (`| AC | Status | Evidence Type | Evidence |`). Requirement
|
|
951
|
+
statuses are `MET | PARTIAL | UNMET`; AC statuses add `N/A`; every AC row id must match a declared
|
|
952
|
+
scenario title verbatim, including its `(req: ...)` suffix. A `MET` row whose evidence hedges
|
|
953
|
+
(`likely`, `probably`, `seems`, `appears`, `possibly`, `presumably`) is rejected as
|
|
954
|
+
`evidence-hedged`, and a level the rows do not earn is rejected as `confidence-unwarranted` or
|
|
955
|
+
`confidence-understated`. `spur task verdict` lints before writing, so a rejected answer leaves no
|
|
956
|
+
artifact and exits non-zero.
|
|
957
|
+
- **Harness tooling refuses direct task-file writes.** Resolve a merge conflict in a task file by
|
|
958
|
+
choosing a git side, then applying the corpus delta through `spur task update` (for example
|
|
959
|
+
`--feature <id>`), never by editing the file.
|
|
960
|
+
- **A skill body has a byte budget** (20,000 B for `SKILL.md`); procedure detail belongs in
|
|
961
|
+
`references/`. Adding a clause to a SKILL.md can fail `R44 — skill BODY budgets`.
|
|
962
|
+
- **`.spur/run/` is volatile** (ADR-131, as amended): its contents are execution scratch. Anything
|
|
963
|
+
that outlives the run cites the durable owner (`.spur/memory/evidence/`, `.spur/memory/runs/`) or
|
|
964
|
+
the tracked Testing section.
|
|
760
965
|
|
|
761
966
|
## Failure contract
|
|
762
967
|
|