@gobing-ai/spur 0.3.72 → 0.3.74
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.example.yaml +48 -27
- package/config/pipeline-budgets.json +9 -5
- package/config/plugin-scripts.json +4 -0
- package/config/proportional-route-table.ts +155 -0
- package/config/rules/strict/runtime-boundaries.yaml +1 -1
- package/config/rules/structure/protected-files.yaml +3 -0
- package/config/task-pipeline-proportional-migration-plan.md +79 -0
- package/config/templates/AGENTS.md +5 -0
- package/config/templates/docs/99_PROJECT_CONSTITUTION.md +2 -0
- package/config/workflows/basic.yaml +5 -2
- package/config/workflows/docs-pipeline.yaml +101 -54
- package/config/workflows/feature-dev.yaml +57 -27
- package/config/workflows/feature-lifecycle.yaml +1 -0
- package/config/workflows/history-anatomy.yaml +8 -4
- package/config/workflows/idea-pipeline.yaml +66 -33
- package/config/workflows/pr-review.yaml +31 -8
- package/config/workflows/task-lifecycle.yaml +28 -15
- package/config/workflows/task-pipeline.yaml +929 -844
- package/config/workflows/wayfinder-resolution.yaml +124 -90
- package/config/workflows/wrapup-pipeline.yaml +290 -107
- package/package.json +9 -9
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/daily-summary/daily-summary.mjs +2 -4
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +2 -4
- package/plugins/sp/scripts/history-anatomy-cache.mjs +7 -1
- package/plugins/sp/scripts/history-anatomy-cache.ts +11 -2
- package/plugins/sp/scripts/inline-pipeline-parity-check.ts +185 -0
- package/plugins/sp/scripts/pr-reviewing.mjs +5 -1
- package/plugins/sp/scripts/pr-reviewing.ts +7 -1
- package/plugins/sp/scripts/task-evidence-precheck.ts +1 -1
- package/plugins/sp/scripts/transition-shim-check.ts +6 -6
- package/plugins/sp/scripts/verify-answer-lint.ts +4 -0
- package/plugins/sp/skills/pr-reviewing/SKILL.md +4 -1
- package/plugins/sp/skills/spur-cli/references/self.md +17 -3
- package/plugins/sp/skills/spur-cli/references/tasks.md +1 -1
- package/plugins/sp/skills/spur-cli/references/workflows/authoring-workflows.md +23 -0
- package/plugins/sp/skills/spur-cli/references/workflows/workflow-fit-and-tuning.md +5 -5
- package/plugins/sp/skills/spur-cli/references/workflows.md +4 -4
- package/plugins/sp/skills/spur-dev/SKILL.md +17 -0
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +3 -3
- package/plugins/sp/skills/spur-dev/references/gate-checklists.md +1 -2
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +20 -0
- package/plugins/sp/skills/wayfinder/SKILL.md +1 -1
- package/schemas/state-machine-workflow.schema.json +3 -1
- package/schemas/transition-flow-workflow.schema.json +3 -1
- package/spur.js +12639 -10106
- package/web/_astro/{BoardApp.BYCNkMOn.js → BoardApp.C60RJZRj.js} +78 -77
- package/web/_astro/BoardApp.ymfj1EHA.js +1 -0
- package/web/_astro/{TaskDetail.CgUreSP2.js → TaskDetail.Guk8VuNu.js} +1 -1
- package/web/_astro/{arc.BySSh34M.js → arc.CAZtlWJM.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.DM46TS_h.js → architectureDiagram-3BPJPVTR.9-XbF_Tz.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.tZhvNUHA.js → blockDiagram-GPEHLZMM.DC4WLx3L.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.PT4Or4Nf.js → c4Diagram-AAUBKEIU.JgjUQMgG.js} +1 -1
- package/web/_astro/channel.Bxxe2Byc.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.J9r0_Bbe.js → chunk-2J33WTMH.D_JFiXa-.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.hzyeIvhR.js → chunk-4BX2VUAB.Bk_RxoeT.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.B0rO7qVh.js → chunk-55IACEB6.a_Rj2VxL.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.wE_Uk5D4.js → chunk-727SXJPM.C2NoS1U6.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.DqEEjQw7.js → chunk-AQP2D5EJ.D1Xn6CNa.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.CDoD9sBX.js → chunk-FMBD7UC4.BhJrSBsX.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.CtX5nF9P.js → chunk-ND2GUHAM.ByVGduYQ.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.CK_EwfaT.js → chunk-QZHKN3VN.B47Paunq.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.DLt5a8Lh.js → classDiagram-4FO5ZUOK.BERMSD5C.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.DLt5a8Lh.js → classDiagram-v2-Q7XG4LA2.BERMSD5C.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.CMCWP49h.js → cose-bilkent-S5V4N54A.O_rHGlhU.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.HyXw_vdS.js → cynefin-OW5HDTMX.qCW2GdNp.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.BTuAzh01.js → dagre-BM42HDAG.C5Y4lH_u.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.D9dr9wfT.js → diagram-2AECGRRQ.DxZmRXxF.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.C4Rot0hj.js → diagram-5GNKFQAL.qSxZeYSK.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.B_TK5uWC.js → diagram-KO2AKTUF.-7vl3bXs.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.JkXKK7CO.js → diagram-LMA3HP47.BHVV-UH3.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.BzMN8Bd6.js → diagram-OG6HWLK6.DHAuW9sK.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.DVZaWGUd.js → erDiagram-TEJ5UH35.DMRLQwPN.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.rjEiWUfR.js → flowDiagram-I6XJVG4X.B3EpPp_8.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.C_EgAarK.js → ganttDiagram-6RSMTGT7.BgUrExXM.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.B-QQSDsK.js → gitGraphDiagram-PVQCEYII.CU-9yVN4.js} +1 -1
- package/web/_astro/{index.B5MTfe7k.css → index.BhvM3djt.css} +1 -1
- package/web/_astro/{infoDiagram-5YYISTIA.DlWesz7T.js → infoDiagram-5YYISTIA.CvVTCRLe.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.BUMZOawi.js → ishikawaDiagram-YF4QCWOH.BUzOfKPf.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.CWfkxfjY.js → journeyDiagram-JHISSGLW.BBTb7ziz.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.B-YpMwXf.js → kanban-definition-UN3LZRKU.CTQQr70i.js} +1 -1
- package/web/_astro/{linear.D7uqzENp.js → linear.CLKlJPiS.js} +1 -1
- package/web/_astro/{mermaid.core.CxrNppBD.js → mermaid.core.BA4wFhLP.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.B4Qe7cM2.js → mindmap-definition-RKZ34NQL.t1vG2l5e.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.Ds-5j2ro.js → pieDiagram-4H26LBE5.C8zJhyTu.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.tBd38uNC.js → quadrantDiagram-W4KKPZXB.DqYYvb7T.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.sFENkWl3.js → requirementDiagram-4Y6WPE33.lzfDZ9nI.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.BeB-Hk7C.js → sankeyDiagram-5OEKKPKP.lm6N5ORM.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.DnTeaSpx.js → sequenceDiagram-3UESZ5HK.E6_IV4Dj.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.B-8Jt5EJ.js → stateDiagram-AJRCARHV.Bnv6Ok2p.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.Br7xoqMW.js → stateDiagram-v2-BHNVJYJU.mJHH_Ng_.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.C-3WdOyi.js → timeline-definition-PNZ67QCA.BhRgqSLa.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.DCIs7Lc6.js → vennDiagram-CIIHVFJN.pjb5DMC9.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.rGAL-bbz.js → wardleyDiagram-YWT4CUSO.CW20KUng.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.hkfQKiRl.js → xychartDiagram-2RQKCTM6.vX9_yuMl.js} +1 -1
- package/web/index.html +2 -2
- package/config/corpus-baseline.json +0 -1637
- package/config/workflow-composition-baseline.json +0 -576
- package/web/_astro/BoardApp.E12MFjOS.js +0 -1
- package/web/_astro/channel.5cYKr5cs.js +0 -1
|
@@ -35,6 +35,29 @@ bootstrap:
|
|
|
35
35
|
url: .spur/spur.db # ${DATABASE_URL} interpolation supported
|
|
36
36
|
scheduler:
|
|
37
37
|
enabled: false # CLI is run-once; no scheduler
|
|
38
|
+
# Periodic executions live here and nowhere else (task 0750). A tick
|
|
39
|
+
# enqueues `scheduler.custom`, which the server's job worker runs through
|
|
40
|
+
# /bin/sh -c in the project root. Use the repo-local CLI, never a global
|
|
41
|
+
# `spur` that may be a stale bundle (AGENTS.md history-validation rule).
|
|
42
|
+
jobs:
|
|
43
|
+
# Incremental refresh, waking hours only (07:00-23:50; the cron hour range
|
|
44
|
+
# keeps 00:00-06:59 quiet). `history daily` = import + analyze; analyze is
|
|
45
|
+
# what refreshes the Board History rollups (token usage included) - import
|
|
46
|
+
# alone never does. Checkpoint resume + the rollup-freshness short-circuit
|
|
47
|
+
# keep a no-op tick cheap (I81).
|
|
48
|
+
- name: history-refresh
|
|
49
|
+
cron: "*/10 7-23 * * *"
|
|
50
|
+
command: spur --no-logo history daily
|
|
51
|
+
|
|
52
|
+
# 05:00 daily: full-corpus import (checkpoint resume self-heals gaps),
|
|
53
|
+
# then the twelve-section history-anatomy diagnostic report headless. On
|
|
54
|
+
# a cache hit the workflow skips the token-expensive model stages and
|
|
55
|
+
# republishes provenance (ADR-079), so a ready report costs no tokens.
|
|
56
|
+
- name: history-daily-report
|
|
57
|
+
cron: "3 5 * * *"
|
|
58
|
+
command: >-
|
|
59
|
+
spur --no-logo history import --source all &&
|
|
60
|
+
spur --no-logo workflow run config/workflows/history-anatomy.yaml --quiet
|
|
38
61
|
|
|
39
62
|
# ── Spur app section (validated by spurConfigSchema in
|
|
40
63
|
# @gobing-ai/spur-config) ──
|
|
@@ -85,13 +108,14 @@ agent:
|
|
|
85
108
|
# ~/.codex/config.toml, ~/.claude.json). The ids below are working examples,
|
|
86
109
|
# not vendor defaults — re-pin for your accounts.
|
|
87
110
|
#
|
|
88
|
-
# Pairing philosophy (operator guidance, 2026-08): a `native` pair — a
|
|
89
|
-
# model served by that vendor's own CLI (gpt-5.6-* via codex,
|
|
90
|
-
# claude, grok-4.6 via grok, gemini-3.x via agy) —
|
|
91
|
-
# same model routed through a third-party CLI,
|
|
92
|
-
# `portable` models (glm-5.x,
|
|
93
|
-
# carried by omp or pi at
|
|
94
|
-
#
|
|
111
|
+
# Pairing philosophy (operator guidance, 2026-08): a `native` pair — a
|
|
112
|
+
# vendor's model served by that vendor's own CLI (gpt-5.6-* via codex,
|
|
113
|
+
# claude-*-5 via claude, grok-4.6 via grok, gemini-3.x via agy) —
|
|
114
|
+
# consistently outperforms the same model routed through a third-party CLI,
|
|
115
|
+
# and belongs at the capable rungs. `portable` models (glm-5.x,
|
|
116
|
+
# deepseek-v4-*, …) are provider-agnostic and best carried by omp or pi at
|
|
117
|
+
# the cheap/standard rungs. The ladder below mixes both classes on purpose;
|
|
118
|
+
# measure pairings with the history plane before promoting
|
|
95
119
|
# a portable model into a capable rung.
|
|
96
120
|
# Live tiers: cheap | standard | capable-1 | capable-2 | capable-3
|
|
97
121
|
# (1=low quality within capable, 3=high). DECLARE tier — inference never
|
|
@@ -106,10 +130,21 @@ agent:
|
|
|
106
130
|
# agent: omp
|
|
107
131
|
# model: minimax/MiniMax-M3
|
|
108
132
|
# tier: cheap
|
|
109
|
-
- name:
|
|
110
|
-
agent:
|
|
111
|
-
|
|
112
|
-
|
|
133
|
+
- name: pi-dsv4-flash-volc
|
|
134
|
+
agent: pi
|
|
135
|
+
# tier: standard
|
|
136
|
+
executionCapabilities:
|
|
137
|
+
version: 1
|
|
138
|
+
axes:
|
|
139
|
+
fsRead:
|
|
140
|
+
state: available
|
|
141
|
+
provenance: native-known
|
|
142
|
+
fsWrite:
|
|
143
|
+
state: available
|
|
144
|
+
provenance: native-known
|
|
145
|
+
processSpawn:
|
|
146
|
+
state: available
|
|
147
|
+
provenance: native-known
|
|
113
148
|
# - name: omp-zai
|
|
114
149
|
# agent: omp
|
|
115
150
|
# model: zai/glm-5.2
|
|
@@ -155,7 +190,8 @@ agent:
|
|
|
155
190
|
# - role: reviewer
|
|
156
191
|
# - role: coder
|
|
157
192
|
# executor: omp
|
|
158
|
-
# purpose: "Pinned executor, role recorded alongside
|
|
193
|
+
# purpose: "Pinned executor, role recorded alongside
|
|
194
|
+
# (pin beats policy)"
|
|
159
195
|
|
|
160
196
|
rules:
|
|
161
197
|
paths:
|
|
@@ -180,18 +216,3 @@ tasks:
|
|
|
180
216
|
|
|
181
217
|
features:
|
|
182
218
|
dir: docs/features
|
|
183
|
-
|
|
184
|
-
# ── History (task 0549 — completion-triggered refresh) ──
|
|
185
|
-
# Opt-in: when work completes (task → done, pipeline run reaching
|
|
186
|
-
# terminal status), enqueue ONE coalesced `history.refresh` job on the
|
|
187
|
-
# embedded job queue (never inline). Bursts inside debounce_ms join the
|
|
188
|
-
# pending job; the covered window spans all completions. The job reuses
|
|
189
|
-
# `spur history daily` semantics (import-all fan-out with per-source
|
|
190
|
-
# isolation + analyze + artifact write) and is consumed by `spur serve`'s
|
|
191
|
-
# job worker. Debounce default 600000 ms (10 min) follows
|
|
192
|
-
# docs/tasks4/0548-import-cost-measurement.md (steady-state all-fanout
|
|
193
|
-
# import ≈ 20.6 s; 10 min comfortably bounds burst coalescing).
|
|
194
|
-
# history:
|
|
195
|
-
# refresh:
|
|
196
|
-
# on_completion: false # set true to enable; disable with no code edits
|
|
197
|
-
# debounce_ms: 600000 # coalescing window; floor 1000 ms
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"generatedAt": "2026-08-20T00:00:00.000Z",
|
|
4
|
-
"note": "Per-pipeline model-query + wall-clock cost budgets (task 0607 R3). modelQueries anchors the
|
|
4
|
+
"note": "Per-pipeline model-query + wall-clock cost budgets (task 0607 R3). modelQueries anchors the live workflow definitions' query lists — the SSOT, extracted by extractResolvedWorkflowFacts and guarded by composition-baseline.test.ts (0775 retired the snapshot this file previously anchored to). wallClockMs is a gross-regression ceiling anchored to measured real-run data (R5): the gate compares the median-of-sane terminal-run wall-clock against it. A null budget is unenforced until measured — never treated as 0. Raising a numeric budget requires a fresh recorded decision in the same commit (config/pipeline-budgets.json vs git HEAD); the check-pipeline-budgets gate fails a silent raise by name.",
|
|
5
5
|
"budgets": {
|
|
6
6
|
"task-pipeline": {
|
|
7
7
|
"modelQueries": 4,
|
|
@@ -25,17 +25,21 @@
|
|
|
25
25
|
"decision": null
|
|
26
26
|
},
|
|
27
27
|
"docs-pipeline": {
|
|
28
|
-
"modelQueries":
|
|
28
|
+
"modelQueries": 2,
|
|
29
29
|
"wallClockMs": null,
|
|
30
30
|
"tokenCostUsd": null,
|
|
31
|
-
"source": "No fixture (0607 option a) and only dry/short runs recorded in history (max 1s, n=4) — wall-clock budget unenforced until a real docs-pipeline run exists. Query budget 1 is
|
|
32
|
-
"decision":
|
|
31
|
+
"source": "No fixture (0607 option a) and only dry/short runs recorded in history (max 1s, n=4) — wall-clock budget unenforced until a real docs-pipeline run exists. Query budget 2 matches the live workflow definition's query list ['draft','verify'] (added by task 0704; 0607 R3 recorded 1, which is stale against the definition). 0754 R6: FIX, not raise — the value was wrong against the definition, the workflow's declared query count did not change.",
|
|
32
|
+
"decision": {
|
|
33
|
+
"date": "2026-09-03",
|
|
34
|
+
"wbs": "0754",
|
|
35
|
+
"note": "align docs-pipeline.modelQueries to the live workflow definition's query list ['draft','verify']; not a measured-cost raise — the workflow's declared query count was already 2 (added by 0704); the budget was the stale side, not the workflow. Per D8 Decision 11."
|
|
36
|
+
}
|
|
33
37
|
},
|
|
34
38
|
"pr-review": {
|
|
35
39
|
"modelQueries": 0,
|
|
36
40
|
"wallClockMs": null,
|
|
37
41
|
"tokenCostUsd": null,
|
|
38
|
-
"source": "Zero-query pipeline (
|
|
42
|
+
"source": "Zero-query pipeline (the live definition declares no model queries); query budget trivially 0 — the gate handles an empty list (0607 R3). No real full run recorded; wall-clock unenforced.",
|
|
39
43
|
"decision": null
|
|
40
44
|
}
|
|
41
45
|
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proportional route table — D8 S3 pilot scaffold (task 0758).
|
|
3
|
+
*
|
|
4
|
+
* The route table is a closed, mutually exhaustive predicate map. Every input
|
|
5
|
+
* resolves to exactly one route. Missing, unknown, or conflicting evidence
|
|
6
|
+
* always selects the safety path (never a fast path). The safety floor
|
|
7
|
+
* (proof-bracket guards, budget-unverifiable fail-closed dispatch,
|
|
8
|
+
* reviewer/executor independence, run-id confinement) holds on the fast
|
|
9
|
+
* path exactly as on the safety path — no route trades the safety floor
|
|
10
|
+
* for speed.
|
|
11
|
+
*
|
|
12
|
+
* See: docs/plans/2026-09-02-d8-proportional-workflow-upgrade-strategy.md §4
|
|
13
|
+
* (route table contract) and §7 (pilot exit bar).
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export type RouteId = 'safety' | 'fast' | 'skipped';
|
|
17
|
+
|
|
18
|
+
export interface RoutePredicate {
|
|
19
|
+
/** Stable id for the route this predicate selects. */
|
|
20
|
+
readonly route: RouteId;
|
|
21
|
+
/** Human label for review. */
|
|
22
|
+
readonly label: string;
|
|
23
|
+
/** Stable predicate id for evidence-bound replay. */
|
|
24
|
+
readonly id: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface RouteEvaluation {
|
|
28
|
+
readonly route: RouteId;
|
|
29
|
+
readonly predicateId: string;
|
|
30
|
+
readonly reason: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The closed route table for proportional routing.
|
|
35
|
+
*/
|
|
36
|
+
export const ROUTE_TABLE: readonly RoutePredicate[] = Object.freeze([
|
|
37
|
+
{
|
|
38
|
+
id: 'safety-default',
|
|
39
|
+
route: 'safety',
|
|
40
|
+
label: 'default / unknown / conflicting evidence',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'fast-complete',
|
|
44
|
+
route: 'fast',
|
|
45
|
+
label: 'complete and consistent evidence',
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: 'skipped-empty',
|
|
49
|
+
route: 'skipped',
|
|
50
|
+
label: 'empty task list / no work required',
|
|
51
|
+
},
|
|
52
|
+
]);
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Evaluate route for wrapup-pipeline over (tasks, mode).
|
|
56
|
+
* Mutually exhaustive predicates matching strategy §4 and prototype 0732 §2.
|
|
57
|
+
*/
|
|
58
|
+
export function evaluateWrapupRoute(input: { tasks?: unknown[] | string; mode?: string }): RouteEvaluation {
|
|
59
|
+
let taskCount = 0;
|
|
60
|
+
if (Array.isArray(input.tasks)) {
|
|
61
|
+
taskCount = input.tasks.length;
|
|
62
|
+
} else if (typeof input.tasks === 'string') {
|
|
63
|
+
try {
|
|
64
|
+
const parsed = JSON.parse(input.tasks);
|
|
65
|
+
if (Array.isArray(parsed)) taskCount = parsed.length;
|
|
66
|
+
} catch {
|
|
67
|
+
taskCount = 0;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (taskCount === 0) {
|
|
72
|
+
return {
|
|
73
|
+
route: 'skipped',
|
|
74
|
+
predicateId: 'skipped-empty',
|
|
75
|
+
reason: 'skipped:empty task list',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const mode = input.mode;
|
|
80
|
+
if (mode === 'fast') {
|
|
81
|
+
return {
|
|
82
|
+
route: 'fast',
|
|
83
|
+
predicateId: 'fast-complete',
|
|
84
|
+
reason: 'fast:evidence complete+consistent',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (!mode) {
|
|
88
|
+
return {
|
|
89
|
+
route: 'safety',
|
|
90
|
+
predicateId: 'safety-default',
|
|
91
|
+
reason: 'safety:missing evidence (mode empty)',
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
if (mode === 'unknown') {
|
|
95
|
+
return {
|
|
96
|
+
route: 'safety',
|
|
97
|
+
predicateId: 'safety-default',
|
|
98
|
+
reason: 'safety:unknown evidence quality',
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (mode === 'conflict') {
|
|
102
|
+
return {
|
|
103
|
+
route: 'safety',
|
|
104
|
+
predicateId: 'safety-default',
|
|
105
|
+
reason: 'safety:conflicting evidence',
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
route: 'safety',
|
|
110
|
+
predicateId: 'safety-default',
|
|
111
|
+
reason: `safety:unrecognized evidence (mode=${mode})`,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Generic evaluateRoute resolving to safety or fast based on input.
|
|
117
|
+
*/
|
|
118
|
+
export function evaluateRoute(input: RouteInput): RouteEvaluation {
|
|
119
|
+
if (input.costCoverage >= 0.8 && input.proofBinding === 'current' && input.reviewerIndependent) {
|
|
120
|
+
return {
|
|
121
|
+
route: 'fast',
|
|
122
|
+
predicateId: 'fast-complete',
|
|
123
|
+
reason: 'fast:evidence complete+consistent',
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
route: 'safety',
|
|
128
|
+
predicateId: 'safety-default',
|
|
129
|
+
reason: 'safety:default / unknown / conflicting evidence',
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Input shape for route evaluation. Frozen interface so the follow-up
|
|
135
|
+
* pilot session extends it without changing call sites.
|
|
136
|
+
*/
|
|
137
|
+
export interface RouteInput {
|
|
138
|
+
readonly runId: string;
|
|
139
|
+
readonly definitionDigest: string;
|
|
140
|
+
readonly evidenceRefs: readonly string[];
|
|
141
|
+
readonly costCoverage: number;
|
|
142
|
+
readonly proofBinding: 'current' | 'stale' | 'missing';
|
|
143
|
+
readonly reviewerIndependent: boolean;
|
|
144
|
+
readonly runIdConfined: boolean;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Safety floor invariant. Every route — safety or fast — must hold these
|
|
149
|
+
* conditions. The fast path cannot trade any of them for speed. This
|
|
150
|
+
* function is the gate the pilot's run-bound evidence writer must
|
|
151
|
+
* enforce before recording a non-safety route.
|
|
152
|
+
*/
|
|
153
|
+
export function safetyFloorHolds(input: RouteInput): boolean {
|
|
154
|
+
return input.proofBinding === 'current' && input.reviewerIndependent === true && input.runIdConfined === true;
|
|
155
|
+
}
|
|
@@ -74,5 +74,5 @@ rules:
|
|
|
74
74
|
# Synchronous template & workflow resolvers:
|
|
75
75
|
- "apps/cli/src/commands/task.ts" # sync loadTemplateContent/Bodies callbacks
|
|
76
76
|
- "apps/cli/src/workflow/make-lifecycle-adapter.ts" # sync resolveWorkflowPath lookup
|
|
77
|
-
- "packages/app/src/services/workflow-service.ts" #
|
|
77
|
+
- "packages/app/src/services/workflow-service.ts" # composition advisory source (0775: facts extracted from the live definition inside the synchronous validate path; the baseline snapshot sync read was retired)
|
|
78
78
|
- "apps/cli/src/release-ops.ts" # sync manifest/workspace reads + Bun.write rewrites inside the git release flow (task 0617); mirrors task.ts sync-template exemption
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# task-pipeline proportional migration plan (task 0759 WIP)
|
|
2
|
+
|
|
3
|
+
**Status:** WIP — gated on 0758 pilot completion. The route table contract proven
|
|
4
|
+
on wrapup-pipeline and task-lifecycle is reused unchanged. No code change to
|
|
5
|
+
task-pipeline.yaml until the pilot clears the bar.
|
|
6
|
+
|
|
7
|
+
## Prerequisites (all satisfied at the time of this commit)
|
|
8
|
+
|
|
9
|
+
- **0751** — proof primitives fail-closed: ✅ done in this branch.
|
|
10
|
+
- **0752** — resume-to-definition binding: ✅ done in this branch.
|
|
11
|
+
- **0753** — workflow-seam repairs (command.gate timeout, run-id validation, nested
|
|
12
|
+
composition, dry-probe escalation): ✅ done in this branch.
|
|
13
|
+
- **0757** — re-measure gate recorded Option A continues (wrapup-pipeline 40 real
|
|
14
|
+
terminal runs, task-lifecycle 27, both pilots clear ≥5): ✅ done in this branch.
|
|
15
|
+
- **0758** — route table proven on the two pilots with ≥5 real terminal runs each
|
|
16
|
+
and ≥80% run-scoped cost row coverage: **WIP** in this branch. The scaffold is in
|
|
17
|
+
`config/proportional-route-table.ts`; the pilot runs accumulate in a follow-up
|
|
18
|
+
session.
|
|
19
|
+
|
|
20
|
+
## Migration contract (frozen; no changes expected from the pilot)
|
|
21
|
+
|
|
22
|
+
1. **Same closed route table.** `config/proportional-route-table.ts` is the data
|
|
23
|
+
structure. task-pipeline adopts it unchanged. The fast-path predicates are
|
|
24
|
+
per-pilot: wrapup-pipeline and task-lifecycle get theirs from the 0758 pilot;
|
|
25
|
+
task-pipeline gets its predicates after the pilot proves the shape.
|
|
26
|
+
|
|
27
|
+
2. **Same safety floor.** `safetyFloorHolds()` is the gate. proofBinding
|
|
28
|
+
`current`, reviewerIndependent `true`, runIdConfined `true` — all three must
|
|
29
|
+
hold on every route.
|
|
30
|
+
|
|
31
|
+
3. **Same evidence-writing interface.** The run-bound evidence writer (R4/R5) is
|
|
32
|
+
implemented once in the 0758 follow-up; task-pipeline uses the same writer.
|
|
33
|
+
No per-workflow duplication.
|
|
34
|
+
|
|
35
|
+
4. **Same revertability property (R7).** task-pipeline gets a per-workflow
|
|
36
|
+
`proportional_routing: enabled|disabled` switch (default: disabled until the
|
|
37
|
+
migration lands). Rollback is one config change, not an engine change.
|
|
38
|
+
|
|
39
|
+
## What task-pipeline.yaml changes when the migration lands
|
|
40
|
+
|
|
41
|
+
A single block at the top of `config/workflows/task-pipeline.yaml`:
|
|
42
|
+
|
|
43
|
+
```yaml
|
|
44
|
+
proportional_routing:
|
|
45
|
+
enabled: true
|
|
46
|
+
route_table: ../proportional-route-table.ts#ROUTE_TABLE
|
|
47
|
+
safety_floor: ../proportional-route-table.ts#safetyFloorHolds
|
|
48
|
+
evidence_writer: ../proportional-route-evidence-writer.ts # lands in 0758 follow-up
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
No change to states, transitions, actions, or guards in this commit. The
|
|
52
|
+
migration lands only after the 0758 pilot clears and the operator approves
|
|
53
|
+
the change (plan §7 S5 operator consent gate).
|
|
54
|
+
|
|
55
|
+
## Acceptance criteria mapping (task 0759)
|
|
56
|
+
|
|
57
|
+
- R1 — closed route table: ✅ frozen in `config/proportional-route-table.ts`; task-pipeline
|
|
58
|
+
adopts unchanged.
|
|
59
|
+
- R2 — safety floor: ✅ frozen in `safetyFloorHolds()`; task-pipeline enforces the
|
|
60
|
+
same invariant.
|
|
61
|
+
- R3 — no regression: PENDING — requires real terminal runs after migration
|
|
62
|
+
with pre/post comparison.
|
|
63
|
+
- R4 — real engine, no fixture: ✅ invariant is a design property; no fixture
|
|
64
|
+
substitution in this branch.
|
|
65
|
+
- R5 — verified-outcome binding: ✅ already shipped via 0751 R4 (the
|
|
66
|
+
`proofBinding: current` guard); the migration reuses it.
|
|
67
|
+
- R6 — measured bounds: ✅ none adjusted in this branch; per-bound justification
|
|
68
|
+
required at migration time.
|
|
69
|
+
- R7 — revertable: ✅ `proportional_routing.enabled: false` is the rollback.
|
|
70
|
+
|
|
71
|
+
## Open items (all blocked on 0758 pilot)
|
|
72
|
+
|
|
73
|
+
- Per-pilot fast-path predicate values (R1).
|
|
74
|
+
- Run-bound evidence writer implementation (R4, R5).
|
|
75
|
+
- Pre/post terminal-run comparison (R3).
|
|
76
|
+
- Operator consent for the task-pipeline.yaml change (plan §7 S5).
|
|
77
|
+
|
|
78
|
+
Status moves from WIP to done when the 0758 pilot clears the bar and the
|
|
79
|
+
follow-up session implements the per-pilot predicates and the evidence writer.
|
|
@@ -74,6 +74,11 @@ All product development work goes through the harness by default.
|
|
|
74
74
|
Use `spur task path <wbs> --json` only when a filesystem consumer needs the absolute path. Both
|
|
75
75
|
commands resolve across configured task folders. Reuse the first `show` response within the run.
|
|
76
76
|
|
|
77
|
+
**Corpus checks (constitution T10/T11):** checker-policy changes require one explicit unsuppressed
|
|
78
|
+
audit, recording/reconciling essential failures without waivers. Ordinary commit prep checks changed
|
|
79
|
+
task/feature documents and their required linked evidence, not the whole corpus. Do not regenerate
|
|
80
|
+
an acceptance baseline or insert audits into routine batch/wrapup loops.
|
|
81
|
+
|
|
77
82
|
**Platform fallback:** Platforms without slash commands and/or subagents still use the harness.
|
|
78
83
|
Install the plugin through Superskill for the target platform, then use skills `sp:spur-dev`,
|
|
79
84
|
`sp:spur-cli`, `sp:code-verification` (and related) plus the `spur` CLI. Do not invent a parallel
|
|
@@ -200,6 +200,8 @@ names the docs that must be touched **in the same commit / same change**:
|
|
|
200
200
|
| T7 | The doc map or process changes | this file → re-sync `AGENTS.md` (§4.4) → propagate to sibling projects |
|
|
201
201
|
| T8 | A multi-wave batch is planned | schedule "doc sync" as an **explicit work item** — same-commit discipline does not survive on memory alone |
|
|
202
202
|
| T9 | A design or feature item is added/changed | the satellite **first** (`docs/design/<slug>.md` or `docs/features/<id>_<slug>.md`), **then** its index row in `04`/`05` — same change (§4.5 rule 5) |
|
|
203
|
+
| T10 | Corpus checker policy changes | Run one explicit unsuppressed audit plus focused rule tests; record/reconcile exposed essential failures without waivers. |
|
|
204
|
+
| T11 | Ordinary task/feature commit prep | Check changed documents and their required linked evidence, not the whole corpus. Never regenerate an acceptance baseline or add routine audit callers. |
|
|
203
205
|
|
|
204
206
|
## 6. Edit principles per file
|
|
205
207
|
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
# Reliability (aligned with task-pipeline / ADR-043):
|
|
7
7
|
# - check is a soft probe (status file + always exit 0) so red gates route to fix
|
|
8
8
|
# via transitions, not a raw lifecycle abort that skips the failed terminal
|
|
9
|
+
# - qualityGateCmd is TRUSTED CONFIG ONLY — executed via `sh -c` so compound
|
|
10
|
+
# commands (`&&`, `||`) preserve their exit status; never interpolated task data
|
|
9
11
|
# - fix is pure slash /sp:dev-fixall against the same qualityGateCmd (single-sourced)
|
|
10
12
|
# - qualityGateMaxFixAttempts bounds the fix loop; exhausted → failed (fail-closed)
|
|
11
13
|
# - Bun package scripts only for gate surface (no alternate package-manager CLIs)
|
|
@@ -13,7 +15,8 @@
|
|
|
13
15
|
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
14
16
|
name: basic
|
|
15
17
|
kind: state-machine
|
|
16
|
-
description: The canonical implement-check-fix-until-pass loop
|
|
18
|
+
description: The canonical implement-check-fix-until-pass loop (example definition; override qualityGateCmd per project)
|
|
19
|
+
version: "1"
|
|
17
20
|
iterationBound: 8
|
|
18
21
|
initialState: implement
|
|
19
22
|
terminalStates:
|
|
@@ -57,7 +60,7 @@ states:
|
|
|
57
60
|
mkdir -p .spur/run &&
|
|
58
61
|
STATUS_FILE=".spur/run/$__runId-basic-gate.status" &&
|
|
59
62
|
set +e &&
|
|
60
|
-
|
|
63
|
+
sh -c "$qualityGateCmd"; gate_rc=$?; set -e &&
|
|
61
64
|
if [ "$gate_rc" -eq 0 ]; then
|
|
62
65
|
printf 'PASS\n' > "$STATUS_FILE";
|
|
63
66
|
else
|