@gobing-ai/spur 0.4.2 → 0.4.3
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/decisions/maker-liveness.yaml +39 -0
- package/config/plugin-scripts.json +0 -4
- package/config/rules/README.md +1 -0
- package/config/rules/docs/sourced-api-claims.yaml +56 -12
- package/config/rules/strict/runtime-boundaries.yaml +7 -0
- package/config/script-placement-baseline.json +1 -19
- package/config/workflows/feature-lifecycle.yaml +46 -1
- package/config/workflows/feature-verification.yaml +11 -0
- package/package.json +9 -9
- package/plugins/sp/README.md +22 -16
- package/plugins/sp/agents/super-reviewer.md +43 -0
- package/plugins/sp/bunfig.toml +8 -5
- package/plugins/sp/hooks/careful-guard.ts +1 -1
- package/plugins/sp/hooks/context-post-tool.ts +7 -427
- package/plugins/sp/hooks/context-session-start.ts +5 -213
- package/plugins/sp/hooks/context-session-stop.ts +5 -191
- package/plugins/sp/hooks/pi/guard-extension.ts +6 -5
- package/plugins/sp/hooks/task-write-guard.ts +1 -1
- package/plugins/sp/{hooks → lib}/agent-hint.ts +1 -1
- package/plugins/sp/lib/context-post-tool.ts +432 -0
- package/plugins/sp/lib/context-session-start.ts +214 -0
- package/plugins/sp/lib/context-session-stop.ts +196 -0
- package/plugins/sp/lib/dogfood-pipeline.ts +265 -0
- package/plugins/sp/lib/dogfood-report.ts +278 -0
- package/plugins/sp/lib/idea-handoff.generated.mjs +3 -3
- package/plugins/sp/lib/inline-run-app.ts +40 -0
- package/plugins/sp/lib/inline-run.generated.d.mts +3 -3
- package/plugins/sp/lib/inline-run.generated.mjs +1347 -90
- package/plugins/sp/lib/plan-projection.generated.d.mts +150 -0
- package/plugins/sp/lib/plan-projection.generated.mjs +591 -1
- package/plugins/sp/lib/quality-gate.generated.mjs +38 -25
- package/plugins/sp/lib/residual-scan.generated.d.mts +7 -1
- package/plugins/sp/lib/residual-scan.generated.mjs +71 -6
- package/plugins/sp/lib/run-summary-core.ts +34 -370
- package/plugins/sp/lib/transcript.ts +37 -228
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/batch-plan.mjs +586 -0
- package/plugins/sp/scripts/batch-preflight.mjs +23 -1
- package/plugins/sp/scripts/batch-preflight.ts +35 -295
- package/plugins/sp/scripts/daily-summary/daily-summary.mjs +108 -87
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +111 -104
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +2 -1
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +7 -265
- package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +3 -0
- package/plugins/sp/scripts/dogfood-testing/validate-report.ts +6 -272
- package/plugins/sp/scripts/feature-verification-steps.mjs +7 -123
- package/plugins/sp/scripts/feature-verification-steps.ts +29 -181
- package/plugins/sp/scripts/inline-run-dispatch.mjs +27 -26
- package/plugins/sp/scripts/inline-run-dispatch.ts +1 -21
- package/plugins/sp/scripts/inline-run-setup.mjs +149 -122
- package/plugins/sp/scripts/inline-run-setup.ts +1 -21
- package/plugins/sp/scripts/quality-gate.mjs +14 -2
- package/plugins/sp/scripts/residual-scan.mjs +73 -6
- package/plugins/sp/scripts/residual-scan.ts +8 -5
- package/plugins/sp/scripts/run-summary.mjs +151 -97
- package/plugins/sp/scripts/run-summary.ts +7 -5
- package/plugins/sp/scripts/session-timeline.mjs +46 -19
- package/plugins/sp/scripts/task-diffstat.mjs +11 -1
- package/plugins/sp/scripts/task-diffstat.ts +8 -39
- package/plugins/sp/skills/code-implementation/SKILL.md +7 -1
- package/plugins/sp/skills/code-verification/references/secu-review.md +2 -1
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +4 -0
- package/plugins/sp/skills/dogfood-testing/SKILL.md +2 -2
- package/plugins/sp/skills/next-feature/SKILL.md +1 -1
- package/plugins/sp/skills/parallel-execution/references/dispatch-surface.md +1 -1
- package/plugins/sp/skills/source-driven-development/SKILL.md +7 -4
- package/plugins/sp/skills/spur-cli/references/builder.md +3 -2
- package/plugins/sp/skills/spur-cli/references/decision.md +32 -7
- package/plugins/sp/skills/spur-cli/references/features.md +46 -5
- package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +1 -1
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +13 -3
- package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +22 -0
- package/plugins/sp/skills/spur-dev/references/execution-worktree-setup.md +14 -0
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +29 -12
- package/spur.js +80681 -79296
- package/web/_astro/{BoardApp.B3GQZvj8.js → BoardApp.5IIl2O-z.js} +1 -1
- package/web/_astro/{BoardApp.BT7L2kSb.js → BoardApp.Drnx-KeM.js} +150 -150
- package/web/_astro/{TaskDetail.Cxov1_N_.js → TaskDetail.Ci4LNn9S.js} +1 -1
- package/web/_astro/{arc.vaT7tPOB.js → arc.j2EVkYVd.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.DRMmTjkN.js → architectureDiagram-3BPJPVTR.DZWSFqEG.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.cla0LO_q.js → blockDiagram-GPEHLZMM.DE6GpXCN.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.D4YAIBKn.js → c4Diagram-AAUBKEIU.DoVR1G6B.js} +1 -1
- package/web/_astro/channel.DmdDxGsq.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.BrSJbMRZ.js → chunk-2J33WTMH.Do6-Gbyf.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.CuLMgKF5.js → chunk-4BX2VUAB.BYV1HGHx.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.zkvgu54F.js → chunk-55IACEB6.CvI1zkrc.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.DxWkACh8.js → chunk-727SXJPM.DNGSCRlX.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.42XrmIlH.js → chunk-AQP2D5EJ.DXhhgT5z.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.Bao2wMK9.js → chunk-FMBD7UC4.dzkRKP2u.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.BhydojD1.js → chunk-ND2GUHAM.DujGLeEf.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.CmZ82g-H.js → chunk-QZHKN3VN.BWtIT3ZU.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.Blr83Gt_.js → classDiagram-4FO5ZUOK.DyApg0Nn.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.Blr83Gt_.js → classDiagram-v2-Q7XG4LA2.DyApg0Nn.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.qmfv0SPn.js → cose-bilkent-S5V4N54A.DJQB-u_l.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.BtELhXPR.js → cynefin-OW5HDTMX.bi3D_siV.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.AWmaAvXo.js → dagre-BM42HDAG.C-f7TrRg.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.DdH34KDi.js → diagram-2AECGRRQ.DrPfjHQB.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.BmNPix1k.js → diagram-5GNKFQAL.C9S4WUHO.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.BNQd_Ppo.js → diagram-KO2AKTUF.Dzzye8QH.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.BAPgDi0e.js → diagram-LMA3HP47.DX-LRiof.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.DZ4zyLdC.js → diagram-OG6HWLK6.Z-kAKwIz.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.Q1TySMmw.js → erDiagram-TEJ5UH35.D6dyClSv.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.BXhFBicG.js → flowDiagram-I6XJVG4X.Cx3q2cxD.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.vHSgcuo2.js → ganttDiagram-6RSMTGT7.DU3gH6CT.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.Bi1f73C7.js → gitGraphDiagram-PVQCEYII.ISmCnJ87.js} +1 -1
- package/web/_astro/index.Do2lLdgo.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.DPvXZEWm.js → infoDiagram-5YYISTIA.DR6_iNOK.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.Cz829fsV.js → ishikawaDiagram-YF4QCWOH.D7wY-ZGA.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.enRnB9Me.js → journeyDiagram-JHISSGLW.DkP9-WK8.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.DnTNk6d2.js → kanban-definition-UN3LZRKU.wtgY98M2.js} +1 -1
- package/web/_astro/{linear.BzGnBwNL.js → linear.CI6djNi7.js} +1 -1
- package/web/_astro/{mermaid.core.CSbTpIVz.js → mermaid.core.CuQPxS8b.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.DGJDy8zT.js → mindmap-definition-RKZ34NQL.5HNIEG7R.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.HSPdi5_8.js → pieDiagram-4H26LBE5.CchhDFjE.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.6Gv8mz2Y.js → quadrantDiagram-W4KKPZXB.PYXXJPSJ.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.CnBCtFwa.js → requirementDiagram-4Y6WPE33.cUQuNAKU.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.5M8VWAu8.js → sankeyDiagram-5OEKKPKP.BO7MnRIA.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.DN41XN-U.js → sequenceDiagram-3UESZ5HK.D9bElyD6.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.ErOO3W8j.js → stateDiagram-AJRCARHV.DhGdRz7I.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.B_ZHICM-.js → stateDiagram-v2-BHNVJYJU.5GvLeAoL.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.CWdosr07.js → timeline-definition-PNZ67QCA.LtAT9haM.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.BHzYjNV5.js → vennDiagram-CIIHVFJN.DRth1yLA.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.h8x4IwPX.js → wardleyDiagram-YWT4CUSO.h9MlZfcr.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.RTapW2X8.js → xychartDiagram-2RQKCTM6.DHqy9DYA.js} +1 -1
- package/web/index.html +2 -2
- package/config/proportional-route-table.ts +0 -155
- package/plugins/sp/scripts/daily-summary/logger.ts +0 -28
- package/web/_astro/channel.D4nG9YLf.js +0 -1
- package/web/_astro/index.BhSwN8yY.css +0 -1
- /package/plugins/sp/{hooks → lib}/context-owner.ts +0 -0
- /package/plugins/sp/{hooks → lib}/destructive-policy.ts +0 -0
- /package/plugins/sp/{hooks → lib}/task-file-policy.ts +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: spur-cli-decision
|
|
3
|
-
description: "spur-cli noun reference: operate `spur decision` as the read/serve surface over the decision catalog - list registered decisions, show one served contract (parameters, criteria vocabulary, effective maker), run a decision to a served outcome (never writes a workflow resultFile), and status the catalog layers. Backed by @gobing-ai/ts-ai-decision 0.5.22 hub; offline by default via the built-in `typesafe` maker. Introduced by task 1093."
|
|
3
|
+
description: "spur-cli noun reference: operate `spur decision` as the read/serve surface over the decision catalog - list registered decisions, show one served contract (parameters, criteria vocabulary, effective maker), run a decision to a served outcome (never writes a workflow resultFile), and status the catalog layers with per-maker readiness marks (registered, configured, recorded, probed). Backed by @gobing-ai/ts-ai-decision 0.5.22 hub; offline by default via the built-in `typesafe` maker. Introduced by task 1093; readiness marks and `--probe` by feature P11."
|
|
4
4
|
see_also:
|
|
5
5
|
- spur-cli
|
|
6
6
|
---
|
|
@@ -25,9 +25,10 @@ Decisions resolve first-wins across three layers:
|
|
|
25
25
|
| `registered` | folders listed in config `decisions.paths` | Operator-added catalogs |
|
|
26
26
|
| `shared` | `decisions/` under the bundled config root | Shipped catalogs (`config/decisions/` in-repo) |
|
|
27
27
|
|
|
28
|
-
The bundled shared catalog ships `task-triage`, `failure-class`,
|
|
29
|
-
`
|
|
30
|
-
|
|
28
|
+
The bundled shared catalog ships `task-triage`, `failure-class`, `review-failure-class`,
|
|
29
|
+
`gate-evidence`, `idea-recommendation`, `needs-design`, `anatomy-validation-verdict` and
|
|
30
|
+
`maker-liveness` (the last served only by `spur decision status --probe`; no workflow references
|
|
31
|
+
it). Duplicate ids across layers keep the higher layer; duplicates inside one file are a load error.
|
|
31
32
|
|
|
32
33
|
**Config keys** (under `decisions:` in the global `~/.config/spur/config.yaml` or project `.spur/config.yaml`):
|
|
33
34
|
`paths` (extra catalog folders forming the `registered` layer), `maker` (default DecisionMaker
|
|
@@ -41,7 +42,7 @@ config error, never a silent fallback (design §3.6).
|
|
|
41
42
|
| `list` | Every visible decision: id, type, layer, catalog file, description | `--layer <layer>`, `--json` |
|
|
42
43
|
| `show <id>` | One served contract: type, parameters, criteria, fallback, minConfidence, effective maker + source | `--json` |
|
|
43
44
|
| `run <id>` | Serve one outcome (closed vocabulary; **never writes a resultFile**) | `--param <pairs...>`, `--evidence <files...>`, `--maker <name>`, `--json` |
|
|
44
|
-
| `status` |
|
|
45
|
+
| `status` | Sectioned readiness report: one status line, errors, aligned `Makers` and `Decisions` tables (registration, configuration source, recorded evidence), `Catalog` block | `--probe`, `--reliability [--since <iso>]`, `--json` |
|
|
45
46
|
|
|
46
47
|
All verbs support `--json` (bare payload) and `--json-envelope` (`{ok, data|error}` envelope,
|
|
47
48
|
ADR-091). Failure output for `--json` commands uses the canonical error envelope only under
|
|
@@ -51,6 +52,25 @@ ADR-091). Failure output for `--json` commands uses the canonical error envelope
|
|
|
51
52
|
(design §3.4 — load errors surface via `status`) except for an invalid `--layer` value (exit 1).
|
|
52
53
|
Error envelopes use `NOT_FOUND` for an unknown id and `VALIDATION_FAILED` for every other caller mistake.
|
|
53
54
|
|
|
55
|
+
## Readiness marks (`status`, feature P11)
|
|
56
|
+
|
|
57
|
+
The default `status` output separates four marks per maker and per decision
|
|
58
|
+
(`docs/design/decision-observability-and-adoption.md` §3.4.1):
|
|
59
|
+
|
|
60
|
+
| Mark | Column | Source |
|
|
61
|
+
| ---- | ------ | ------ |
|
|
62
|
+
| registered | `ST` glyph on the maker row | the maker registry holds the name |
|
|
63
|
+
| configured | `CONFIGURED BY` | `decisions.maker` / `decisions.makers.<id>` |
|
|
64
|
+
| recorded | `RECORDED` / `ACCEPTED` / `LAST SEEN` | the recorded `decision.end` ledger, matched on `(decisionId, maker)` |
|
|
65
|
+
| proven | `PROBE` (only with `--probe`) | one live liveness decision per maker |
|
|
66
|
+
|
|
67
|
+
`RECORDED` with `ACCEPTED 0` is the state that reads as healthy everywhere else: the maker is
|
|
68
|
+
registered and configured but has never once succeeded. `--probe` is the only path that calls a
|
|
69
|
+
maker, is bounded per driver, writes no decision log row and emits no event (`--probe` with
|
|
70
|
+
`--reliability` is refused). Parse the marks with `--json`: `makers[]`
|
|
71
|
+
(`registered`, `configuredBy`, `serves`, `evidence`), `perDecision[].evidence`, and `probes[]`
|
|
72
|
+
under `--probe`.
|
|
73
|
+
|
|
54
74
|
**Exit contract for `run` (design §3.4):** any backend-observed outcome — low confidence, model
|
|
55
75
|
error surfaced as fallback, declined — is still a **successful serve** (exit 0, envelope carries
|
|
56
76
|
`source`/`confidence` so callers can see why). Exit 1 is reserved for caller mistakes caught
|
|
@@ -95,11 +115,16 @@ spur decision run task-triage --param wbs=1093 --json
|
|
|
95
115
|
spur decision run failure-class --param wbs=1093 \
|
|
96
116
|
--evidence .spur/run/<runId>-test-output.txt # redacted evidence -> instructions
|
|
97
117
|
spur decision status --json | jq '.loadErrors' # audit catalog health
|
|
118
|
+
spur decision status # readiness marks: registered / configured / recorded
|
|
119
|
+
spur decision status --probe # prove each effective maker answers now
|
|
120
|
+
spur decision status --json | jq '.makers[] | {name, registered, recorded: .evidence.recorded}'
|
|
98
121
|
```
|
|
99
122
|
|
|
100
123
|
## References
|
|
101
124
|
|
|
102
|
-
- Design: `docs/design/decision-catalog.md` (§2 catalog authoring, §3.4 CLI surface)
|
|
125
|
+
- Design: `docs/design/decision-catalog.md` (§2 catalog authoring, §3.4 CLI surface),
|
|
126
|
+
`docs/design/decision-observability-and-adoption.md` (§3.4 reliability report, §3.4.1 readiness view)
|
|
103
127
|
- Hub: `@gobing-ai/ts-ai-decision` 0.5.22 (`DecisionService`, catalog schema, maker registry)
|
|
104
|
-
- App service: `packages/app/src/decision/decision-service.ts`
|
|
128
|
+
- App service: `packages/app/src/decision/decision-service.ts` (makers, `probeMaker`),
|
|
129
|
+
`packages/app/src/decision/decision-status-readiness.ts` (readiness marks, probe loop)
|
|
105
130
|
- Shipped catalogs: `config/decisions/*.yaml`
|
|
@@ -81,9 +81,45 @@ mode needs its own required args).
|
|
|
81
81
|
spur feature update H2 active
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
Valid statuses: `backlog → active → verifying → blocked → done → cancelled
|
|
85
|
-
enforces legal transitions; `verifying` is the gate where AC
|
|
86
|
-
**One active goal** at a time is enforced by `check` (a corpus invariant).
|
|
84
|
+
Valid statuses: `backlog → active → verifying → blocked → done → cancelled`, plus the frozen
|
|
85
|
+
`deprecated`. The lifecycle engine enforces legal transitions; `verifying` is the gate where AC
|
|
86
|
+
traceability must hold (L4). **One active goal** at a time is enforced by `check` (a corpus invariant).
|
|
87
|
+
|
|
88
|
+
**`deprecated` — frozen, not terminal (ADR-136).** The state means *retained and frozen*: the record
|
|
89
|
+
stays readable and countable, but nothing may attach new work to it or advance it. It is entered from
|
|
90
|
+
`backlog`, `active`, `verifying`, `blocked` and `done`, and left only to `active` (un-deprecate,
|
|
91
|
+
mandatory History entry) or `cancelled` (removed) — every other edge out of `deprecated` is undeclared
|
|
92
|
+
and refused by the graph. The freeze, as shipped:
|
|
93
|
+
|
|
94
|
+
- **Task links are refused** — `task create`, `update --feature` and `batch-create` exit non-zero and
|
|
95
|
+
write nothing (no sibling hint; `--no-reopen` is irrelevant for a frozen parent):
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
parent-feature-status: feature <id> is deprecated (frozen); new tasks cannot link to it. Run removal work under its own feature, or un-deprecate: spur feature update <id> active
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
- **`L4.feature-deprecated`** — `task check` warns (does not error) for a *live* task under a
|
|
102
|
+
deprecated feature; a terminal task is the correct end state and gets no finding; `--strict`
|
|
103
|
+
elevates the warning as usual:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
Feature "<id>" is deprecated (frozen) — re-parent this live task (`spur task update <wbs> --feature <otherId>`) or cancel it.
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
- **`update --section` still lands** (exit `0`), appending a warning to `warnings[]`:
|
|
110
|
+
|
|
111
|
+
```
|
|
112
|
+
Feature <id> is deprecated (frozen); change it only as part of removal
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
- **`sync` / `refresh` / `check --fix` never move it** — derivation is sticky and returns the no-op
|
|
116
|
+
proposal `{ from: 'deprecated', to: 'deprecated', reason: 'Feature is deprecated (frozen)' }`.
|
|
117
|
+
|
|
118
|
+
- **`advance` refuses** with exit `1`, naming the un-deprecate command:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
<id> is deprecated; un-deprecate first: spur feature update <id> active
|
|
122
|
+
```
|
|
87
123
|
|
|
88
124
|
**Scalar field set:**
|
|
89
125
|
|
|
@@ -113,7 +149,8 @@ spur feature advance H2 --to verifying
|
|
|
113
149
|
```
|
|
114
150
|
|
|
115
151
|
Walks the legal forward path (`backlog → active → verifying → done`) hop-by-hop until `--to`
|
|
116
|
-
(default `done`). No-op when already at the target
|
|
152
|
+
(default `done`). No-op when already at the target; a `deprecated` feature is refused up front (see
|
|
153
|
+
the freeze block above).
|
|
117
154
|
|
|
118
155
|
## Acceptance criteria conventions
|
|
119
156
|
|
|
@@ -169,7 +206,8 @@ the AC coverage map. Habits that keep it green:
|
|
|
169
206
|
Use feature hierarchy for user-facing capabilities and roadmap themes; keep technical-module mapping
|
|
170
207
|
as prose or notes unless the product surface is developer infrastructure. Priority is a scalar field
|
|
171
208
|
(`spur feature update <id> --field priority --value P1`), while status is lifecycle state
|
|
172
|
-
(`backlog → active → verifying → blocked → done → cancelled`). Do not
|
|
209
|
+
(`backlog → active → verifying → blocked → done → cancelled`, plus the frozen `deprecated`). Do not
|
|
210
|
+
conflate the two.
|
|
173
211
|
|
|
174
212
|
**Structure first:** [features/hierarchy-mece.md](features/hierarchy-mece.md) (MECE roots, when to
|
|
175
213
|
extend vs create). **Then priority:** [features/roadmap-priority.md](features/roadmap-priority.md).
|
|
@@ -216,6 +254,9 @@ spur feature sync H2 --force # re-derive live: bypass replay +
|
|
|
216
254
|
spur feature sync H2 --folder docs/custom-tasks --json # non-default tasks folder
|
|
217
255
|
```
|
|
218
256
|
|
|
257
|
+
- **`deprecated` is sticky:** derivation returns no proposal for it
|
|
258
|
+
(`reason: 'Feature is deprecated (frozen)'`), so neither `sync` nor `check --fix` moves a frozen
|
|
259
|
+
feature — see the freeze block in §Editing a feature.
|
|
219
260
|
- **`[id]`** syncs one feature; **`--all`** syncs every feature with linked tasks. One of the two is
|
|
220
261
|
required - exit `2` if neither is given.
|
|
221
262
|
- **`--dry-run`** reports proposed transitions without applying. **`--force`** applies a *reopen*
|
|
@@ -126,7 +126,7 @@ success exits 0 printing
|
|
|
126
126
|
`{"ok":true,"persisted":<n>,"skipped":[{"id":<run-id>,"reason":"id-exists"|"external-key-conflict"|"record-conflict:<file>"|"record-missing:<file>"|"cited-directory:<name>"|"cited-symlink:<name>"|"cited-non-file:<name>"}],"evidenceSkipped":[{"name":<file>,"reason":"foreign-divergent","newer":"invoking"|"worktree"}]}`
|
|
127
127
|
(`evidenceSkipped` is omitted when empty; the same object is written to `<invoking-tree>/.spur/run/persist-out.json` for `persist-out-check` to read)
|
|
128
128
|
— an `external-key-conflict` skip leaves the target run unchanged **and fails the pass (1049)**: the delegate exits 1 printing `{"ok":false,"error":…}` naming the skipped source run ids, because that (workflow, external key) identity already belongs to a different receiving run — the batch was not persisted, so reconcile the source worktree by hand (it stays the provenance owner of record) before any teardown: auditable reconciliation names the original archived DB snapshot and the skipped source run identities, verifies the archive by file hash and by merged-commit ancestry of the source branch, and treats any residual deletion as explicit operator-authorized cleanup under the strict canonical evidence rules above. Task 1149 R1 grades a terminal duplicate lifecycle row (`task-lifecycle`/`feature-lifecycle`) as `external-key-conflict-bookkeeping` (non-fatal), so guarded transitions driven inside the worktree do not block teardown; the receiving lifecycle row is reconciled automatically during persist-out. An `id-exists` replay repairs missing owned artifacts and task links without duplicating them. A
|
|
129
|
-
`record-conflict:<file>` never overwrites a divergent invoking-tree record and causes the delegate to exit 1, retaining the worktree. A
|
|
129
|
+
`record-conflict:<file>` never overwrites a divergent invoking-tree record and causes the delegate to exit 1, retaining the worktree. Before reconciling one by hand, check **what** the invoking record is: when the run closed `done` inside a linked worktree the owning tree holds that run's **WT-0 landing obligation** (`landing: required branch=… path=… base=…`, see [execution-worktree-landing.md § WT-0](execution-worktree-landing.md#wt-0--markerless-worktree-runs)) — a 3-line record that is *not* a stub of the worktree's stage narrative, so copying the worktree record over it destroys the very obligation a later merge must rewrite. The reconciliation is therefore: discharge the obligation in place (`landing: merged <sha>` or `landing: retained`), and preserve the worktree narrative beside it (e.g. `<runId>.worktree-provenance.md`) rather than in place of it. A
|
|
130
130
|
`record-missing:<file>` skip is a known `task-lifecycle`/`feature-lifecycle` row with no record file
|
|
131
131
|
at all (its inserted DB row still counts in `persisted` — 0984 R5). Any failure
|
|
132
132
|
exits 1 printing `{"ok":false,"error":<message>}` (a worktree DB run id that is not a single safe
|
|
@@ -606,15 +606,25 @@ The cheap rule gates fail fast when run first — the full-gate run is dominated
|
|
|
606
606
|
suite, so a gate run that dies at `test-post-check` or `corpus-check` wastes most of its wall time.
|
|
607
607
|
Before launching a full `spur-check-new`:
|
|
608
608
|
|
|
609
|
-
1. **Run the
|
|
609
|
+
1. **Run the static checks first** — `bun run lint` (biome over the whole tree plus every workspace
|
|
610
|
+
typecheck). They fail in ~20 s and are the two defects that most often kill a full launch: a
|
|
611
|
+
formatting miss in a touched test file, or a generated declaration surface that no longer lists a
|
|
612
|
+
new export. Measured 2026-10-12: two consecutive `spur-check` launches died here and each spent
|
|
613
|
+
its whole wall time before reaching the suite.
|
|
614
|
+
2. **Then the two rule gates** — `bun run test-pre-check` (43 rules: `no-console-output`,
|
|
610
615
|
`no-direct-process-spawn`, `cli-*`, `require-corresponding-test`) and `bun run test-post-check`
|
|
611
616
|
(`every-export-has-tsdoc`, `coverage-gate`). They catch boundary/TSDoc violations in seconds.
|
|
612
|
-
|
|
617
|
+
Two `coverage-gate` facts change how you call post-check: it **fails closed when
|
|
618
|
+
`.coverage/lcov.info` is absent**, which is the normal state of a fresh worktree, so run it after
|
|
619
|
+
the suite has written coverage or expect that one error instead of reading it as a finding; and a
|
|
620
|
+
targeted `bun test <file>` **overwrites** the tracefile, so post-check judges partial data if it
|
|
621
|
+
runs after one (the `coverage-gate` rule's own header documents both facts).
|
|
622
|
+
3. **Promoted code must satisfy the boundary rules `scripts/` never enforced.** A command module
|
|
613
623
|
moving from `scripts/` into `apps/cli/src` must route output through the `CommandOutput` seam (no
|
|
614
624
|
`console.*`), spawn processes via `NodeProcessExecutor` (no `Bun.spawnSync`), get a
|
|
615
625
|
`runtime-boundaries` fs-io exemption for sync reads (mirrors `task.ts`), and a non-command helper
|
|
616
626
|
must not live in `apps/cli/src/commands/` (the noun scan treats every file there as a noun).
|
|
617
|
-
|
|
627
|
+
4. **Doc/TSDoc edits shift `file:line` anchors** cited by other tasks — the per-task gate surfaces
|
|
618
628
|
them as `L4.anchor-subject-mismatch` (0775: the corpus sweep retired; run `spur task check <wbs>`
|
|
619
629
|
on touched tasks). Repoint the shifted citations (via `spur task update --section`)
|
|
620
630
|
|
|
@@ -16,6 +16,28 @@ When the batch completes with **no failed task**, fast-forward-merge the worktre
|
|
|
16
16
|
base ref. The terminal action after the merge differs by mode (ownership rule: *the flag removes
|
|
17
17
|
only what it created*):
|
|
18
18
|
|
|
19
|
+
**Duplicate-WBS sweep before the merge (observed 2026-10-12).** WBS allocation is per-tree — a
|
|
20
|
+
worktree's allocator reads only its own corpus and DB — so two trees cut from the same base both
|
|
21
|
+
allocate the same next number, and the collision surfaces only here (measured: the F42 batch's
|
|
22
|
+
residual task shipped as `1183` while the base ref had already given `1183` to another task, so the
|
|
23
|
+
file had to be re-filed before landing). Run this against the ref you are about to merge INTO:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
TARGET=<base-ref>; BRANCH=sp/<branch>
|
|
27
|
+
for f in $(git diff --diff-filter=A --name-only "$(git merge-base "$TARGET" "$BRANCH")..$BRANCH" -- docs \
|
|
28
|
+
| grep -E '^docs/tasks[0-9]*/'); do
|
|
29
|
+
wbs=$(basename "$f" | cut -c1-4)
|
|
30
|
+
git ls-tree -r --name-only "$TARGET" -- docs | grep -qE "/${wbs}_" && echo "COLLISION $wbs $f"
|
|
31
|
+
done
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
A hit means one file must lose: keep the target's task, re-create the branch's task in the invoking
|
|
35
|
+
tree through `spur task create` (the allocator picks the next free number), and drop the branch's
|
|
36
|
+
file in the merge resolution — never land both, and never renumber by hand. Verified against a
|
|
37
|
+
fixture (flags a duplicate, silent on a free WBS). The pathspec form is load-bearing: `-- docs` plus
|
|
38
|
+
the `^docs/tasks[0-9]*/` filter, because plain `docs/tasks*` matches nothing under `ls-tree` here and
|
|
39
|
+
`:(glob)` magic is rejected by `ls-tree`/`diff` (it works for `git status`).
|
|
40
|
+
|
|
19
41
|
#### Create mode — merge, remove, delete
|
|
20
42
|
|
|
21
43
|
```bash
|
|
@@ -180,6 +180,13 @@ main-repo hooks from inside the "isolated" tree. Scripts are skipped only at thi
|
|
|
180
180
|
a normal clone keeps `prepare`. The worktree still gets a usable dependency tree — the install
|
|
181
181
|
exists so the first `bun test` resolves workspace imports.
|
|
182
182
|
|
|
183
|
+
**Generated trees are still missing after the install.** A gitignored generated tree does not ride the
|
|
184
|
+
worktree, so any suite comparing the canonical source against its generated copy fails until the
|
|
185
|
+
generator runs. In this repo that is `apps/cli/config/` (from `bun run bundle:config`): without it
|
|
186
|
+
`plugins/sp/tests/command-flag-parity.test.ts` fails on the missing generated pipeline YAML under that
|
|
187
|
+
directory (observed 2026-10-12, A91 batch). Run the generator once after a create-mode install, and
|
|
188
|
+
again after a rebase or merge that touches the canonical workflow sources it is built from.
|
|
189
|
+
|
|
183
190
|
#### Reuse mode (`--worktree <name>`)
|
|
184
191
|
|
|
185
192
|
Creation is **skipped entirely**. `$BRANCH` is the resolved worktree's already-checked-out branch; a
|
|
@@ -374,6 +381,13 @@ same call, verifies the WT-3 identity, and never relies on a persistent `cd` or
|
|
|
374
381
|
> concurrent writer happened to leave in the tree — the two incidents this guard exists for are
|
|
375
382
|
> `a94f9f431` and `36f274590` — so a driver stages only through `commit-guard stage`.
|
|
376
383
|
|
|
384
|
+
> **`stage` takes files, never directories.** A directory argument is refused as `(unchanged since
|
|
385
|
+
> base)` and stages *nothing*, so a task whose diff lives in a moved tree must expand the changed
|
|
386
|
+
> paths (`git status --porcelain` plus the filter for the task's own files) instead of passing
|
|
387
|
+
> `plugins/sp/hooks` or `packages/app/tests/hooks`. Symptom observed on 2026-10-12 (A91 batch): the
|
|
388
|
+
> first commit captured only the pre-staged `git mv`/`git rm` renames and needed an amend to carry
|
|
389
|
+
> the rest of the task.
|
|
390
|
+
|
|
377
391
|
The FF-only git merge carries only commits — uncommitted writes in the worktree would be left
|
|
378
392
|
behind by the merge and then destroyed by create mode's `git worktree remove`. WT-3b exists so
|
|
379
393
|
that can never happen.
|
|
@@ -441,9 +441,9 @@ Action semantics come from the YAML and the workflow action contract:
|
|
|
441
441
|
recheck's `no-progress` guard honours it, so a cap-kill reads as a red gate the next time. Preserve
|
|
442
442
|
the signal-induced receipt under a distinct name (`<wbs>-check-receipt.false-fail-<signal>.json`)
|
|
443
443
|
and say so in the run log before re-running: never delete it silently, and never report a
|
|
444
|
-
cap-killed attempt as a genuine gate failure. Measured
|
|
445
|
-
(25.6 min host-wide lock wait + 7.4 min runtime) was SIGTERM-killed at the
|
|
446
|
-
exactly that false FAIL receipt.
|
|
444
|
+
cap-killed attempt as a genuine gate failure. Measured 2026-10-10 (before the gate-lock removal):
|
|
445
|
+
one foreground gate call (25.6 min host-wide lock wait + 7.4 min runtime) was SIGTERM-killed at the
|
|
446
|
+
host cap and produced exactly that false FAIL receipt.
|
|
447
447
|
- `note` — append the expanded message to the inline run log.
|
|
448
448
|
- `doctor.probe` — run the declared Spur doctor once, persist its status file, and apply any
|
|
449
449
|
`setVars` result (including a resolved executor) before the next action or state.
|
|
@@ -766,7 +766,7 @@ host-owned gate sat between the stages, or when the platform cannot address comp
|
|
|
766
766
|
**Implement and test-fix workers never run the full gate (task 1142 R3).** Workers use the light tier as their iteration check (task 1142 R3) in exact order:
|
|
767
767
|
1. the **changed-path matrix** (`code-implementation/SKILL.md` § Changed-path targeted checks) over the paths they touched;
|
|
768
768
|
2. the resolved `quality-gate` script in `light` mode (source-repo probe, else the installed twin) (biome, per-workspace typecheck, rule presets, related tests).
|
|
769
|
-
Neither step takes the full
|
|
769
|
+
Neither step takes the full gate. The pipeline's `test` and `test-recheck` hops remain the only full gate. Carry
|
|
770
770
|
`--gate-log <path>` through to the child **verbatim**: never paraphrase it into "re-run the named
|
|
771
771
|
gate command". That paraphrase is not cosmetic — it sent a worker into a full gate run instead of
|
|
772
772
|
targeted iteration checks. The pipeline's next `test` (recheck)
|
|
@@ -835,6 +835,19 @@ The quality gate's `proofDigest` certifies a specific tree. Two rules protect it
|
|
|
835
835
|
before a review dispatch is spent. The mismatch routes through the existing tripwire to `failed`;
|
|
836
836
|
there is no new re-certify path.
|
|
837
837
|
|
|
838
|
+
**A host-driven pass that skips the `test` hop has no digest-bound receipt.** `proofDigest` is
|
|
839
|
+
captured by the `test` state's `proof.fingerprint` action, so hops driven out of band — an operator
|
|
840
|
+
implementing/verifying/recording by hand, or an audit of a task whose gate the host ran directly —
|
|
841
|
+
leave no `.spur/run/<wbs>-check-receipt.json`. The deciding artifacts are then
|
|
842
|
+
`.spur/run/<wbs>-test-gate.status` plus its gate log. Two consequences, in order of preference:
|
|
843
|
+
|
|
844
|
+
1. **Drive the pipeline** for anything that must be certified: only a digest-bound full receipt
|
|
845
|
+
satisfies a review/verify/record boundary (`sp-spur-check`), and the `test` hop owns the capture.
|
|
846
|
+
2. **When a host-driven pass is unavoidable, say so in the verdict** — name the status file and gate
|
|
847
|
+
log as the evidence and grade confidence `MEDIUM`. A `HIGH` claim needs re-runnable evidence from a
|
|
848
|
+
checked path (the `sp-code-verification` ladder); claiming a receipt that was never written is the
|
|
849
|
+
failure mode this note exists to prevent (observed across four A91 tasks, 2026-10-12).
|
|
850
|
+
|
|
838
851
|
**A mismatch names the drifted paths (R5).** At gate entry the pipeline snapshots the non-corpus tree
|
|
839
852
|
state (`git status --porcelain=v1 -uall` plus `git diff --name-only HEAD`, excluding
|
|
840
853
|
`docs/tasks*` / `docs/features*`) into `.spur/run/<run-id>-gate-paths.txt`. On a mismatch the
|
|
@@ -909,21 +922,25 @@ verdict artifact exists (tasks 0617, 0619 — the clobbering spiral).
|
|
|
909
922
|
|
|
910
923
|
## Running the full gate (task 0872 / 1127 R7)
|
|
911
924
|
|
|
912
|
-
The full gate
|
|
913
|
-
|
|
914
|
-
minutes and aborting the run's own verification):
|
|
925
|
+
The full gate legitimately runs 10-25 minutes. Two rules keep it from becoming a hazard (both
|
|
926
|
+
violated once on 2026-10-10, aborting the run's own verification):
|
|
915
927
|
|
|
916
928
|
- **Never wrap it in an external `timeout`, and never treat its runtime as a hang.** An outer bound
|
|
917
|
-
that fires mid-suite leaves
|
|
918
|
-
- **Run it detached and poll the log**, so losing the foreground call cannot lose the
|
|
929
|
+
that fires mid-suite leaves a half-finished suite behind while the caller believes it is gone.
|
|
930
|
+
- **Run it detached and poll the log**, so losing the foreground call cannot lose the run:
|
|
919
931
|
|
|
920
932
|
```bash
|
|
921
933
|
nohup bash -c 'bun run spur-check > /tmp/gate.log 2>&1; echo "EXIT=$?" >> /tmp/gate.log' &
|
|
922
934
|
```
|
|
923
935
|
|
|
924
|
-
- **
|
|
925
|
-
|
|
926
|
-
|
|
936
|
+
- **Preserve a signal-induced receipt.** A cap-killed attempt leaves a red receipt that the next
|
|
937
|
+
recheck's `no-progress` guard honours, so it reads as a genuine failure unless the receipt is kept
|
|
938
|
+
under a distinct name and the run log says so.
|
|
939
|
+
|
|
940
|
+
> Historical (task 1127, removed in task 1173): these rules were once stated in terms of a host-wide
|
|
941
|
+
> `full-gate.lock`. The lock mechanism was deleted (`refactor(gate): remove gate-lock mechanism and
|
|
942
|
+
> simplify quality-gate`), so nothing serializes gate runs today — the guidance above stands on its
|
|
943
|
+
> own without it.
|
|
927
944
|
|
|
928
945
|
## Runtime interface contracts (from run evidence)
|
|
929
946
|
|