@gobing-ai/spur 0.4.2 → 0.4.4
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/docs/verdict-confidence.yaml +68 -0
- package/config/rules/quality/coverage-gate.yaml +13 -0
- package/config/rules/recommended-pre-check.yaml +6 -2
- package/config/rules/strict/runtime-boundaries.yaml +10 -0
- package/config/rules/surface/check-cli-surface.yaml +1 -3
- package/config/script-placement-baseline.json +1 -19
- package/config/templates/AGENTS.md +7 -0
- package/config/workflows/feature-lifecycle.yaml +46 -1
- package/config/workflows/feature-verification.yaml +20 -1
- package/config/workflows/task-lifecycle.yaml +50 -0
- package/config/workflows/task-pipeline.yaml +15 -9
- package/package.json +10 -10
- package/plugins/sp/README.md +22 -16
- package/plugins/sp/agents/super-reviewer.md +46 -0
- package/plugins/sp/bunfig.toml +8 -5
- package/plugins/sp/commands/dev-changelog.md +1 -1
- package/plugins/sp/commands/dev-gitmsg.md +6 -2
- package/plugins/sp/commands/dev-handover.md +1 -0
- package/plugins/sp/commands/dev-job-dump.md +2 -1
- package/plugins/sp/commands/dev-refine.md +14 -2
- package/plugins/sp/commands/dev-refineall.md +7 -1
- package/plugins/sp/commands/dev-review.md +1 -0
- 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/history-anatomy.generated.mjs +18 -18
- package/plugins/sp/lib/idea-handoff.generated.mjs +91 -91
- 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 +1425 -212
- package/plugins/sp/lib/plan-projection.generated.d.mts +150 -0
- package/plugins/sp/lib/plan-projection.generated.mjs +623 -9
- package/plugins/sp/lib/quality-gate.generated.d.mts +1 -0
- package/plugins/sp/lib/quality-gate.generated.mjs +13809 -13969
- package/plugins/sp/lib/residual-scan.generated.d.mts +20 -1
- package/plugins/sp/lib/residual-scan.generated.mjs +120 -25
- package/plugins/sp/lib/run-summary-core.ts +34 -370
- package/plugins/sp/lib/step-profile.generated.mjs +8 -8
- package/plugins/sp/lib/transcript.ts +37 -228
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/batch-plan.mjs +619 -11
- package/plugins/sp/scripts/batch-preflight.mjs +57 -13
- package/plugins/sp/scripts/batch-preflight.ts +35 -295
- package/plugins/sp/scripts/commit-guard.mjs +6 -6
- package/plugins/sp/scripts/daily-summary/daily-summary.mjs +127 -108
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +111 -104
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +13 -12
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +7 -265
- package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +7 -4
- package/plugins/sp/scripts/dogfood-testing/validate-report.ts +6 -272
- package/plugins/sp/scripts/feature-verification-steps.mjs +20 -125
- package/plugins/sp/scripts/feature-verification-steps.ts +57 -181
- package/plugins/sp/scripts/history-anatomy-cache.mjs +19 -19
- package/plugins/sp/scripts/idea-handoff.mjs +2 -2
- package/plugins/sp/scripts/inline-run-dispatch.mjs +25 -24
- package/plugins/sp/scripts/inline-run-dispatch.ts +1 -21
- package/plugins/sp/scripts/inline-run-setup.mjs +164 -137
- package/plugins/sp/scripts/inline-run-setup.ts +1 -21
- package/plugins/sp/scripts/persist-out-check.mjs +5 -5
- package/plugins/sp/scripts/pr-reviewing.mjs +13 -13
- package/plugins/sp/scripts/quality-gate.mjs +13730 -13887
- package/plugins/sp/scripts/residual-scan.mjs +129 -31
- package/plugins/sp/scripts/residual-scan.ts +16 -13
- package/plugins/sp/scripts/run-summary.mjs +166 -112
- package/plugins/sp/scripts/run-summary.ts +7 -5
- package/plugins/sp/scripts/script-root.mjs +6 -6
- package/plugins/sp/scripts/session-timeline.mjs +62 -35
- package/plugins/sp/scripts/task-diffstat.mjs +19 -9
- package/plugins/sp/scripts/task-diffstat.ts +8 -39
- package/plugins/sp/scripts/workflow-step-profile.mjs +12 -12
- package/plugins/sp/scripts/wrapup-drift-probe.mjs +9 -9
- package/plugins/sp/scripts/wrapup-steps.mjs +25 -25
- 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/next-router/SKILL.md +3 -1
- package/plugins/sp/skills/next-router/references/routing-table.md +9 -3
- 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-cli/references/tasks.md +24 -4
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +8 -1
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +37 -11
- package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +1 -1
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +19 -6
- 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/flag-glossary.md +4 -2
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +49 -12
- package/schemas/section-matrix.schema.json +3 -2
- package/spur.js +66591 -73262
- package/web/_astro/{BoardApp.B3GQZvj8.js → BoardApp.Niubpe6z.js} +1 -1
- package/web/_astro/{BoardApp.BT7L2kSb.js → BoardApp.hF0RAIrg.js} +150 -150
- package/web/_astro/TaskDetail.DJpapt38.js +1 -0
- package/web/_astro/{arc.vaT7tPOB.js → arc.BgKcK0i8.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.DRMmTjkN.js → architectureDiagram-3BPJPVTR.ChP-Ig0V.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.cla0LO_q.js → blockDiagram-GPEHLZMM.BVaCBoeN.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.D4YAIBKn.js → c4Diagram-AAUBKEIU.CHSDpEO5.js} +1 -1
- package/web/_astro/channel.DkpvN2tD.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.BrSJbMRZ.js → chunk-2J33WTMH.MYRcYVRa.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.CuLMgKF5.js → chunk-4BX2VUAB.S5j22Zqa.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.zkvgu54F.js → chunk-55IACEB6.gciRPvmZ.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.DxWkACh8.js → chunk-727SXJPM.CdqrSQkX.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.42XrmIlH.js → chunk-AQP2D5EJ.CyokdHT3.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.Bao2wMK9.js → chunk-FMBD7UC4.Bg9KJhjW.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.BhydojD1.js → chunk-ND2GUHAM.1gxxI-vD.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.CmZ82g-H.js → chunk-QZHKN3VN.DDEYtCKU.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.Blr83Gt_.js → classDiagram-4FO5ZUOK.Ch3zi37T.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.Blr83Gt_.js → classDiagram-v2-Q7XG4LA2.Ch3zi37T.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.qmfv0SPn.js → cose-bilkent-S5V4N54A.D2q5AINI.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.BtELhXPR.js → cynefin-OW5HDTMX.BA9kC2YT.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.AWmaAvXo.js → dagre-BM42HDAG.CHO08nRp.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.DdH34KDi.js → diagram-2AECGRRQ.BfFQt0LW.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.BmNPix1k.js → diagram-5GNKFQAL.CHMwMTe4.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.BNQd_Ppo.js → diagram-KO2AKTUF.BJ-7gK-w.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.BAPgDi0e.js → diagram-LMA3HP47.C5xqcc0t.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.DZ4zyLdC.js → diagram-OG6HWLK6.C2pQjfrC.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.Q1TySMmw.js → erDiagram-TEJ5UH35._FoNyxgC.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.BXhFBicG.js → flowDiagram-I6XJVG4X.B_ENkT2I.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.vHSgcuo2.js → ganttDiagram-6RSMTGT7.B3q8h77C.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.Bi1f73C7.js → gitGraphDiagram-PVQCEYII.QWZFDboZ.js} +1 -1
- package/web/_astro/index.BFyW13JJ.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.DPvXZEWm.js → infoDiagram-5YYISTIA.Cs6eEc92.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.Cz829fsV.js → ishikawaDiagram-YF4QCWOH.BU9DJMHA.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.enRnB9Me.js → journeyDiagram-JHISSGLW.D35k6Jpe.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.DnTNk6d2.js → kanban-definition-UN3LZRKU.DBqcAT4m.js} +1 -1
- package/web/_astro/{linear.BzGnBwNL.js → linear.D-LSNK8W.js} +1 -1
- package/web/_astro/{mermaid.core.CSbTpIVz.js → mermaid.core.CSruWVOa.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.DGJDy8zT.js → mindmap-definition-RKZ34NQL.BiFaUsfj.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.HSPdi5_8.js → pieDiagram-4H26LBE5.D1KTKqb9.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.6Gv8mz2Y.js → quadrantDiagram-W4KKPZXB.DBHflsMG.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.CnBCtFwa.js → requirementDiagram-4Y6WPE33.hwX9CNdk.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.5M8VWAu8.js → sankeyDiagram-5OEKKPKP.DkI8mJiE.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.DN41XN-U.js → sequenceDiagram-3UESZ5HK.DMz7jBDx.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.ErOO3W8j.js → stateDiagram-AJRCARHV.B6Ef2xJ1.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.B_ZHICM-.js → stateDiagram-v2-BHNVJYJU.CgoaufB9.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.CWdosr07.js → timeline-definition-PNZ67QCA.B3c8h1t-.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.BHzYjNV5.js → vennDiagram-CIIHVFJN.BdHPAxAb.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.h8x4IwPX.js → wardleyDiagram-YWT4CUSO.BrJXPPAj.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.RTapW2X8.js → xychartDiagram-2RQKCTM6.CMH5yFsV.js} +1 -1
- package/web/index.html +2 -2
- package/web/spur_logo.svg +1 -1
- package/config/proportional-route-table.ts +0 -155
- package/plugins/sp/scripts/daily-summary/logger.ts +0 -28
- package/web/_astro/TaskDetail.Cxov1_N_.js +0 -1
- 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
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* (`--no-summary` skips it). Inputs: one or more `spur workflow progress <run-id> --json` files
|
|
5
5
|
* (`--progress [<label>=]<file>`) and the host transcript (resolved like session-timeline, or
|
|
6
6
|
* `--transcript`), OR the close-written per-run summary files (`--rollup [<label>=]<file>`, 1146 R3).
|
|
7
|
-
* Measurement
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Measurement lives in `packages/app/src/workflow/run-summary.ts` (reached through the generated
|
|
8
|
+
* pure bundle entry); presentation and host IO live in `../lib/run-summary-core` (ADR-130 glue
|
|
9
|
+
* budget). This script is argv parsing and dispatch. Reporting only: no transcript or no timed
|
|
10
|
+
* attempts → an `n/a` result, exit 0. Node builtins only.
|
|
10
11
|
*/
|
|
11
12
|
import { readFileSync } from 'node:fs';
|
|
12
13
|
import { getEnvVars } from '../lib/env';
|
|
@@ -18,7 +19,7 @@ import {
|
|
|
18
19
|
renderRollupMarkdown,
|
|
19
20
|
renderSummaryMarkdown,
|
|
20
21
|
} from '../lib/run-summary-core';
|
|
21
|
-
import { resolveTranscript } from '../lib/transcript';
|
|
22
|
+
import { parseRows, resolveTranscript } from '../lib/transcript';
|
|
22
23
|
|
|
23
24
|
// Re-exported so the suite and any consumer keep one import path for the whole surface.
|
|
24
25
|
export * from '../lib/run-summary-core';
|
|
@@ -90,7 +91,8 @@ export function main(
|
|
|
90
91
|
const [label, file] = eq > 0 ? [arg.slice(0, eq), arg.slice(eq + 1)] : [undefined, arg];
|
|
91
92
|
return { label, progress: JSON.parse(readFileSync(file, 'utf8')) as Progress };
|
|
92
93
|
});
|
|
93
|
-
const
|
|
94
|
+
const { rows, skippedLines } = parseRows(readFileSync(resolved.path, 'utf8').split('\n'));
|
|
95
|
+
const summary = buildRunSummary(rows, inputs, { since, skippedLines });
|
|
94
96
|
summary.gate = gateFromReceipt(process.cwd(), inputs[0]?.progress.runId ?? '');
|
|
95
97
|
write(
|
|
96
98
|
`${markdown ? renderSummaryMarkdown(summary) : JSON.stringify({ ...summary, transcript: resolved.path })}\n`,
|
|
@@ -119,11 +119,11 @@ function main(argv, env = getEnvVars(), options = {}) {
|
|
|
119
119
|
process.exit(main(process.argv.slice(2)));
|
|
120
120
|
}
|
|
121
121
|
export {
|
|
122
|
-
|
|
123
|
-
main,
|
|
124
|
-
digestScriptSet,
|
|
125
|
-
SOURCE_REPO_MARKER,
|
|
126
|
-
SCRIPT_ROOT_USAGE,
|
|
122
|
+
PROJECT_SCRIPTS_DIR,
|
|
127
123
|
SCRIPT_ROOT_TWIN_REL,
|
|
128
|
-
|
|
124
|
+
SCRIPT_ROOT_USAGE,
|
|
125
|
+
SOURCE_REPO_MARKER,
|
|
126
|
+
digestScriptSet,
|
|
127
|
+
main,
|
|
128
|
+
runScriptRoot
|
|
129
129
|
};
|
|
@@ -13,9 +13,12 @@ function getEnvVars() {
|
|
|
13
13
|
import { existsSync, readdirSync } from "node:fs";
|
|
14
14
|
import { homedir } from "node:os";
|
|
15
15
|
import { join } from "node:path";
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
var
|
|
16
|
+
|
|
17
|
+
// plugins/sp/lib/plan-projection.generated.mjs
|
|
18
|
+
var NON_ABORTING_PREFLIGHT_CODES = new Set([
|
|
19
|
+
"L4.scenario-unverified",
|
|
20
|
+
"L4.verifying-incomplete-tasks"
|
|
21
|
+
]);
|
|
19
22
|
var zeroTokens = () => ({ input: 0, cacheCreate: 0, cacheRead: 0, output: 0 });
|
|
20
23
|
function formatDuration(ms) {
|
|
21
24
|
const s = Math.round(ms / 1000);
|
|
@@ -35,6 +38,23 @@ function formatTokenSplit(t) {
|
|
|
35
38
|
const nonCached = t.input + t.cacheCreate + t.output;
|
|
36
39
|
return `${formatTokens(nonCached + t.cacheRead)} / ${formatTokens(nonCached)}`;
|
|
37
40
|
}
|
|
41
|
+
var OPERATOR_TOOLS = new Set(["AskUserQuestion", "ask_user_question"]);
|
|
42
|
+
function firstTextContent(row) {
|
|
43
|
+
const content = row.message?.content;
|
|
44
|
+
if (typeof content === "string")
|
|
45
|
+
return content;
|
|
46
|
+
if (!Array.isArray(content))
|
|
47
|
+
return;
|
|
48
|
+
return content.find((b) => b.type === "text")?.text;
|
|
49
|
+
}
|
|
50
|
+
function skillInvocationPrompt(text) {
|
|
51
|
+
const name = /^\s*<skill name="([^"]+)"/.exec(text)?.[1];
|
|
52
|
+
if (name === undefined)
|
|
53
|
+
return;
|
|
54
|
+
const close = text.lastIndexOf("</skill>");
|
|
55
|
+
const args = close < 0 ? "" : text.slice(close + "</skill>".length).trim();
|
|
56
|
+
return args === "" ? `/${name}` : `/${name} ${args}`;
|
|
57
|
+
}
|
|
38
58
|
function promptText(row) {
|
|
39
59
|
if (row.type === "custom_message") {
|
|
40
60
|
if (row.display === false)
|
|
@@ -59,22 +79,6 @@ function promptText(row) {
|
|
|
59
79
|
return;
|
|
60
80
|
return content.find((b) => b.type === "text")?.text;
|
|
61
81
|
}
|
|
62
|
-
function firstTextContent(row) {
|
|
63
|
-
const content = row.message?.content;
|
|
64
|
-
if (typeof content === "string")
|
|
65
|
-
return content;
|
|
66
|
-
if (!Array.isArray(content))
|
|
67
|
-
return;
|
|
68
|
-
return content.find((b) => b.type === "text")?.text;
|
|
69
|
-
}
|
|
70
|
-
function skillInvocationPrompt(text) {
|
|
71
|
-
const name = /^\s*<skill name="([^"]+)"/.exec(text)?.[1];
|
|
72
|
-
if (name === undefined)
|
|
73
|
-
return;
|
|
74
|
-
const close = text.lastIndexOf("</skill>");
|
|
75
|
-
const args = close < 0 ? "" : text.slice(close + "</skill>".length).trim();
|
|
76
|
-
return args === "" ? `/${name}` : `/${name} ${args}`;
|
|
77
|
-
}
|
|
78
82
|
function sumTokens(all) {
|
|
79
83
|
const total = zeroTokens();
|
|
80
84
|
for (const t of all)
|
|
@@ -103,21 +107,21 @@ function accumulate(acc, row, ts) {
|
|
|
103
107
|
if (row.type === "message") {
|
|
104
108
|
const m = row.message;
|
|
105
109
|
if (m?.role === "assistant") {
|
|
106
|
-
const
|
|
107
|
-
for (const b of
|
|
110
|
+
const blocks = Array.isArray(m.content) ? m.content : [];
|
|
111
|
+
for (const b of blocks) {
|
|
108
112
|
if (b.type === "toolCall" && b.id) {
|
|
109
113
|
acc.toolIds.add(b.id);
|
|
110
114
|
if (b.name && OPERATOR_TOOLS.has(b.name))
|
|
111
115
|
acc.asks.set(b.id, ts);
|
|
112
116
|
}
|
|
113
117
|
}
|
|
114
|
-
const
|
|
115
|
-
if (
|
|
118
|
+
const u = m.usage;
|
|
119
|
+
if (u && row.id)
|
|
116
120
|
acc.messages.set(row.id, {
|
|
117
|
-
input:
|
|
118
|
-
cacheCreate:
|
|
119
|
-
cacheRead:
|
|
120
|
-
output:
|
|
121
|
+
input: u.input ?? 0,
|
|
122
|
+
cacheCreate: u.cacheWrite ?? 0,
|
|
123
|
+
cacheRead: u.cacheRead ?? 0,
|
|
124
|
+
output: u.output ?? 0
|
|
121
125
|
});
|
|
122
126
|
} else if (m?.role === "toolResult" && m.toolCallId && acc.asks.has(m.toolCallId)) {
|
|
123
127
|
acc.askMs += ts - (acc.asks.get(m.toolCallId) ?? ts);
|
|
@@ -146,6 +150,29 @@ function accumulate(acc, row, ts) {
|
|
|
146
150
|
});
|
|
147
151
|
}
|
|
148
152
|
}
|
|
153
|
+
var SHELL_TOOLS = new Set(["bash", "shell", "exec", "exec_command", "run_command", "command"]);
|
|
154
|
+
var SUBAGENT_TOOLS = new Set(["agent", "task", "invoke_subagent", "subagent", "dispatch_agent"]);
|
|
155
|
+
var LOWERCASE_OPERATOR_TOOLS = new Set(["askuserquestion", "ask_user_question"]);
|
|
156
|
+
var SENSITIVE_PREFIXES = ["drizzle/", "packages/config/", ".github/", "plugins/sp/hooks/"];
|
|
157
|
+
var SENSITIVE_GLOBS = ["apps/server/src/**/auth*", "**/*secret*", "**/*.sql"];
|
|
158
|
+
function globToRegex(glob) {
|
|
159
|
+
const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replaceAll("**/", "\x00").replaceAll("**", "\x01").replaceAll("*", "[^/]*").replaceAll("\x00", "(?:.*/)?").replaceAll("\x01", ".*");
|
|
160
|
+
return new RegExp(`^${escaped}$`, "s");
|
|
161
|
+
}
|
|
162
|
+
var SENSITIVE_MATCHERS = [
|
|
163
|
+
...SENSITIVE_PREFIXES.map((prefix) => ({
|
|
164
|
+
label: prefix,
|
|
165
|
+
test: (path) => path.startsWith(prefix)
|
|
166
|
+
})),
|
|
167
|
+
...SENSITIVE_GLOBS.map((glob) => {
|
|
168
|
+
const regex = globToRegex(glob);
|
|
169
|
+
return { label: glob, test: (path) => regex.test(path) };
|
|
170
|
+
})
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
// plugins/sp/lib/transcript.ts
|
|
174
|
+
var SESSION_ID = /^[A-Za-z0-9_-]+$/;
|
|
175
|
+
var PI_ROLES = new Set(["user", "assistant", "toolResult"]);
|
|
149
176
|
function sniffFormat(rows) {
|
|
150
177
|
for (const row of rows) {
|
|
151
178
|
if (row.type === "message" && PI_ROLES.has(row.message?.role ?? ""))
|
|
@@ -320,13 +347,13 @@ function main(argv, env = getEnvVars(), write = (s) => process.stdout.write(s),
|
|
|
320
347
|
}
|
|
321
348
|
process.exit(main(process.argv.slice(2)));
|
|
322
349
|
export {
|
|
323
|
-
|
|
324
|
-
resolveTranscript,
|
|
325
|
-
parseGroups,
|
|
326
|
-
main,
|
|
327
|
-
formatTokens,
|
|
328
|
-
formatTokenSplit,
|
|
329
|
-
formatDuration,
|
|
350
|
+
SESSION_TIMELINE_USAGE,
|
|
330
351
|
buildTimeline,
|
|
331
|
-
|
|
352
|
+
formatDuration,
|
|
353
|
+
formatTokenSplit,
|
|
354
|
+
formatTokens,
|
|
355
|
+
main,
|
|
356
|
+
parseGroups,
|
|
357
|
+
resolveTranscript,
|
|
358
|
+
zeroSegmentReason
|
|
332
359
|
};
|
|
@@ -11,7 +11,15 @@ function getEnvVars() {
|
|
|
11
11
|
return process.env;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
// plugins/sp/
|
|
14
|
+
// plugins/sp/lib/plan-projection.generated.mjs
|
|
15
|
+
var NON_ABORTING_PREFLIGHT_CODES = new Set([
|
|
16
|
+
"L4.scenario-unverified",
|
|
17
|
+
"L4.verifying-incomplete-tasks"
|
|
18
|
+
]);
|
|
19
|
+
var OPERATOR_TOOLS = new Set(["AskUserQuestion", "ask_user_question"]);
|
|
20
|
+
var SHELL_TOOLS = new Set(["bash", "shell", "exec", "exec_command", "run_command", "command"]);
|
|
21
|
+
var SUBAGENT_TOOLS = new Set(["agent", "task", "invoke_subagent", "subagent", "dispatch_agent"]);
|
|
22
|
+
var LOWERCASE_OPERATOR_TOOLS = new Set(["askuserquestion", "ask_user_question"]);
|
|
15
23
|
var SENSITIVE_PREFIXES = ["drizzle/", "packages/config/", ".github/", "plugins/sp/hooks/"];
|
|
16
24
|
var SENSITIVE_GLOBS = ["apps/server/src/**/auth*", "**/*secret*", "**/*.sql"];
|
|
17
25
|
function globToRegex(glob) {
|
|
@@ -32,6 +40,8 @@ function sensitiveReasonForPath(path) {
|
|
|
32
40
|
const match = SENSITIVE_MATCHERS.find((entry) => entry.test(path));
|
|
33
41
|
return match ? `matches sensitive pattern ${match.label}` : null;
|
|
34
42
|
}
|
|
43
|
+
|
|
44
|
+
// plugins/sp/scripts/task-diffstat.ts
|
|
35
45
|
function defaultGitRunner(cwd) {
|
|
36
46
|
return (args) => {
|
|
37
47
|
const run = spawnSync("git", args, { cwd, encoding: "utf8" });
|
|
@@ -74,9 +84,9 @@ function runDiffstat(env, options = {}, git = defaultGitRunner(options.cwd)) {
|
|
|
74
84
|
const failSafe = (reason) => {
|
|
75
85
|
process.stderr.write(`task-diffstat: ${reason} \u2014 failing safe (sensitive)
|
|
76
86
|
`);
|
|
77
|
-
const
|
|
78
|
-
writeArtifact(abs(relResult),
|
|
79
|
-
return { ...
|
|
87
|
+
const row = { files: 0, insertions: 0, deletions: 0, paths: [], sensitive: true };
|
|
88
|
+
writeArtifact(abs(relResult), row);
|
|
89
|
+
return { ...row, resultFile: relResult, exitCode: 0 };
|
|
80
90
|
};
|
|
81
91
|
let stdoutIsArtifact = false;
|
|
82
92
|
try {
|
|
@@ -162,10 +172,10 @@ function main(argv, env = getEnvVars(), options = {}) {
|
|
|
162
172
|
process.exit(main(process.argv.slice(2)));
|
|
163
173
|
}
|
|
164
174
|
export {
|
|
165
|
-
|
|
166
|
-
runDiffstat,
|
|
167
|
-
main,
|
|
168
|
-
TASK_DIFFSTAT_USAGE,
|
|
175
|
+
SENSITIVE_GLOBS,
|
|
169
176
|
SENSITIVE_PREFIXES,
|
|
170
|
-
|
|
177
|
+
TASK_DIFFSTAT_USAGE,
|
|
178
|
+
main,
|
|
179
|
+
runDiffstat,
|
|
180
|
+
sensitiveReasonForPath
|
|
171
181
|
};
|
|
@@ -65,45 +65,14 @@ export interface DiffstatResult extends Diffstat {
|
|
|
65
65
|
exitCode: number;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
export const SENSITIVE_PREFIXES = ['drizzle/', 'packages/config/', '.github/', 'plugins/sp/hooks/'] as const;
|
|
77
|
-
export const SENSITIVE_GLOBS = ['apps/server/src/**/auth*', '**/*secret*', '**/*.sql'] as const;
|
|
78
|
-
|
|
79
|
-
// Glob → RegExp: `**` + `/` matches any-or-no directories, bare `**` any characters, `*` within one segment.
|
|
80
|
-
function globToRegex(glob: string): RegExp {
|
|
81
|
-
const escaped = glob
|
|
82
|
-
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
83
|
-
.replaceAll('**/', '\u0000')
|
|
84
|
-
.replaceAll('**', '\u0001')
|
|
85
|
-
.replaceAll('*', '[^/]*')
|
|
86
|
-
.replaceAll('\u0000', '(?:.*/)?')
|
|
87
|
-
.replaceAll('\u0001', '.*');
|
|
88
|
-
return new RegExp(`^${escaped}$`, 's');
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const SENSITIVE_MATCHERS: Array<{ label: string; test: (path: string) => boolean }> = [
|
|
92
|
-
...SENSITIVE_PREFIXES.map((prefix) => ({
|
|
93
|
-
label: prefix,
|
|
94
|
-
test: (path: string): boolean => path.startsWith(prefix),
|
|
95
|
-
})),
|
|
96
|
-
...SENSITIVE_GLOBS.map((glob) => {
|
|
97
|
-
const regex = globToRegex(glob);
|
|
98
|
-
return { label: glob, test: (path: string): boolean => regex.test(path) };
|
|
99
|
-
}),
|
|
100
|
-
];
|
|
101
|
-
|
|
102
|
-
/** Sensitive-pattern match for one path, or null — mirrors wrapup-drift-probe's driftReasonForPath. */
|
|
103
|
-
export function sensitiveReasonForPath(path: string): string | null {
|
|
104
|
-
const match = SENSITIVE_MATCHERS.find((entry) => entry.test(path));
|
|
105
|
-
return match ? `matches sensitive pattern ${match.label}` : null;
|
|
106
|
-
}
|
|
68
|
+
// Task 1176: the sensitive-path policy (0943 R2c) moved to the workflow/task owner —
|
|
69
|
+
// `packages/app/src/workflow/task-triage.ts`, reached through the generated pure bundle. That policy
|
|
70
|
+
// still forces the safety lane for any changed path under the frozen sensitive list before the
|
|
71
|
+
// `task-triage` decide runs, so sensitive work is never lane-negotiated with a model; this entry
|
|
72
|
+
// keeps git capture and the output artifact, and re-exports the policy for existing callers.
|
|
73
|
+
import { SENSITIVE_GLOBS, SENSITIVE_PREFIXES, sensitiveReasonForPath } from '../lib/plan-projection.generated.mjs';
|
|
74
|
+
|
|
75
|
+
export { SENSITIVE_GLOBS, SENSITIVE_PREFIXES, sensitiveReasonForPath };
|
|
107
76
|
|
|
108
77
|
function defaultGitRunner(cwd?: string): GitRunner {
|
|
109
78
|
return (args: string[]): GitRunResult => {
|
|
@@ -322,17 +322,17 @@ function main(argv) {
|
|
|
322
322
|
process.exit(main(process.argv.slice(2)));
|
|
323
323
|
}
|
|
324
324
|
export {
|
|
325
|
-
|
|
326
|
-
rowFlags,
|
|
327
|
-
parseStepProfileCliArgs,
|
|
328
|
-
nonDryRuns,
|
|
329
|
-
nearestRankP50,
|
|
330
|
-
main,
|
|
331
|
-
formatStepProfileHuman,
|
|
332
|
-
extractExecutions,
|
|
333
|
-
buildStepProfile,
|
|
334
|
-
buildRows,
|
|
335
|
-
STEP_PROFILE_USAGE,
|
|
325
|
+
DEFAULT_LAST,
|
|
336
326
|
DEFAULT_WINDOW_SEC,
|
|
337
|
-
|
|
327
|
+
STEP_PROFILE_USAGE,
|
|
328
|
+
buildRows,
|
|
329
|
+
buildStepProfile,
|
|
330
|
+
extractExecutions,
|
|
331
|
+
formatStepProfileHuman,
|
|
332
|
+
main,
|
|
333
|
+
nearestRankP50,
|
|
334
|
+
nonDryRuns,
|
|
335
|
+
parseStepProfileCliArgs,
|
|
336
|
+
rowFlags,
|
|
337
|
+
runStepProfileCli
|
|
338
338
|
};
|
|
@@ -114,13 +114,13 @@ function runDriftProbe(env, options = {}, spur = defaultSpurRunner(env, options.
|
|
|
114
114
|
mkdirSync(abs(join(".spur", "run")), { recursive: true });
|
|
115
115
|
writeFileSync(abs(relModeFile), `
|
|
116
116
|
`);
|
|
117
|
-
const finish = (clean,
|
|
118
|
-
writeFileSync(abs(relProbeFile), `${JSON.stringify({ clean, reasons
|
|
117
|
+
const finish = (clean, reasons, paths) => {
|
|
118
|
+
writeFileSync(abs(relProbeFile), `${JSON.stringify({ clean, reasons, paths })}
|
|
119
119
|
`);
|
|
120
120
|
writeFileSync(abs(relModeFile), clean ? `fast
|
|
121
121
|
` : `
|
|
122
122
|
`);
|
|
123
|
-
return { clean, reasons
|
|
123
|
+
return { clean, reasons, paths, probeFile: relProbeFile, modeFile: relModeFile, exitCode: 0 };
|
|
124
124
|
};
|
|
125
125
|
let tasks;
|
|
126
126
|
try {
|
|
@@ -180,11 +180,11 @@ function main(argv, env = getEnvVars(), options = {}) {
|
|
|
180
180
|
process.exit(main(process.argv.slice(2)));
|
|
181
181
|
}
|
|
182
182
|
export {
|
|
183
|
-
|
|
184
|
-
runDriftProbe,
|
|
185
|
-
main,
|
|
186
|
-
driftReasonForPath,
|
|
187
|
-
changedPathsOf,
|
|
183
|
+
DOC_OWNED_SURFACES,
|
|
188
184
|
WRAPUP_DRIFT_PROBE_USAGE,
|
|
189
|
-
|
|
185
|
+
changedPathsOf,
|
|
186
|
+
driftReasonForPath,
|
|
187
|
+
main,
|
|
188
|
+
runDriftProbe,
|
|
189
|
+
solutionSectionOf
|
|
190
190
|
};
|
|
@@ -296,7 +296,7 @@ function runMetrics(env, options = {}) {
|
|
|
296
296
|
const parsed = JSON.parse(shown.stdout);
|
|
297
297
|
const frontmatter = parsed.frontmatter !== null && typeof parsed.frontmatter === "object" ? parsed.frontmatter : {};
|
|
298
298
|
const featureId = String(jqPick(frontmatter.feature_id, parsed.feature_id, ""));
|
|
299
|
-
const
|
|
299
|
+
const status = String(jqPick(frontmatter.status, parsed.status, "unknown"));
|
|
300
300
|
const durable = join(".spur", "memory", "evidence", `${wbs}-verdict.json`);
|
|
301
301
|
const hasDurable = existsSync(abs(durable));
|
|
302
302
|
const verdictPath = hasDurable ? durable : join(".spur", "run", `${wbs}-verdict.json`);
|
|
@@ -308,7 +308,7 @@ function runMetrics(env, options = {}) {
|
|
|
308
308
|
`);
|
|
309
309
|
}
|
|
310
310
|
const timestamp = new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
311
|
-
const row = { wbs, feature_id: featureId, status
|
|
311
|
+
const row = { wbs, feature_id: featureId, status, verdict, timestamp };
|
|
312
312
|
try {
|
|
313
313
|
appendFileSync(abs(relMetricsFile), `${JSON.stringify(row)}
|
|
314
314
|
`);
|
|
@@ -600,28 +600,28 @@ function main(argv, env = getEnvVars(), options = {}) {
|
|
|
600
600
|
process.exit(main(process.argv.slice(2)));
|
|
601
601
|
}
|
|
602
602
|
export {
|
|
603
|
-
|
|
604
|
-
verdictFromTestingSection,
|
|
605
|
-
taskStatusOf,
|
|
606
|
-
runMetrics,
|
|
607
|
-
runFeatureTransition,
|
|
608
|
-
runDocSyncSnapshot,
|
|
609
|
-
runDocSyncScope,
|
|
610
|
-
runDocSupersessionCheck,
|
|
611
|
-
resolveTasks,
|
|
612
|
-
preflightFeature,
|
|
613
|
-
porcelainPath,
|
|
614
|
-
main,
|
|
615
|
-
jqPick,
|
|
616
|
-
isDocSyncAllowedPath,
|
|
617
|
-
docSyncScopeViolations,
|
|
618
|
-
classifySync,
|
|
619
|
-
WRAPUP_STEPS_USAGE,
|
|
620
|
-
WBS_PATTERN,
|
|
621
|
-
SUPERSESSION_PIN,
|
|
622
|
-
SNAPSHOT_MISSING,
|
|
623
|
-
GIT_UNAVAILABLE_MARKER,
|
|
624
|
-
DOC_SYNC_SUPERSESSION_STATUS_SUFFIX,
|
|
603
|
+
DOC_SYNC_SCOPE_STATUS_SUFFIX,
|
|
625
604
|
DOC_SYNC_SNAPSHOT_SUFFIX,
|
|
626
|
-
|
|
605
|
+
DOC_SYNC_SUPERSESSION_STATUS_SUFFIX,
|
|
606
|
+
GIT_UNAVAILABLE_MARKER,
|
|
607
|
+
SNAPSHOT_MISSING,
|
|
608
|
+
SUPERSESSION_PIN,
|
|
609
|
+
WBS_PATTERN,
|
|
610
|
+
WRAPUP_STEPS_USAGE,
|
|
611
|
+
classifySync,
|
|
612
|
+
docSyncScopeViolations,
|
|
613
|
+
isDocSyncAllowedPath,
|
|
614
|
+
jqPick,
|
|
615
|
+
main,
|
|
616
|
+
porcelainPath,
|
|
617
|
+
preflightFeature,
|
|
618
|
+
resolveTasks,
|
|
619
|
+
runDocSupersessionCheck,
|
|
620
|
+
runDocSyncScope,
|
|
621
|
+
runDocSyncSnapshot,
|
|
622
|
+
runFeatureTransition,
|
|
623
|
+
runMetrics,
|
|
624
|
+
taskStatusOf,
|
|
625
|
+
verdictFromTestingSection,
|
|
626
|
+
writeRouteReason
|
|
627
627
|
};
|
|
@@ -82,6 +82,12 @@ not have and work you were not asked to do.
|
|
|
82
82
|
- **Never implement a requirement belonging to another WBS**, even when it looks like a
|
|
83
83
|
prerequisite. If the target genuinely cannot proceed without it, stop and say so — a blocked task
|
|
84
84
|
is a cheap finding; a two-task diff costs the reverts.
|
|
85
|
+
- **Flip the target's `## Plan` boxes as each step completes.** They are this pass's progress
|
|
86
|
+
ledger, and the record-time residual sweep treats an *unchecked* Plan box as blocking: it
|
|
87
|
+
downgrades the verdict to PARTIAL and closes the `done` gate
|
|
88
|
+
(`docs/design/task-residual-sweep.md`), which costs a remediation cycle and a manual re-record.
|
|
89
|
+
Leave every finished step ticked through `spur task update <wbs> --section Plan --from-file`, and
|
|
90
|
+
if you deliberately skipped one, say so in the Solution change map.
|
|
85
91
|
|
|
86
92
|
Why this is a rule and not a nicety: driving task 0486 lost several hours to exactly this. Task 0485
|
|
87
93
|
was committed but still `todo` in the tree, and two different executors (omp run `ca130182`, claude
|
|
@@ -165,7 +171,7 @@ that single full gate.
|
|
|
165
171
|
1. The **changed-path matrix** below (narrow behavior tests + affected consumers);
|
|
166
172
|
2. the resolved `quality-gate` script in `light` mode (source-repo probe, else the installed twin) (biome on changed files, per-workspace typecheck, rule presets `rules:pre`/`rules:post`, related tests).
|
|
167
173
|
|
|
168
|
-
Neither step takes the
|
|
174
|
+
Neither step takes the full project gate. The full gate is named only for the pipeline's `test` and `test-recheck` hops.
|
|
169
175
|
|
|
170
176
|
| Changed surface | Required targeted tests | Required typechecks |
|
|
171
177
|
| --- | --- | --- |
|
|
@@ -92,7 +92,8 @@ Before declaring a task `done`, run this lightweight checklist. It catches the m
|
|
|
92
92
|
- [ ] Solution section contains `file:line` citations for every changed file.
|
|
93
93
|
- [ ] API/library claims in changed docs carry a source citation (URL or `path:line`) and an honest
|
|
94
94
|
confidence level (HIGH/MEDIUM/LOW) per source-driven-development; the deterministic advisory backstop is
|
|
95
|
-
the `cited-version-strings` / `cited-api-claims`
|
|
95
|
+
the `cited-version-strings` / `cited-api-claims` / `cited-api-claims-confidence` rules
|
|
96
|
+
(`.spur/rules/docs/sourced-api-claims.yaml`, run ad hoc — not wired into a preset).
|
|
96
97
|
- [ ] Review section has P1–P4 findings table (even if all rows are empty — the table itself proves review happened).
|
|
97
98
|
|
|
98
99
|
This checklist runs as part of the verify step when the task reaches `testing` status. It does not replace the formal gates — it augments them with between-pipeline hygiene checks.
|
|
@@ -202,6 +202,10 @@ citation in the SAME cell, or lint rejects the row with `evidence-citation` (blo
|
|
|
202
202
|
- The rule is cell-local: a citation in another row or a "receipt above" cross-reference does not
|
|
203
203
|
satisfy it. Marker-free receipts (test counts, reviewed-log notes) need no citation. The check is
|
|
204
204
|
presence-only — which citation proves which claim is not deterministically decidable.
|
|
205
|
+
- The same two predicates (`hasClaimMarker` / `hasCitationForm` in
|
|
206
|
+
`packages/app/src/services/verify-answer-lint.ts`) gate the review report's findings table through
|
|
207
|
+
`residual-scan review-gate` as the blocking `review-contract` category — one definition of
|
|
208
|
+
"external claim" and one set of accepted citation forms for both paths.
|
|
205
209
|
|
|
206
210
|
## Hedged evidence is rejected on MET rows
|
|
207
211
|
|
|
@@ -82,11 +82,11 @@ The command forwards these via `$ARGUMENTS`:
|
|
|
82
82
|
> <!-- pipeline-tokens:end -->
|
|
83
83
|
> matched as a **distinct hyphen-word**
|
|
84
84
|
> (machine-checked by
|
|
85
|
-
> [`detectPipelineDriving`](../../
|
|
85
|
+
> [`detectPipelineDriving`](../../lib/dogfood-pipeline.ts);
|
|
86
86
|
> see [§Pipeline-driving word-boundary contract](#pipeline-driving-word-boundary-contract))
|
|
87
87
|
> → `⚠ pipeline-driving testee detected; pass --max-retry 0 (observe-only) or --max-retry N (fix mode, tree mutation acknowledged)`.
|
|
88
88
|
> - **Mutating `--fix` modes** — `--fix all` / `--fix blockers-first`, boundary-guarded via
|
|
89
|
-
> [`hasMutatingFixMode`](../../
|
|
89
|
+
> [`hasMutatingFixMode`](../../lib/dogfood-pipeline.ts) (never matches
|
|
90
90
|
> `--fix none` / `--focus all` / `--prefix all`). No pipeline token required: a verify/review leg
|
|
91
91
|
> with `--fix all` mutates the tree on its own (0280 dogfood P2, task 0293). Honesty note:
|
|
92
92
|
> `--max-retry 0` here bounds **the driver only** — the testee still mutates the tree — so the
|
|
@@ -64,7 +64,7 @@ Run the steps in order. Each step's depth lives in its reference; this file is t
|
|
|
64
64
|
change a frontier feature's status, the report **leads with the drift summary** ("sync first")
|
|
65
65
|
and the ranking is computed over the *post-sync* status view. Details:
|
|
66
66
|
[references/signal-derivation.md](references/signal-derivation.md) §0.
|
|
67
|
-
1. **Assemble the candidate set.** `spur feature list --json`; drop `done`/`cancelled`, drop
|
|
67
|
+
1. **Assemble the candidate set.** `spur feature list --json`; drop `done`/`cancelled`/`deprecated`, drop
|
|
68
68
|
structural containers (has children, no own open tasks — defect D1), drop the map/feature that
|
|
69
69
|
owns this investigation if present. The `group` tag is **not** a reliable container marker (D3).
|
|
70
70
|
2. **Gate on actionability.** Read the frontier predicate **at runtime** from
|
|
@@ -142,7 +142,8 @@ but redundant. See the glossary entry for the disambiguation in full.
|
|
|
142
142
|
|
|
143
143
|
Never: reimplement the pipeline inside the router; default `todo` to `--mode full`; infer targets
|
|
144
144
|
from git/chat; auto-`--merge` on wrap; auto-author rules; use `dev-runall` as the feature default;
|
|
145
|
-
bypass lifecycle guards (`--no-lifecycle`) to force progress
|
|
145
|
+
bypass lifecycle guards (`--no-lifecycle`) to force progress; select a `deprecated` task for any
|
|
146
|
+
lifecycle hop (TABLE A A10 — frozen work has no forward step; un-deprecate or cancel it first).
|
|
146
147
|
|
|
147
148
|
## Common Rationalizations
|
|
148
149
|
|
|
@@ -158,6 +159,7 @@ bypass lifecycle guards (`--no-lifecycle`) to force progress.
|
|
|
158
159
|
- Dispatching more than one primary command in a single invocation.
|
|
159
160
|
- `--auto` used to choose among HITL candidates.
|
|
160
161
|
- A route that is not in TABLE A/B/C (invented dispatch targets).
|
|
162
|
+
- A `deprecated` WBS routed into any lifecycle command instead of stopping (A10/ADR-137).
|
|
161
163
|
- `--once` honored by merely skipping the re-invoke while leaving `--next` in the child argv.
|
|
162
164
|
- Omitting the `dev-next:` prefix on stop/plan messages.
|
|
163
165
|
|
|
@@ -54,7 +54,7 @@ it or when chaining (chain links already use `--auto` per existing refine→run
|
|
|
54
54
|
|
|
55
55
|
## 1. TABLE A — Task primary routes (corpus status)
|
|
56
56
|
|
|
57
|
-
Statuses from `TASK_STATUSES`: backlog | todo | wip | testing | blocked | done | cancelled.
|
|
57
|
+
Statuses from `TASK_STATUSES`: backlog | todo | wip | testing | blocked | done | cancelled | deprecated.
|
|
58
58
|
|
|
59
59
|
| # | Precondition | Primary signal | Dispatch | probe | Chain on success? | Stop / notes |
|
|
60
60
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
@@ -67,8 +67,9 @@ Statuses from `TASK_STATUSES`: backlog | todo | wip | testing | blocked | done |
|
|
|
67
67
|
| A7 | `status == blocked` | `task.show` + Notes/History for blocker text | `/sp:dev-handover "<blocker summary from Notes or status>"` | no | no | **STOP** after handover doc; human unblocks |
|
|
68
68
|
| A8 | `status == done` | `task.show.status` | `/sp:dev-wrap <wbs>` (`--agent <value>` preserved when the originating command supplied it; omission remains omission) | no | **no** (wrap's `--merge` is irreversible HITL; never auto) | Operator may re-invoke with `--merge` explicitly later |
|
|
69
69
|
| A9 | `status == cancelled` | `task.show.status` | *(none)* | no | no | **STOP** — no-op; print "cancelled — nothing to advance" |
|
|
70
|
+
| A10 | `status == deprecated` | `task.show.status` | *(none)* | no | no | **STOP** — frozen and retained (ADR-137); nothing to advance. Print "deprecated — frozen; un-deprecate with `spur task update <wbs> todo`, or cancel it once removed". Never falls through to another row |
|
|
70
71
|
|
|
71
|
-
**Priority when multiple TABLE A rows could match:** lowest row number wins (A1–
|
|
72
|
+
**Priority when multiple TABLE A rows could match:** lowest row number wins (A1–A10 are mutually
|
|
72
73
|
exclusive by status except A2/A3 which split `todo`).
|
|
73
74
|
|
|
74
75
|
## 2. TABLE B — Feature-ID path (rollup)
|
|
@@ -84,7 +85,7 @@ When input is a feature id:
|
|
|
84
85
|
| B4 | No frontier tasks AND `feature.status == backlog` AND AC placeholder/invalid | `feature check` / AC body | `/sp:dev-plan` continuation is not WBS-shaped — **STOP** with: run `/sp:dev-plan --feature <id>` or fix AC then `spur feature check <id>` | no | no | Planning-half entry needs a description; do not invent idea text |
|
|
85
86
|
| B5 | No frontier tasks AND feature has valid AC but zero tasks | feature + empty task roster | **STOP** with: `/sp:dev-plan --feature <id>` (decompose/batch-create) | no | no | Avoid auto-running plan without operator description confirmation in v1 |
|
|
86
87
|
| B6 | No frontier tasks AND all child tasks `done` AND feature in `active`\|`verifying` | task list all done | `/sp:dev-wrapall --feature <id>` (`--agent <value>` preserved when the originating command supplied it; omission remains omission) | no | no | Advances feature via wrapall guards; `--merge` never auto |
|
|
87
|
-
| B7 | No frontier tasks AND mixed cancelled
|
|
88
|
+
| B7 | No frontier tasks AND mixed terminal only (any combination drawn from `cancelled`, `done`, `deprecated`) | statuses | **STOP** — print summary; suggest feature status update manually | no | no | |
|
|
88
89
|
| B8 | `feature.status == blocked` | feature status | **STOP** — print feature blocked; do not pick tasks | no | no | |
|
|
89
90
|
|
|
90
91
|
**Frontier selection algorithm (B3) — precise:**
|
|
@@ -101,6 +102,11 @@ if pick is null → TABLE B rows B4–B7
|
|
|
101
102
|
else → TABLE A(pick)
|
|
102
103
|
```
|
|
103
104
|
|
|
105
|
+
The candidate set is exactly the open statuses: `done`, `cancelled` **and `deprecated`** are never
|
|
106
|
+
frontier candidates (ADR-137 — a frozen task is retained, not resumable), and a `deprecated`
|
|
107
|
+
dependency is not a satisfied one, so it keeps its dependents out of the frontier exactly like any
|
|
108
|
+
unfinished dep.
|
|
109
|
+
|
|
104
110
|
## 3. TABLE C — Light-gate short-circuit (probe=yes rows only)
|
|
105
111
|
|
|
106
112
|
**Order is sequential short-circuit** (first match wins). Never parallel in v1 (simpler, lower
|
|
@@ -167,7 +167,7 @@ rule at the source, so no per-path shim is needed:
|
|
|
167
167
|
| `spur agent loop` | `AgentService.run` per drained iteration | Same resolution path as `spur agent run`; inherits its own `SPUR_ROLE` |
|
|
168
168
|
| `spur serve` team supervisor → member | spawns `spur agent loop` | Member inherits the supervisor's `SPUR_ROLE` (recursive by construction) |
|
|
169
169
|
| Native subagent fan-out (this skill's default) | in-session `Task()`/`Skill()` | In-session subagents share the host session; when they themselves dispatch, the host's role is already in the session env — the rule holds at the next `spur agent run` boundary |
|
|
170
|
-
| `
|
|
170
|
+
| `scripts/commands/skill-eval.ts` | `spawnSync('spur agent run', …)` per scenario | Out of scope: a top-level eval harness, not a fan-out — no dispatcher role exists to inherit; each scenario is an independent top-level run (documented, no shim) |
|
|
171
171
|
|
|
172
172
|
The inventory is recorded in `docs/04_DESIGN.md` § `spur agent run` envelope (`roleOrigin`).
|
|
173
173
|
Paths that shell out to `spur agent run` without the rule would silently drop or double-attribute
|
|
@@ -101,10 +101,13 @@ line: `Verification: HIGH|MEDIUM|LOW — <first failing check and deviation, or
|
|
|
101
101
|
- **Honest confidence** — the stated HIGH/MEDIUM/LOW matches the evidence: HIGH requires a check against the pinned source this session; MEDIUM/LOW items are labeled, and LOW is never presented as fact.
|
|
102
102
|
- **HIGH** — all three checks pass. **MEDIUM** — coverage intact but some citations are off-version (latest docs for a pinned dependency) or secondary-source only. **LOW** — an API/library claim lacks any citation, or a LOW-confidence claim was asserted as fact.
|
|
103
103
|
|
|
104
|
-
Deterministic backstop: the `cited-version-strings
|
|
105
|
-
(`.spur/rules/docs/sourced-api-claims.yaml
|
|
106
|
-
assert version
|
|
107
|
-
|
|
104
|
+
Deterministic backstop: the `cited-version-strings`, `cited-api-claims` and
|
|
105
|
+
`cited-api-claims-confidence` rules (`.spur/rules/docs/sourced-api-claims.yaml`) flag markdown lines
|
|
106
|
+
that assert a version string or a time-anchored API claim with no citation anchor on the same line,
|
|
107
|
+
and claim lines that carry no HIGH/MEDIUM/LOW level. They are an info-severity advisory backstop for
|
|
108
|
+
this contract and are deliberately NOT wired into `recommended-pre-check` — run them ad hoc with
|
|
109
|
+
`spur rule run --file .spur/rules/docs/sourced-api-claims.yaml`. The checks above and the SECUA
|
|
110
|
+
checklist remain the enforcement points.
|
|
108
111
|
|
|
109
112
|
## Common Rationalizations
|
|
110
113
|
|
|
@@ -8,8 +8,9 @@ see_also:
|
|
|
8
8
|
# spur builder - release plumbing
|
|
9
9
|
|
|
10
10
|
`spur builder` is the CLI for **version bumps and release tags**. It wraps the internal
|
|
11
|
-
`
|
|
12
|
-
`scripts/commands/release.ts`
|
|
11
|
+
`release-ops` implementation behind a public two-verb surface. The repo-local forwarder
|
|
12
|
+
(`scripts/commands/release.ts`) and its `spur-dev` dispatcher cases were retired in task 1173; the
|
|
13
|
+
root `bun run bump-ver` / `bun run drop-tags` names route to `spur builder` directly. Package ids are
|
|
13
14
|
the unscoped short names (`@gobing-ai/spur` → `spur`); the released set and the aggregate tag are
|
|
14
15
|
discovered from the repo's own workspace manifests, so the same code serves any git+semver
|
|
15
16
|
monorepo.
|