@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
|
@@ -3,19 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
// plugins/sp/scripts/feature-verification-steps.ts
|
|
5
5
|
import { spawnSync } from "child_process";
|
|
6
|
-
import {
|
|
7
|
-
closeSync,
|
|
8
|
-
existsSync,
|
|
9
|
-
mkdirSync,
|
|
10
|
-
openSync,
|
|
11
|
-
readdirSync,
|
|
12
|
-
readFileSync,
|
|
13
|
-
realpathSync,
|
|
14
|
-
renameSync,
|
|
15
|
-
statSync,
|
|
16
|
-
writeFileSync
|
|
17
|
-
} from "fs";
|
|
18
|
-
import { join } from "path";
|
|
6
|
+
import { existsSync } from "fs";
|
|
19
7
|
import { fileURLToPath } from "url";
|
|
20
8
|
|
|
21
9
|
// plugins/sp/lib/env.ts
|
|
@@ -25,12 +13,11 @@ function getEnvVar(name, fallback) {
|
|
|
25
13
|
}
|
|
26
14
|
|
|
27
15
|
// plugins/sp/scripts/feature-verification-steps.ts
|
|
28
|
-
var SAFE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
29
16
|
function bundleEntry() {
|
|
30
17
|
return fileURLToPath(new URL("../lib/inline-run.generated.mjs", import.meta.url));
|
|
31
18
|
}
|
|
32
19
|
function sourceModeError(spurBin) {
|
|
33
|
-
return new Error(`source mode: refusing to load packages/app/src/index.ts (spurBin=${JSON.stringify(spurBin)}). ` + "That entry is not the feature-verification contract surface \u2014 it does not export " + "
|
|
20
|
+
return new Error(`source mode: refusing to load packages/app/src/index.ts (spurBin=${JSON.stringify(spurBin)}). ` + "That entry is not the feature-verification contract surface \u2014 it does not export " + "runFeatureVerification. Fix: run in bundle mode so " + "plugins/sp/lib/inline-run.generated.mjs is loaded (omit --module-mode, or pass --module-mode bundle) " + "and regenerate that bundle if it is missing. Reinstalling the sp plugin does not add the operation to the app source entry.");
|
|
34
21
|
}
|
|
35
22
|
function resolveModuleMode(raw) {
|
|
36
23
|
if (raw === "" || raw === "bundle")
|
|
@@ -50,125 +37,33 @@ async function loadModule(spurBin, mode) {
|
|
|
50
37
|
}
|
|
51
38
|
async function requireModule(entry, mode) {
|
|
52
39
|
const mod = await import(entry);
|
|
53
|
-
const missing = [
|
|
54
|
-
"startFeatureVerificationReceipt",
|
|
55
|
-
"completeFeatureVerificationReceipt",
|
|
56
|
-
"captureFeatureReceiptDigest",
|
|
57
|
-
"resolveWorkflowDefinition",
|
|
58
|
-
"splitLaunchCommand",
|
|
59
|
-
"openInlineRunProjectDb",
|
|
60
|
-
"ArtifactDao"
|
|
61
|
-
].filter((k) => typeof mod[k] !== "function");
|
|
40
|
+
const missing = ["runFeatureVerification"].filter((k) => typeof mod[k] !== "function");
|
|
62
41
|
if (missing.length > 0) {
|
|
63
42
|
throw new Error(`feature-verification-steps: mode=${mode} \u2014 application entry ${entry} is missing feature-verification seams (${missing.join(", ")}). ` + "Fix: the generated inline bundle is stale or incomplete \u2014 run `bun run build:bundle` in a source checkout, or reinstall the sp plugin.");
|
|
64
43
|
}
|
|
65
44
|
return mod;
|
|
66
45
|
}
|
|
67
|
-
function
|
|
68
|
-
if (!SAFE_ID_RE.test(id) || id.includes("..")) {
|
|
69
|
-
throw new Error(`refusing unsafe ${kind}: ${id}`);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
function findFeatureFile(featureDir, featureId) {
|
|
73
|
-
if (!existsSync(featureDir))
|
|
74
|
-
return;
|
|
75
|
-
const name = readdirSync(featureDir).find((n) => n.startsWith(`${featureId}_`) && n.endsWith(".md"));
|
|
76
|
-
return name === undefined ? undefined : join(featureDir, name);
|
|
77
|
-
}
|
|
78
|
-
var nodeFsShim = {
|
|
79
|
-
realPath: realpathSync,
|
|
80
|
-
readDir: readdirSync,
|
|
81
|
-
stat: (path) => {
|
|
82
|
-
try {
|
|
83
|
-
return statSync(path);
|
|
84
|
-
} catch (error) {
|
|
85
|
-
if (error.code === "ENOENT")
|
|
86
|
-
return null;
|
|
87
|
-
throw error;
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
ensureDir: (dir) => {
|
|
91
|
-
mkdirSync(dir, { recursive: true });
|
|
92
|
-
},
|
|
93
|
-
writeFile: (path, body) => {
|
|
94
|
-
writeFileSync(path, body);
|
|
95
|
-
},
|
|
96
|
-
rename: (src, dest) => {
|
|
97
|
-
renameSync(src, dest);
|
|
98
|
-
},
|
|
99
|
-
readFile: (path) => {
|
|
100
|
-
return readFileSync(path, "utf8");
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
async function verify(featureId, runId, cmdOverride, spurBin, mode) {
|
|
104
|
-
assertSafeId("feature id", featureId);
|
|
105
|
-
assertSafeId("run id", runId);
|
|
106
|
-
const cwd = process.cwd();
|
|
46
|
+
async function verify(featureId, runId, cmdOverride, spurBin, mode, confidenceOverride, ackLowConfidence) {
|
|
107
47
|
const mod = await loadModule(spurBin, mode);
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
const selected = await mod.resolveWorkflowDefinition(cwd, "feature-verification");
|
|
111
|
-
const vars = selected.workflow.vars;
|
|
112
|
-
const configuredCmd = typeof vars?.verificationCmd === "string" && vars.verificationCmd.length > 0 ? vars.verificationCmd : "bun run spur-check-feature";
|
|
113
|
-
const effectiveCmd = cmdOverride !== "" ? cmdOverride : configuredCmd;
|
|
114
|
-
const featureFile = findFeatureFile(join(cwd, "docs", "features"), featureId);
|
|
115
|
-
if (featureFile === undefined) {
|
|
116
|
-
throw new Error(`feature ${featureId} not found under ${join(cwd, "docs", "features")}`);
|
|
117
|
-
}
|
|
118
|
-
const featureContent = readFileSync(featureFile, "utf8");
|
|
119
|
-
const learningsPath = join(cwd, ".spur", "context", "learnings.md");
|
|
120
|
-
const learningsContent = existsSync(learningsPath) ? readFileSync(learningsPath, "utf8") : undefined;
|
|
121
|
-
const beforeDigest = await mod.captureFeatureReceiptDigest(cwd, featureContent, learningsContent);
|
|
122
|
-
const receipt = await mod.startFeatureVerificationReceipt(nodeFsShim, runDir, {
|
|
48
|
+
const result = await mod.runFeatureVerification({
|
|
49
|
+
workdir: process.cwd(),
|
|
123
50
|
featureId,
|
|
124
51
|
runId,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
sourcePath: selected.path,
|
|
129
|
-
layer: selected.layer,
|
|
130
|
-
definitionDigest: selected.digest
|
|
131
|
-
},
|
|
132
|
-
verificationCmd: effectiveCmd,
|
|
133
|
-
inputDigest: beforeDigest
|
|
52
|
+
cmdOverride,
|
|
53
|
+
confidenceOverride,
|
|
54
|
+
ackLowConfidence
|
|
134
55
|
});
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
await new mod.ArtifactDao(db.adapter).record({
|
|
138
|
-
runId,
|
|
139
|
-
path: mod.featureReceiptPaths(runDir, featureId, runId).runScoped,
|
|
140
|
-
kind: "feature-verification"
|
|
141
|
-
});
|
|
142
|
-
} finally {
|
|
143
|
-
db.close();
|
|
144
|
-
}
|
|
145
|
-
const logPath = join(runDir, `${runId}-feature-verification.log`);
|
|
146
|
-
const launch = mod.splitLaunchCommand(effectiveCmd, "feature-verification verificationCmd");
|
|
147
|
-
if ("error" in launch) {
|
|
148
|
-
await mod.completeFeatureVerificationReceipt(nodeFsShim, runDir, receipt, {
|
|
149
|
-
status: "FAIL",
|
|
150
|
-
inputDigest: beforeDigest
|
|
151
|
-
});
|
|
152
|
-
console.log(`feature-verification-steps: ${featureId} verification FAIL (${launch.error})`);
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
const logFd = openSync(logPath, "a");
|
|
156
|
-
try {
|
|
157
|
-
const result = spawnSync(launch.command, launch.leadingArgs, {
|
|
158
|
-
cwd,
|
|
159
|
-
stdio: ["ignore", logFd, logFd],
|
|
160
|
-
timeout: 14400000
|
|
161
|
-
});
|
|
162
|
-
const exit = result.status ?? 1;
|
|
163
|
-
const afterDigest = await mod.captureFeatureReceiptDigest(cwd, featureContent, learningsContent);
|
|
164
|
-
const status = exit === 0 && afterDigest === beforeDigest ? "PASS" : "FAIL";
|
|
165
|
-
await mod.completeFeatureVerificationReceipt(nodeFsShim, runDir, receipt, { status, inputDigest: afterDigest });
|
|
166
|
-
console.log(`feature-verification-steps: ${featureId} verification ${status} (run ${runId}, log ${logPath})`);
|
|
167
|
-
} finally {
|
|
168
|
-
closeSync(logFd);
|
|
169
|
-
}
|
|
56
|
+
console.log(`feature-verification-steps: ${featureId} ${result.message}`);
|
|
170
57
|
}
|
|
171
58
|
function main() {
|
|
59
|
+
if (!process.versions.bun) {
|
|
60
|
+
const child = spawnSync("bun", [fileURLToPath(import.meta.url), ...process.argv.slice(2)], {
|
|
61
|
+
stdio: "inherit"
|
|
62
|
+
});
|
|
63
|
+
if (child.error)
|
|
64
|
+
console.error(`feature-verification-steps requires Bun on PATH: ${child.error.message}`);
|
|
65
|
+
process.exit(child.status ?? 1);
|
|
66
|
+
}
|
|
172
67
|
const args = process.argv.slice(2);
|
|
173
68
|
const command = args[0];
|
|
174
69
|
const flag = (name) => {
|
|
@@ -176,10 +71,10 @@ function main() {
|
|
|
176
71
|
return i >= 0 && i + 1 < args.length ? args[i + 1] ?? "" : "";
|
|
177
72
|
};
|
|
178
73
|
if (command !== "verify") {
|
|
179
|
-
console.error("Usage: feature-verification-steps.ts verify --feature-id <id> --run-id <id> [--cmd <command>]");
|
|
74
|
+
console.error("Usage: feature-verification-steps.ts verify --feature-id <id> --run-id <id> [--cmd <command>] [--confidence HIGH|MEDIUM|LOW] [--ack-low-confidence]");
|
|
180
75
|
process.exit(2);
|
|
181
76
|
}
|
|
182
|
-
verify(flag("--feature-id") || getEnvVar("featureId") || "", flag("--run-id") || getEnvVar("__runId") || "", flag("--cmd") || getEnvVar("verificationCmd") || "", flag("--spur-bin") || getEnvVar("spurBin") || "", resolveModuleMode(flag("--module-mode"))).then(() => process.exit(0)).catch((err) => {
|
|
77
|
+
verify(flag("--feature-id") || getEnvVar("featureId") || "", flag("--run-id") || getEnvVar("__runId") || "", flag("--cmd") || getEnvVar("verificationCmd") || "", flag("--spur-bin") || getEnvVar("spurBin") || "", resolveModuleMode(flag("--module-mode")), flag("--confidence") || getEnvVar("confidence") || "", args.includes("--ack-low-confidence") || getEnvVar("ackLowConfidence") === "true").then(() => process.exit(0)).catch((err) => {
|
|
183
78
|
console.error(`feature-verification-steps: ${String(err)}`);
|
|
184
79
|
process.exit(1);
|
|
185
80
|
});
|
|
@@ -1,62 +1,40 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
/**
|
|
3
|
-
* feature-verification-steps —
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* feature-verification-steps — argv adapter for the feature-verification pass (task 0915; reduced to
|
|
4
|
+
* an adapter in task 1176).
|
|
5
|
+
*
|
|
6
|
+
* The pass itself — resolve the selected `feature-verification` definition, capture the before
|
|
7
|
+
* proof-input digest, record and register the RUNNING receipt, run the effective command through the
|
|
8
|
+
* safe argv splitter (never `sh -c`), compare the after digest, complete PASS/FAIL plus the coarse
|
|
9
|
+
* status file — lives in the application operation
|
|
10
|
+
* `packages/app/src/workflow/feature-verification-run.ts`, reached through the generated inline
|
|
11
|
+
* bundle. This entry owns argv, usage and exit codes only: it opens no project DB, constructs no
|
|
12
|
+
* `ArtifactDao`, and scans no feature directory (that lookup is app-owned too).
|
|
6
13
|
*
|
|
7
14
|
* Usage (workflow onEnter, vars: featureId, verificationCmd, spurBin, __runId):
|
|
8
15
|
* bun plugins/sp/scripts/feature-verification-steps.ts verify \
|
|
9
|
-
* --feature-id D63 --run-id <runId> --cmd "bun run spur-check-feature"
|
|
16
|
+
* --feature-id D63 --run-id <runId> [--cmd "bun run spur-check-feature"] [--module-mode bundle]
|
|
17
|
+
*
|
|
18
|
+
* Always exits 0 after a completed pass; the transition guard reads the status file and the
|
|
19
|
+
* completion boundary validates the receipt (fail-closed). Unsafe ids are refused by the operation.
|
|
10
20
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* copies) and register the run-scoped receipt as a run artifact,
|
|
16
|
-
* 3. run the effective command via the safe launch splitter (never `sh -c`
|
|
17
|
-
* interpolation), output to the run-scoped log,
|
|
18
|
-
* 4. capture the after digest — a mismatch records FAIL,
|
|
19
|
-
* 5. complete the receipt PASS/FAIL (both copies) and the coarse status file.
|
|
21
|
+
* The installed `.mjs` twin may be launched with plain `node` (the `feature-verification` workflow
|
|
22
|
+
* selects `installed -> node`), but the generated inline bundle keeps `bun:sqlite` external by design, so the
|
|
23
|
+
* twin re-enters Bun at the top of `main()` (task 1197). Unlike daily-summary it fails closed when Bun
|
|
24
|
+
* is missing — a verifier that cannot load its bundle cannot run the checks.
|
|
20
25
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* refused outright. Node-builtin imports only (ADR-065); the application seams come
|
|
24
|
-
* from the generated inline bundle (`plugins/sp/lib/inline-run.generated.mjs`) — never
|
|
25
|
-
* from `packages/app/src/index.ts`, which is not a plugin-script contract surface (0948 R3).
|
|
26
|
+
* Node-builtin imports only (ADR-065); the application seams come from the generated inline bundle,
|
|
27
|
+
* never from `packages/app/src/index.ts`, which is not a plugin-script contract surface (0948 R3).
|
|
26
28
|
*/
|
|
27
29
|
|
|
28
30
|
import { spawnSync } from 'node:child_process';
|
|
29
|
-
import {
|
|
30
|
-
closeSync,
|
|
31
|
-
existsSync,
|
|
32
|
-
mkdirSync,
|
|
33
|
-
openSync,
|
|
34
|
-
readdirSync,
|
|
35
|
-
readFileSync,
|
|
36
|
-
realpathSync,
|
|
37
|
-
renameSync,
|
|
38
|
-
statSync,
|
|
39
|
-
writeFileSync,
|
|
40
|
-
} from 'node:fs';
|
|
41
|
-
import { join } from 'node:path';
|
|
31
|
+
import { existsSync } from 'node:fs';
|
|
42
32
|
import { fileURLToPath } from 'node:url';
|
|
43
33
|
import { getEnvVar } from '../lib/env';
|
|
44
34
|
|
|
45
|
-
/** Single safe filename component — same class as the inline-run-setup run-id guard. */
|
|
46
|
-
const SAFE_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
47
|
-
|
|
48
35
|
/** Shape of the generated inline bundle / repo app source this script consumes. */
|
|
49
36
|
interface VerificationModule {
|
|
50
|
-
|
|
51
|
-
startFeatureVerificationReceipt: typeof import('../../../packages/app/src/workflow/feature-verification-receipt')['startFeatureVerificationReceipt'];
|
|
52
|
-
completeFeatureVerificationReceipt: typeof import('../../../packages/app/src/workflow/feature-verification-receipt')['completeFeatureVerificationReceipt'];
|
|
53
|
-
captureFeatureReceiptDigest: typeof import('../../../packages/app/src/workflow/feature-verification-receipt')['captureFeatureReceiptDigest'];
|
|
54
|
-
resolveWorkflowDefinition: typeof import('../../../packages/app/src/workflow/workflow-resolver')['resolveWorkflowDefinition'];
|
|
55
|
-
splitLaunchCommand: typeof import('../../../packages/app/src/workflow/split-launch-command')['splitLaunchCommand'];
|
|
56
|
-
openInlineRunProjectDb: typeof import('../../../packages/app/src/services/inline-run-setup')['openInlineRunProjectDb'];
|
|
57
|
-
ArtifactDao: new (
|
|
58
|
-
adapter: never,
|
|
59
|
-
) => { record: (input: { runId: string; path: string; kind: string }) => Promise<unknown> };
|
|
37
|
+
runFeatureVerification: typeof import('../../../packages/app/src/workflow/feature-verification-run')['runFeatureVerification'];
|
|
60
38
|
}
|
|
61
39
|
|
|
62
40
|
/** Explicit module surface. Source is not the contract; the generated bundle is (0948 R3). */
|
|
@@ -67,16 +45,16 @@ function bundleEntry(): string {
|
|
|
67
45
|
}
|
|
68
46
|
|
|
69
47
|
/**
|
|
70
|
-
* Source mode names the mode and the fix. The app source entry does not export
|
|
71
|
-
*
|
|
48
|
+
* Source mode names the mode and the fix. The app source entry does not export the verification
|
|
49
|
+
* operation, and "rebuild/install the sp plugin" cannot add it.
|
|
72
50
|
*/
|
|
73
51
|
function sourceModeError(spurBin: string): Error {
|
|
74
52
|
return new Error(
|
|
75
53
|
`source mode: refusing to load packages/app/src/index.ts (spurBin=${JSON.stringify(spurBin)}). ` +
|
|
76
54
|
'That entry is not the feature-verification contract surface — it does not export ' +
|
|
77
|
-
'
|
|
55
|
+
'runFeatureVerification. Fix: run in bundle mode so ' +
|
|
78
56
|
'plugins/sp/lib/inline-run.generated.mjs is loaded (omit --module-mode, or pass --module-mode bundle) ' +
|
|
79
|
-
'and regenerate that bundle if it is missing. Reinstalling the sp plugin does not add
|
|
57
|
+
'and regenerate that bundle if it is missing. Reinstalling the sp plugin does not add the operation to the app source entry.',
|
|
80
58
|
);
|
|
81
59
|
}
|
|
82
60
|
|
|
@@ -89,8 +67,8 @@ function resolveModuleMode(raw: string): ModuleMode {
|
|
|
89
67
|
/**
|
|
90
68
|
* Bundle mode loads the generated inline bundle. Source mode fails closed with a named fix.
|
|
91
69
|
*
|
|
92
|
-
* `spurBin` no longer selects the module (it named the *caller's* CLI entry, not this
|
|
93
|
-
*
|
|
70
|
+
* `spurBin` no longer selects the module (it named the *caller's* CLI entry, not this script's runtime
|
|
71
|
+
* surface); it is kept only to make the failure message actionable.
|
|
94
72
|
*/
|
|
95
73
|
async function loadModule(spurBin: string, mode: ModuleMode): Promise<VerificationModule> {
|
|
96
74
|
if (mode === 'source') throw sourceModeError(spurBin);
|
|
@@ -105,20 +83,10 @@ async function loadModule(spurBin: string, mode: ModuleMode): Promise<Verificati
|
|
|
105
83
|
return requireModule(bundle, mode);
|
|
106
84
|
}
|
|
107
85
|
|
|
108
|
-
/** Dynamic-import the entry and refuse
|
|
86
|
+
/** Dynamic-import the entry and refuse a module missing the verification operation. */
|
|
109
87
|
async function requireModule(entry: string, mode: ModuleMode): Promise<VerificationModule> {
|
|
110
88
|
const mod = (await import(entry)) as Partial<VerificationModule> & Record<string, unknown>;
|
|
111
|
-
const missing = (
|
|
112
|
-
[
|
|
113
|
-
'startFeatureVerificationReceipt',
|
|
114
|
-
'completeFeatureVerificationReceipt',
|
|
115
|
-
'captureFeatureReceiptDigest',
|
|
116
|
-
'resolveWorkflowDefinition',
|
|
117
|
-
'splitLaunchCommand',
|
|
118
|
-
'openInlineRunProjectDb',
|
|
119
|
-
'ArtifactDao',
|
|
120
|
-
] as const
|
|
121
|
-
).filter((k) => typeof mod[k] !== 'function');
|
|
89
|
+
const missing = (['runFeatureVerification'] as const).filter((k) => typeof mod[k] !== 'function');
|
|
122
90
|
if (missing.length > 0) {
|
|
123
91
|
throw new Error(
|
|
124
92
|
`feature-verification-steps: mode=${mode} — application entry ${entry} is missing feature-verification seams (${missing.join(', ')}). ` +
|
|
@@ -128,48 +96,6 @@ async function requireModule(entry: string, mode: ModuleMode): Promise<Verificat
|
|
|
128
96
|
return mod as VerificationModule;
|
|
129
97
|
}
|
|
130
98
|
|
|
131
|
-
/** Refuse ids that could escape the run directory through receipt filenames. */
|
|
132
|
-
function assertSafeId(kind: string, id: string): void {
|
|
133
|
-
if (!SAFE_ID_RE.test(id) || id.includes('..')) {
|
|
134
|
-
throw new Error(`refusing unsafe ${kind}: ${id}`);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/** Locate `<id>_<slug>.md` under the features directory. */
|
|
139
|
-
function findFeatureFile(featureDir: string, featureId: string): string | undefined {
|
|
140
|
-
if (!existsSync(featureDir)) return undefined;
|
|
141
|
-
const name = readdirSync(featureDir).find((n) => n.startsWith(`${featureId}_`) && n.endsWith('.md'));
|
|
142
|
-
return name === undefined ? undefined : join(featureDir, name);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// ponytail: the receipt service is filesystem-pure over the ts-runtime
|
|
146
|
-
// FileSystem port; only ensureDir/writeFile/rename are exercised on the
|
|
147
|
-
// start/complete paths. Swap to createNodeFileSystem if more surface is needed.
|
|
148
|
-
const nodeFsShim = {
|
|
149
|
-
realPath: realpathSync,
|
|
150
|
-
readDir: readdirSync,
|
|
151
|
-
stat: (path: string) => {
|
|
152
|
-
try {
|
|
153
|
-
return statSync(path);
|
|
154
|
-
} catch (error) {
|
|
155
|
-
if ((error as { code?: string }).code === 'ENOENT') return null;
|
|
156
|
-
throw error;
|
|
157
|
-
}
|
|
158
|
-
},
|
|
159
|
-
ensureDir: (dir: string) => {
|
|
160
|
-
mkdirSync(dir, { recursive: true });
|
|
161
|
-
},
|
|
162
|
-
writeFile: (path: string, body: string) => {
|
|
163
|
-
writeFileSync(path, body);
|
|
164
|
-
},
|
|
165
|
-
rename: (src: string, dest: string) => {
|
|
166
|
-
renameSync(src, dest);
|
|
167
|
-
},
|
|
168
|
-
readFile: (path: string) => {
|
|
169
|
-
return readFileSync(path, 'utf8');
|
|
170
|
-
},
|
|
171
|
-
} as never;
|
|
172
|
-
|
|
173
99
|
/** One full verification pass for one feature. Always exits 0. */
|
|
174
100
|
async function verify(
|
|
175
101
|
featureId: string,
|
|
@@ -177,90 +103,35 @@ async function verify(
|
|
|
177
103
|
cmdOverride: string,
|
|
178
104
|
spurBin: string,
|
|
179
105
|
mode: ModuleMode,
|
|
106
|
+
confidenceOverride: string,
|
|
107
|
+
ackLowConfidence: boolean,
|
|
180
108
|
): Promise<void> {
|
|
181
|
-
assertSafeId('feature id', featureId);
|
|
182
|
-
assertSafeId('run id', runId);
|
|
183
|
-
const cwd = process.cwd();
|
|
184
109
|
const mod = await loadModule(spurBin, mode);
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
// 1. Selected verifier definition (project override wins); its configured
|
|
189
|
-
// command is the completion contract — an override records but can't complete.
|
|
190
|
-
const selected = await mod.resolveWorkflowDefinition(cwd, 'feature-verification');
|
|
191
|
-
const vars = selected.workflow.vars as { verificationCmd?: unknown } | undefined;
|
|
192
|
-
const configuredCmd =
|
|
193
|
-
typeof vars?.verificationCmd === 'string' && vars.verificationCmd.length > 0
|
|
194
|
-
? vars.verificationCmd
|
|
195
|
-
: 'bun run spur-check-feature';
|
|
196
|
-
const effectiveCmd = cmdOverride !== '' ? cmdOverride : configuredCmd;
|
|
197
|
-
|
|
198
|
-
const featureFile = findFeatureFile(join(cwd, 'docs', 'features'), featureId);
|
|
199
|
-
if (featureFile === undefined) {
|
|
200
|
-
throw new Error(`feature ${featureId} not found under ${join(cwd, 'docs', 'features')}`);
|
|
201
|
-
}
|
|
202
|
-
const featureContent = readFileSync(featureFile, 'utf8');
|
|
203
|
-
const learningsPath = join(cwd, '.spur', 'context', 'learnings.md');
|
|
204
|
-
const learningsContent = existsSync(learningsPath) ? readFileSync(learningsPath, 'utf8') : undefined;
|
|
205
|
-
|
|
206
|
-
// 2. Before digest + RUNNING receipt (both copies) + artifact registration.
|
|
207
|
-
const beforeDigest = await mod.captureFeatureReceiptDigest(cwd, featureContent, learningsContent);
|
|
208
|
-
const receipt = await mod.startFeatureVerificationReceipt(nodeFsShim, runDir, {
|
|
110
|
+
const result = await mod.runFeatureVerification({
|
|
111
|
+
workdir: process.cwd(),
|
|
209
112
|
featureId,
|
|
210
113
|
runId,
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
},
|
|
218
|
-
verificationCmd: effectiveCmd,
|
|
219
|
-
inputDigest: beforeDigest,
|
|
114
|
+
cmdOverride,
|
|
115
|
+
// Operator-declared level (empty = derive from the pass) and the acknowledgement of a LOW
|
|
116
|
+
// level. Both ride the receipt, so the completion boundary reads them from the evidence
|
|
117
|
+
// rather than from whatever flags a later transition happens to pass.
|
|
118
|
+
confidenceOverride,
|
|
119
|
+
ackLowConfidence,
|
|
220
120
|
});
|
|
221
|
-
|
|
222
|
-
try {
|
|
223
|
-
await new mod.ArtifactDao(db.adapter as never).record({
|
|
224
|
-
runId,
|
|
225
|
-
path: mod.featureReceiptPaths(runDir, featureId, runId).runScoped,
|
|
226
|
-
kind: 'feature-verification',
|
|
227
|
-
});
|
|
228
|
-
} finally {
|
|
229
|
-
db.close();
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// 3. Run the effective command via the safe splitter (no `sh -c`).
|
|
233
|
-
const logPath = join(runDir, `${runId}-feature-verification.log`);
|
|
234
|
-
const launch = mod.splitLaunchCommand(effectiveCmd, 'feature-verification verificationCmd');
|
|
235
|
-
if ('error' in launch) {
|
|
236
|
-
await mod.completeFeatureVerificationReceipt(nodeFsShim, runDir, receipt, {
|
|
237
|
-
status: 'FAIL',
|
|
238
|
-
inputDigest: beforeDigest,
|
|
239
|
-
});
|
|
240
|
-
console.log(`feature-verification-steps: ${featureId} verification FAIL (${launch.error})`);
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
const logFd = openSync(logPath, 'a');
|
|
244
|
-
try {
|
|
245
|
-
const result = spawnSync(launch.command, launch.leadingArgs, {
|
|
246
|
-
cwd,
|
|
247
|
-
stdio: ['ignore', logFd, logFd],
|
|
248
|
-
timeout: 4 * 60 * 60 * 1000,
|
|
249
|
-
});
|
|
250
|
-
const exit = result.status ?? 1;
|
|
251
|
-
|
|
252
|
-
// 4/5. After digest — a mismatch records FAIL (the pass did not verify
|
|
253
|
-
// the tree it left behind); otherwise the exit code decides.
|
|
254
|
-
const afterDigest = await mod.captureFeatureReceiptDigest(cwd, featureContent, learningsContent);
|
|
255
|
-
const status: 'PASS' | 'FAIL' = exit === 0 && afterDigest === beforeDigest ? 'PASS' : 'FAIL';
|
|
256
|
-
await mod.completeFeatureVerificationReceipt(nodeFsShim, runDir, receipt, { status, inputDigest: afterDigest });
|
|
257
|
-
console.log(`feature-verification-steps: ${featureId} verification ${status} (run ${runId}, log ${logPath})`);
|
|
258
|
-
} finally {
|
|
259
|
-
closeSync(logFd);
|
|
260
|
-
}
|
|
121
|
+
console.log(`feature-verification-steps: ${featureId} ${result.message}`);
|
|
261
122
|
}
|
|
262
123
|
|
|
263
124
|
function main(): void {
|
|
125
|
+
// The portable Node twin has no workspace imports; the generated bundle still uses Spur's Bun
|
|
126
|
+
// runtime (bun:sqlite stays external by design). Re-enter Bun before any app import; fail closed
|
|
127
|
+
// when Bun is missing — the verifier must not report a pass it could not run.
|
|
128
|
+
if (!process.versions.bun) {
|
|
129
|
+
const child = spawnSync('bun', [fileURLToPath(import.meta.url), ...process.argv.slice(2)], {
|
|
130
|
+
stdio: 'inherit',
|
|
131
|
+
});
|
|
132
|
+
if (child.error) console.error(`feature-verification-steps requires Bun on PATH: ${child.error.message}`);
|
|
133
|
+
process.exit(child.status ?? 1);
|
|
134
|
+
}
|
|
264
135
|
const args = process.argv.slice(2);
|
|
265
136
|
const command = args[0];
|
|
266
137
|
const flag = (name: string): string => {
|
|
@@ -268,7 +139,10 @@ function main(): void {
|
|
|
268
139
|
return i >= 0 && i + 1 < args.length ? (args[i + 1] ?? '') : '';
|
|
269
140
|
};
|
|
270
141
|
if (command !== 'verify') {
|
|
271
|
-
console.error(
|
|
142
|
+
console.error(
|
|
143
|
+
'Usage: feature-verification-steps.ts verify --feature-id <id> --run-id <id> [--cmd <command>] ' +
|
|
144
|
+
'[--confidence HIGH|MEDIUM|LOW] [--ack-low-confidence]',
|
|
145
|
+
);
|
|
272
146
|
process.exit(2);
|
|
273
147
|
}
|
|
274
148
|
verify(
|
|
@@ -277,6 +151,8 @@ function main(): void {
|
|
|
277
151
|
flag('--cmd') || getEnvVar('verificationCmd') || '',
|
|
278
152
|
flag('--spur-bin') || getEnvVar('spurBin') || '',
|
|
279
153
|
resolveModuleMode(flag('--module-mode')),
|
|
154
|
+
flag('--confidence') || getEnvVar('confidence') || '',
|
|
155
|
+
args.includes('--ack-low-confidence') || getEnvVar('ackLowConfidence') === 'true',
|
|
280
156
|
)
|
|
281
157
|
.then(() => process.exit(0))
|
|
282
158
|
.catch((err: unknown) => {
|
|
@@ -908,24 +908,24 @@ ${reasons}`, stderr: "" };
|
|
|
908
908
|
process.exitCode = exitCode;
|
|
909
909
|
}
|
|
910
910
|
export {
|
|
911
|
-
|
|
912
|
-
stampReport,
|
|
913
|
-
semanticArtifactDigest,
|
|
914
|
-
runCacheCli,
|
|
915
|
-
resolvePaths,
|
|
916
|
-
renderProvenanceFrontmatter,
|
|
917
|
-
refreshReport,
|
|
918
|
-
publishAtomically,
|
|
919
|
-
probe,
|
|
920
|
-
porcelainPaths,
|
|
921
|
-
parseProvenance,
|
|
922
|
-
logicDigest,
|
|
923
|
-
importedSnapshotAsOf,
|
|
924
|
-
diffPorcelain,
|
|
925
|
-
decideCache,
|
|
926
|
-
checkReportStructure,
|
|
927
|
-
buildProvenance,
|
|
928
|
-
bannerLine,
|
|
911
|
+
FINDING_FIELDS,
|
|
929
912
|
REPORT_SECTIONS,
|
|
930
|
-
|
|
913
|
+
bannerLine,
|
|
914
|
+
buildProvenance,
|
|
915
|
+
checkReportStructure,
|
|
916
|
+
decideCache,
|
|
917
|
+
diffPorcelain,
|
|
918
|
+
importedSnapshotAsOf,
|
|
919
|
+
logicDigest,
|
|
920
|
+
parseProvenance,
|
|
921
|
+
porcelainPaths,
|
|
922
|
+
probe,
|
|
923
|
+
publishAtomically,
|
|
924
|
+
refreshReport,
|
|
925
|
+
renderProvenanceFrontmatter,
|
|
926
|
+
resolvePaths,
|
|
927
|
+
runCacheCli,
|
|
928
|
+
semanticArtifactDigest,
|
|
929
|
+
stampReport,
|
|
930
|
+
validateSelector
|
|
931
931
|
};
|
|
@@ -3,9 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
// plugins/sp/scripts/inline-run-dispatch.ts
|
|
5
5
|
import { spawnSync } from "child_process";
|
|
6
|
-
import {
|
|
7
|
-
import { dirname, join, resolve } from "path";
|
|
8
|
-
import { fileURLToPath } from "url";
|
|
6
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9
7
|
|
|
10
8
|
// plugins/sp/lib/env.ts
|
|
11
9
|
function getEnvVar(name, fallback) {
|
|
@@ -14,6 +12,28 @@ function getEnvVar(name, fallback) {
|
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
// plugins/sp/lib/inline-run-app.ts
|
|
15
|
+
import { existsSync } from "node:fs";
|
|
16
|
+
import { dirname, join, resolve } from "node:path";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
function resolveAppEntry(spurBin) {
|
|
19
|
+
const fallback = fileURLToPath(new URL("../../../apps/cli/src/index.ts", import.meta.url));
|
|
20
|
+
const candidates = spurBin !== "" ? [spurBin] : [fallback];
|
|
21
|
+
for (const candidate of candidates) {
|
|
22
|
+
const tokens = candidate.split(/\s+/).filter(Boolean);
|
|
23
|
+
const mainModule = [...tokens].reverse().find((t) => t.endsWith(".ts"));
|
|
24
|
+
if (mainModule === undefined || !existsSync(mainModule))
|
|
25
|
+
continue;
|
|
26
|
+
const repoRoot = resolve(dirname(mainModule), "..", "..", "..");
|
|
27
|
+
const appEntry = join(repoRoot, "packages", "app", "src", "index.ts");
|
|
28
|
+
if (existsSync(appEntry))
|
|
29
|
+
return { entry: appEntry, portable: false };
|
|
30
|
+
}
|
|
31
|
+
const entry = fileURLToPath(new URL("../lib/inline-run.generated.mjs", import.meta.url));
|
|
32
|
+
if (!existsSync(entry)) {
|
|
33
|
+
throw new Error("inline application bundle is missing — rebuild/install the sp plugin before running inline");
|
|
34
|
+
}
|
|
35
|
+
return { entry, portable: true };
|
|
36
|
+
}
|
|
17
37
|
async function loadInlineApp(spurBin, resolveAppEntry, required) {
|
|
18
38
|
const { entry, portable } = resolveAppEntry(spurBin);
|
|
19
39
|
const app = await import(entry);
|
|
@@ -36,28 +56,9 @@ function usage() {
|
|
|
36
56
|
process.exit(2);
|
|
37
57
|
}
|
|
38
58
|
var SAFE_RUN_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
39
|
-
function resolveAppEntry(spurBin) {
|
|
40
|
-
const fallback = fileURLToPath(new URL("../../../apps/cli/src/index.ts", import.meta.url));
|
|
41
|
-
const candidates = spurBin !== "" ? [spurBin] : [fallback];
|
|
42
|
-
for (const candidate of candidates) {
|
|
43
|
-
const tokens = candidate.split(/\s+/).filter(Boolean);
|
|
44
|
-
const mainModule = [...tokens].reverse().find((t) => t.endsWith(".ts"));
|
|
45
|
-
if (mainModule === undefined || !existsSync(mainModule))
|
|
46
|
-
continue;
|
|
47
|
-
const repoRoot = resolve(dirname(mainModule), "..", "..", "..");
|
|
48
|
-
const appEntry = join(repoRoot, "packages", "app", "src", "index.ts");
|
|
49
|
-
if (existsSync(appEntry))
|
|
50
|
-
return { entry: appEntry, portable: false };
|
|
51
|
-
}
|
|
52
|
-
const entry = fileURLToPath(new URL("../lib/inline-run.generated.mjs", import.meta.url));
|
|
53
|
-
if (!existsSync(entry)) {
|
|
54
|
-
throw new Error("inline application bundle is missing \u2014 rebuild/install the sp plugin before running inline");
|
|
55
|
-
}
|
|
56
|
-
return { entry, portable: true };
|
|
57
|
-
}
|
|
58
59
|
async function main() {
|
|
59
60
|
if (!process.versions.bun) {
|
|
60
|
-
const child = spawnSync("bun", [
|
|
61
|
+
const child = spawnSync("bun", [fileURLToPath2(import.meta.url), ...process.argv.slice(2)], {
|
|
61
62
|
stdio: "inherit"
|
|
62
63
|
});
|
|
63
64
|
if (child.error)
|
|
@@ -113,7 +114,7 @@ async function main() {
|
|
|
113
114
|
}
|
|
114
115
|
if (decision.trim() === "" || text === "" === (textFile === ""))
|
|
115
116
|
usage();
|
|
116
|
-
const { app: lib } = await loadInlineApp(spurBin, () => ({ entry:
|
|
117
|
+
const { app: lib } = await loadInlineApp(spurBin, () => ({ entry: fileURLToPath2(new URL("../lib/inline-run.generated.mjs", import.meta.url)), portable }), ["loadSpurConfig"]);
|
|
117
118
|
const spurConfig = await lib.loadSpurConfig(process.cwd(), portable ? { embeddedSchemas: lib.EMBEDDED_SPUR_SCHEMAS } : undefined);
|
|
118
119
|
process.exit(await app.runInlineRunDispatchFailure({
|
|
119
120
|
runId,
|