@gobing-ai/spur 0.3.85 → 0.3.87
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/pipeline-budgets.json +0 -25
- package/config/plugin-scripts.json +4 -5
- package/config/rules/boundary/env-var-hygiene.yaml +41 -0
- package/config/templates/feature/default.md +2 -0
- package/config/templates/task/brainstorm.md +2 -2
- package/config/templates/task/feature-impl.md +2 -2
- package/config/templates/task/issue.md +2 -2
- package/config/templates/task/meta.md +2 -2
- package/config/templates/task/review.md +2 -2
- package/config/templates/task/standard.md +2 -2
- package/config/transition-shims.json +1 -1
- package/config/workflow-candidates.json +19 -0
- package/config/workflows/feature-lifecycle.yaml +10 -2
- package/config/workflows/feature-verification.yaml +71 -0
- package/config/workflows/idea-pipeline.yaml +87 -39
- package/config/workflows/task-pipeline.yaml +43 -12
- package/config/workflows/wrapup-pipeline.yaml +70 -7
- package/package.json +9 -9
- package/plugins/sp/README.md +7 -7
- package/plugins/sp/commands/dev-idea.md +9 -2
- package/plugins/sp/commands/dev-refactor.md +33 -0
- package/plugins/sp/hooks/agent-hint.ts +5 -4
- package/plugins/sp/hooks/careful-guard.ts +3 -1
- package/plugins/sp/hooks/context-post-tool.ts +2 -1
- package/plugins/sp/hooks/context-session-start.ts +4 -3
- package/plugins/sp/hooks/context-session-stop.ts +2 -1
- package/plugins/sp/hooks/pi/guard-extension.ts +4 -3
- package/plugins/sp/hooks/task-write-guard.ts +4 -3
- package/plugins/sp/lib/idea-handoff.generated.mjs +260 -260
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/references/roles.md +1 -1
- package/plugins/sp/scripts/daily-summary/daily-summary.mjs +13 -4
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +6 -4
- package/plugins/sp/scripts/feature-sync-bounded.mjs +10 -2
- package/plugins/sp/scripts/feature-sync-bounded.ts +2 -1
- package/plugins/sp/scripts/idea-coverage-check.ts +168 -0
- package/plugins/sp/scripts/idea-handoff.mjs +6 -1
- package/plugins/sp/scripts/idea-handoff.ts +4 -1
- package/plugins/sp/scripts/inline-pipeline-parity-check.ts +115 -4
- package/plugins/sp/scripts/inline-run-setup.ts +195 -2
- package/plugins/sp/scripts/pr-reviewing.mjs +8 -1
- package/plugins/sp/scripts/pr-reviewing.ts +2 -1
- package/plugins/sp/scripts/quality-gate.mjs +8 -1
- package/plugins/sp/scripts/quality-gate.ts +2 -1
- package/plugins/sp/scripts/surface-drift-inventory.ts +1 -4
- package/plugins/sp/scripts/task-evidence-precheck.ts +2 -1
- package/plugins/sp/scripts/task-size-precheck.ts +6 -4
- package/plugins/sp/scripts/verify-answer-lint.ts +2 -1
- package/plugins/sp/scripts/workflow-step-profile.mjs +10 -2
- package/plugins/sp/scripts/workflow-step-profile.ts +2 -1
- package/plugins/sp/scripts/wrapup-steps.mjs +8 -1
- package/plugins/sp/scripts/wrapup-steps.ts +2 -1
- package/plugins/sp/skills/brainstorm/SKILL.md +4 -0
- package/plugins/sp/skills/code-refactoring/SKILL.md +155 -0
- package/plugins/sp/skills/code-refactoring/references/finding-schema.md +74 -0
- package/plugins/sp/skills/code-refactoring/references/fix-ladder.md +52 -0
- package/plugins/sp/skills/code-refactoring/references/focus-detection.md +44 -0
- package/plugins/sp/skills/code-refactoring/references/refactor-finding.schema.json +95 -0
- package/plugins/sp/skills/spec-decomposition/references/decomposition.md +23 -16
- package/plugins/sp/skills/spur-cli/references/features/acceptance-criteria.md +4 -2
- package/plugins/sp/skills/spur-cli/references/features.md +6 -1
- package/plugins/sp/skills/spur-cli/references/workflows/authoring-workflows.md +2 -2
- package/plugins/sp/skills/spur-cli/references/workflows/operations.md +3 -3
- package/plugins/sp/skills/spur-cli/references/workflows/workflow-fit-and-tuning.md +1 -1
- package/plugins/sp/skills/spur-cli/references/workflows.md +8 -7
- package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +24 -0
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +4 -5
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +18 -2
- package/plugins/sp/skills/spur-dev/references/done-housekeeping.md +1 -1
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +22 -3
- package/plugins/sp/skills/spur-dev/references/gate-checklists.md +2 -2
- package/plugins/sp/skills/spur-dev/references/idea-evaluation.md +11 -1
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +91 -1
- package/plugins/sp/skills/taste-refactoring-api/SKILL.md +44 -1
- package/plugins/sp/skills/taste-refactoring-api/references/protocol-modes.md +31 -0
- package/plugins/sp/skills/taste-refactoring-architect/SKILL.md +43 -0
- package/plugins/sp/skills/taste-refactoring-tests/SKILL.md +42 -0
- package/plugins/sp/skills/taste-refactoring-ui/SKILL.md +43 -0
- package/schemas/state-machine-workflow.schema.json +5 -0
- package/schemas/task-batch.schema.json +2 -2
- package/schemas/transition-flow-workflow.schema.json +5 -0
- package/spur.js +21106 -20669
- package/web/_astro/BoardApp.CJiqp5pS.js +1 -0
- package/web/_astro/{BoardApp.yW425dRZ.js → BoardApp.yBBcFXWP.js} +4 -4
- package/web/_astro/{TaskDetail.CGwJAinW.js → TaskDetail.DqFJbRFc.js} +1 -1
- package/web/_astro/{arc.B-qNXzSO.js → arc.DL-BpHoi.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.Bdg-xiji.js → architectureDiagram-3BPJPVTR.9IdQYyDq.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.DFArg3Kp.js → blockDiagram-GPEHLZMM.BKsFCqTl.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.BkOLcjyx.js → c4Diagram-AAUBKEIU.DhwI0dh1.js} +1 -1
- package/web/_astro/channel.CX5453qQ.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.DN3c20V4.js → chunk-2J33WTMH.B3QVmQ9S.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.B64K0Ttd.js → chunk-4BX2VUAB.DBSuqs9F.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.C-AJvNwa.js → chunk-55IACEB6.BSYTWAYD.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.DMkr46uS.js → chunk-727SXJPM.cWVuxXfS.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.4_WGbI64.js → chunk-AQP2D5EJ.DpU_Ob3d.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.cbFW_lVE.js → chunk-FMBD7UC4.BykFkyji.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.CDW_-2O1.js → chunk-ND2GUHAM.DwgHlMdY.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.BoYOt_yg.js → chunk-QZHKN3VN.CusXUGWM.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.BNT6XpHQ.js → classDiagram-4FO5ZUOK.fx0ObzkN.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.BNT6XpHQ.js → classDiagram-v2-Q7XG4LA2.fx0ObzkN.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.CI1DQxR3.js → cose-bilkent-S5V4N54A.Z4HgOlsd.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.A8ARH61p.js → cynefin-OW5HDTMX.B5dIZHJu.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.u28ZmqwR.js → dagre-BM42HDAG.DT70Q_Yw.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.Dx8JcrGc.js → diagram-2AECGRRQ.DqHA3XBF.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.BXJUG9GY.js → diagram-5GNKFQAL.BmCem957.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.BtlMuWqW.js → diagram-KO2AKTUF.sn0-hrE0.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.C4T7qaAy.js → diagram-LMA3HP47.BSHe9tVc.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.D5HECE10.js → diagram-OG6HWLK6.DHIc-86k.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.B_hMR3yw.js → erDiagram-TEJ5UH35.Bxayrs7v.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.BAmpcYl3.js → flowDiagram-I6XJVG4X.BkzoE_5I.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.DV1bSWK-.js → ganttDiagram-6RSMTGT7.okT6CvTo.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.DxYKkKrJ.js → gitGraphDiagram-PVQCEYII.CJuYbhC7.js} +1 -1
- package/web/_astro/{infoDiagram-5YYISTIA.BAWY2xMb.js → infoDiagram-5YYISTIA.RqLy7nBo.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.CZssRn6V.js → ishikawaDiagram-YF4QCWOH.BwIcoagw.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.C--muARd.js → journeyDiagram-JHISSGLW.UB1VbWtH.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.D_QCK4et.js → kanban-definition-UN3LZRKU.AaxMKpTk.js} +1 -1
- package/web/_astro/{linear.DzrmTtZ0.js → linear.Nv_xOUjP.js} +1 -1
- package/web/_astro/{mermaid.core.Da03W3iu.js → mermaid.core.Bc4LqQgX.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.6cy-8hR_.js → mindmap-definition-RKZ34NQL.oKUvU_qi.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.C5rS1pdU.js → pieDiagram-4H26LBE5.DQk0oo03.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.w56GZZ6Q.js → quadrantDiagram-W4KKPZXB.BdDjESDa.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.CipX3Pwu.js → requirementDiagram-4Y6WPE33.C2u9hUeH.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.C0VVzgJm.js → sankeyDiagram-5OEKKPKP.CDEoiJST.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.BX2dUUbF.js → sequenceDiagram-3UESZ5HK.D_hT_GAT.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.ypCdgODQ.js → stateDiagram-AJRCARHV.DI8RYG0b.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.In0baEtg.js → stateDiagram-v2-BHNVJYJU.Bkxz4DnP.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.CJN4Vkvl.js → timeline-definition-PNZ67QCA.DSY-kH3-.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.Cf8KkIPY.js → vennDiagram-CIIHVFJN.CpaDtuGr.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.DCZBo9xw.js → wardleyDiagram-YWT4CUSO.DujQWvo8.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.Cm4v_MiJ.js → xychartDiagram-2RQKCTM6.DcM5Y4b9.js} +1 -1
- package/web/index.html +1 -1
- package/config/workflows/basic.yaml +0 -146
- package/config/workflows/docs-pipeline.yaml +0 -350
- package/config/workflows/feature-dev.yaml +0 -288
- package/plugins/sp/scripts/feature-dev-precheck.mjs +0 -146
- package/plugins/sp/scripts/feature-dev-precheck.ts +0 -238
- package/web/_astro/BoardApp.CHenHFia.js +0 -1
- package/web/_astro/channel.CbDHK5UQ.js +0 -1
|
@@ -30,16 +30,29 @@
|
|
|
30
30
|
* Usage:
|
|
31
31
|
* bun plugins/sp/scripts/inline-run-setup.ts --run-id <id> --file <definition> [--spur-bin <path>]
|
|
32
32
|
* bun plugins/sp/scripts/inline-run-setup.ts --fingerprint --task-file <path> [--feature-file <path>] [--spur-bin <path>]
|
|
33
|
+
* bun plugins/sp/scripts/inline-run-setup.ts --action --run-id <id> --node <state> --kind <kind> \
|
|
34
|
+
* --status <done|failed> --ok <true|false> --duration-ms <n> [--spur-bin <path>]
|
|
35
|
+
* bun plugins/sp/scripts/inline-run-setup.ts --close --run-id <id> --status <done|failed|paused> [--spur-bin <path>]
|
|
33
36
|
*
|
|
34
37
|
* The `--fingerprint` mode prints the engine's proof-input digest for the given spec files and
|
|
35
38
|
* creates nothing (task 0862 R5).
|
|
36
39
|
*
|
|
40
|
+
* The `--action` / `--close` modes are the inline driver's ADR-117 emission boundary (task
|
|
41
|
+
* 0868): `--action` records one completed action boundary as an `action_runs` row through the
|
|
42
|
+
* shared `WorkflowActionTraceWriter`, `--close` marks the run row terminal through the same
|
|
43
|
+
* writer. `--action` is best-effort: a persistence failure is appended to
|
|
44
|
+
* `.spur/run/<run-id>.log` and the script still exits 0 with `{"ok":false}` on stdout, so
|
|
45
|
+
* observation never wedges the run. `--close` is NOT best-effort: the run-row closure is
|
|
46
|
+
* bookkeeping, so a missing run row or a persistence failure exits 1 with a named error.
|
|
47
|
+
* Exit 2 is reserved for usage errors.
|
|
48
|
+
*
|
|
37
49
|
* Env: SPUR_BIN
|
|
38
50
|
*/
|
|
39
51
|
|
|
40
|
-
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
52
|
+
import { appendFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
41
53
|
import { dirname, join, resolve } from 'node:path';
|
|
42
54
|
import { fileURLToPath } from 'node:url';
|
|
55
|
+
import { getEnvVar } from '@gobing-ai/ts-utils';
|
|
43
56
|
|
|
44
57
|
/** Outcome document written to `.spur/run/<run-id>-inline-setup.json`. */
|
|
45
58
|
interface SetupOutcome {
|
|
@@ -63,6 +76,13 @@ function usage(): never {
|
|
|
63
76
|
console.error(
|
|
64
77
|
' bun plugins/sp/scripts/inline-run-setup.ts --fingerprint --task-file <path> [--feature-file <path>] [--spur-bin <path>]',
|
|
65
78
|
);
|
|
79
|
+
console.error(
|
|
80
|
+
' bun plugins/sp/scripts/inline-run-setup.ts --action --run-id <id> --node <state> --kind <kind> ' +
|
|
81
|
+
'--status <done|failed> --ok <true|false> --duration-ms <n> [--spur-bin <path>]',
|
|
82
|
+
);
|
|
83
|
+
console.error(
|
|
84
|
+
' bun plugins/sp/scripts/inline-run-setup.ts --close --run-id <id> --status <done|failed|paused> [--spur-bin <path>]',
|
|
85
|
+
);
|
|
66
86
|
process.exit(2);
|
|
67
87
|
}
|
|
68
88
|
|
|
@@ -177,13 +197,143 @@ async function printFingerprint(taskFile: string, featureFile: string, spurBin:
|
|
|
177
197
|
return 0;
|
|
178
198
|
}
|
|
179
199
|
|
|
200
|
+
/** Terminal statuses the inline driver may declare when closing its run row. */
|
|
201
|
+
const CLOSE_STATUSES = new Set(['done', 'failed', 'paused']);
|
|
202
|
+
|
|
203
|
+
/** Finalize statuses — a finish emission is terminal, so only done|failed are valid (0868 #4). */
|
|
204
|
+
const ACTION_STATUSES = new Set(['done', 'failed']);
|
|
205
|
+
|
|
206
|
+
/** Input for the ADR-117 emission modes (`--action` / `--close`). */
|
|
207
|
+
import type { WorkflowActionTraceWriter } from '@gobing-ai/app';
|
|
208
|
+
|
|
209
|
+
interface TraceModeInput {
|
|
210
|
+
readonly runId: string;
|
|
211
|
+
readonly close: boolean;
|
|
212
|
+
readonly node: string;
|
|
213
|
+
readonly kind: string;
|
|
214
|
+
readonly status: string;
|
|
215
|
+
readonly ok: boolean;
|
|
216
|
+
readonly durationMs: number;
|
|
217
|
+
readonly spurBin: string;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Append one emission-failure line to `.spur/run/<run-id>.log` — the run log the inline
|
|
222
|
+
* driver already owns. Best-effort and synchronous (the process may exit immediately
|
|
223
|
+
* after), and never throws: an unwritable log must not wedge the run (ADR-117 R3).
|
|
224
|
+
*/
|
|
225
|
+
function appendTraceFailureLine(runId: string, detail: string): void {
|
|
226
|
+
try {
|
|
227
|
+
const runDir = join(process.cwd(), '.spur', 'run');
|
|
228
|
+
if (!existsSync(runDir)) mkdirSync(runDir, { recursive: true });
|
|
229
|
+
const safeRunId = runId.replace(/[^A-Za-z0-9._-]/g, '_');
|
|
230
|
+
const stamp = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
231
|
+
appendFileSync(join(runDir, `${safeRunId}.log`), `[${stamp}] ${detail}\n`);
|
|
232
|
+
} catch {
|
|
233
|
+
// Best-effort (R3): the run continues even when the failure cannot be recorded.
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Emit one trace write through the SHARED `WorkflowActionTraceWriter` (task 0868 R5/R7).
|
|
239
|
+
* `--action` is best-effort: an emission failure is recorded to the run log and reported
|
|
240
|
+
* on stdout as `{"ok":false}`, and the script exits 0 so the run still reaches its declared
|
|
241
|
+
* terminal state (R3/R12). `--close` is bookkeeping, so a missing run row or a persistence
|
|
242
|
+
* failure fails loudly with exit 1 (review findings #1/#4).
|
|
243
|
+
*/
|
|
244
|
+
async function runTraceMode(input: TraceModeInput): Promise<number> {
|
|
245
|
+
const operation = input.close ? 'run.close' : 'action.finish';
|
|
246
|
+
const fail = (error: string): number => {
|
|
247
|
+
appendTraceFailureLine(
|
|
248
|
+
input.runId,
|
|
249
|
+
`trace-emission-failed operation=${operation} run=${input.runId}` +
|
|
250
|
+
`${input.node === '' ? '' : ` node=${input.node}`}${input.kind === '' ? '' : ` kind=${input.kind}`}: ${error}`,
|
|
251
|
+
);
|
|
252
|
+
process.stdout.write(`${JSON.stringify({ ok: false, runId: input.runId, error })}\n`);
|
|
253
|
+
// The action boundary is best-effort (exit 0); the run-row closure fails loudly (exit 1).
|
|
254
|
+
return input.close ? 1 : 0;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const { entry, repoRoot, chain } = resolveAppEntry(input.spurBin);
|
|
258
|
+
if (entry === null || repoRoot === null) {
|
|
259
|
+
return fail(
|
|
260
|
+
`no monorepo checkout of spur is reachable via ${chain} — the shared trace writer ` +
|
|
261
|
+
'lives in packages/app (point SPUR_BIN at a repo checkout)',
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Compile-time link (0868 finding #5): the writer half is typed by the real packages/app
|
|
266
|
+
// export (type-only import, erased at runtime) so a signature drift breaks THIS file's
|
|
267
|
+
// typecheck instead of hiding behind the hand-declared cast.
|
|
268
|
+
const app = (await import(entry)) as {
|
|
269
|
+
openInlineRunProjectDb: (workdir: string) => Promise<{ adapter: unknown; close: () => void }>;
|
|
270
|
+
createWorkflowActionTraceWriter: (
|
|
271
|
+
db: unknown,
|
|
272
|
+
recordFailure?: (failure: unknown) => void,
|
|
273
|
+
) => WorkflowActionTraceWriter;
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
let projectDb: { adapter: unknown; close: () => void } | undefined;
|
|
277
|
+
try {
|
|
278
|
+
projectDb = await app.openInlineRunProjectDb(process.cwd());
|
|
279
|
+
const writer = app.createWorkflowActionTraceWriter(projectDb.adapter, (failure: unknown) => {
|
|
280
|
+
const detail = failure as { operation?: string; error?: string };
|
|
281
|
+
appendTraceFailureLine(
|
|
282
|
+
input.runId,
|
|
283
|
+
`trace-emission-failed operation=${detail.operation ?? operation} run=${input.runId}: ${detail.error ?? 'unknown error'}`,
|
|
284
|
+
);
|
|
285
|
+
});
|
|
286
|
+
const result = (
|
|
287
|
+
input.close
|
|
288
|
+
? await writer.closeRun(input.runId, input.status)
|
|
289
|
+
: await writer.recordAction({
|
|
290
|
+
runId: input.runId,
|
|
291
|
+
node: input.node,
|
|
292
|
+
kind: input.kind,
|
|
293
|
+
status: input.status,
|
|
294
|
+
ok: input.ok,
|
|
295
|
+
durationMs: input.durationMs,
|
|
296
|
+
})
|
|
297
|
+
) as Record<string, unknown>;
|
|
298
|
+
if (result.ok !== true && result.failure !== undefined) {
|
|
299
|
+
// One stdout shape for emission failures (0868 finding #1): flatten the guard's
|
|
300
|
+
// nested failure object to the same `{ok, runId, error}` the direct paths emit.
|
|
301
|
+
const failure = result.failure as { error?: string };
|
|
302
|
+
return fail(failure.error ?? 'unknown trace emission failure');
|
|
303
|
+
}
|
|
304
|
+
process.stdout.write(`${JSON.stringify({ ...result, runId: input.runId })}\n`);
|
|
305
|
+
return 0;
|
|
306
|
+
} catch (error) {
|
|
307
|
+
if (input.close && (error as { name?: string }).name === 'RunRowNotFoundError') {
|
|
308
|
+
// The run row must exist before --close can mark it terminal (R6); a missing row
|
|
309
|
+
// is a loud correctness failure, not a best-effort emission failure (finding #4).
|
|
310
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
311
|
+
appendTraceFailureLine(input.runId, `trace-close-failed run=${input.runId}: ${message}`);
|
|
312
|
+
process.stdout.write(
|
|
313
|
+
`${JSON.stringify({ ok: false, runId: input.runId, error: message, code: 'RUN_NOT_FOUND' })}\n`,
|
|
314
|
+
);
|
|
315
|
+
return 1;
|
|
316
|
+
}
|
|
317
|
+
return fail(error instanceof Error ? error.message : String(error));
|
|
318
|
+
} finally {
|
|
319
|
+
projectDb?.close();
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
180
323
|
async function main(): Promise<void> {
|
|
181
324
|
let runId = '';
|
|
182
325
|
let file = '';
|
|
183
326
|
let fingerprint = false;
|
|
184
327
|
let taskFile = '';
|
|
185
328
|
let featureFile = '';
|
|
186
|
-
let
|
|
329
|
+
let action = false;
|
|
330
|
+
let close = false;
|
|
331
|
+
let node = '';
|
|
332
|
+
let kind = '';
|
|
333
|
+
let status = '';
|
|
334
|
+
let okRaw = '';
|
|
335
|
+
let durationRaw = '';
|
|
336
|
+
let spurBin = getEnvVar('SPUR_BIN') ?? '';
|
|
187
337
|
const argv = process.argv.slice(2);
|
|
188
338
|
for (let i = 0; i < argv.length; i++) {
|
|
189
339
|
if (argv[i] === '--run-id') runId = argv[++i] ?? '';
|
|
@@ -191,6 +341,13 @@ async function main(): Promise<void> {
|
|
|
191
341
|
else if (argv[i] === '--fingerprint') fingerprint = true;
|
|
192
342
|
else if (argv[i] === '--task-file') taskFile = argv[++i] ?? '';
|
|
193
343
|
else if (argv[i] === '--feature-file') featureFile = argv[++i] ?? '';
|
|
344
|
+
else if (argv[i] === '--action') action = true;
|
|
345
|
+
else if (argv[i] === '--close') close = true;
|
|
346
|
+
else if (argv[i] === '--node') node = argv[++i] ?? '';
|
|
347
|
+
else if (argv[i] === '--kind') kind = argv[++i] ?? '';
|
|
348
|
+
else if (argv[i] === '--status') status = argv[++i] ?? '';
|
|
349
|
+
else if (argv[i] === '--ok') okRaw = argv[++i] ?? '';
|
|
350
|
+
else if (argv[i] === '--duration-ms') durationRaw = argv[++i] ?? '';
|
|
194
351
|
else if (argv[i] === '--spur-bin') spurBin = argv[++i] ?? spurBin;
|
|
195
352
|
}
|
|
196
353
|
|
|
@@ -200,6 +357,42 @@ async function main(): Promise<void> {
|
|
|
200
357
|
if (runId !== '' || file !== '' || taskFile.trim() === '') usage();
|
|
201
358
|
process.exit(await printFingerprint(taskFile, featureFile, spurBin));
|
|
202
359
|
}
|
|
360
|
+
|
|
361
|
+
// ADR-117 emission modes (task 0868): the inline driver reports one completed action
|
|
362
|
+
// boundary, or closes its run row at the declared terminal state. Both share the run-id
|
|
363
|
+
// filename guard, the app-entry resolution chain and the best-effort failure contract.
|
|
364
|
+
if (action || close) {
|
|
365
|
+
if (action && close) usage();
|
|
366
|
+
if (runId.trim() === '' || status.trim() === '') usage();
|
|
367
|
+
if (!SAFE_RUN_ID_RE.test(runId)) refuseUnsafeRunId(runId);
|
|
368
|
+
if (close) {
|
|
369
|
+
if (!CLOSE_STATUSES.has(status)) usage();
|
|
370
|
+
process.exit(
|
|
371
|
+
await runTraceMode({
|
|
372
|
+
runId,
|
|
373
|
+
close: true,
|
|
374
|
+
node: '',
|
|
375
|
+
kind: '',
|
|
376
|
+
status,
|
|
377
|
+
ok: true,
|
|
378
|
+
durationMs: 0,
|
|
379
|
+
spurBin,
|
|
380
|
+
}),
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
if (node.trim() === '' || kind.trim() === '') usage();
|
|
384
|
+
if (!ACTION_STATUSES.has(status)) usage();
|
|
385
|
+
// `--ok` and `--duration-ms` are required and exact for the action mode
|
|
386
|
+
// (review finding #2): a miscased `--ok True` or an omitted `--duration-ms`
|
|
387
|
+
// must be a loud usage error, never a silently-defaulted `ok=0` /
|
|
388
|
+
// `duration_ms=0` row.
|
|
389
|
+
if (okRaw !== 'true' && okRaw !== 'false') usage();
|
|
390
|
+
const ok = okRaw === 'true';
|
|
391
|
+
const durationMs = Number(durationRaw);
|
|
392
|
+
if (durationRaw.trim() === '' || !Number.isFinite(durationMs) || durationMs < 0) usage();
|
|
393
|
+
process.exit(await runTraceMode({ runId, close: false, node, kind, status, ok, durationMs, spurBin }));
|
|
394
|
+
}
|
|
395
|
+
|
|
203
396
|
if (runId.trim() === '' || file.trim() === '') usage();
|
|
204
397
|
if (!SAFE_RUN_ID_RE.test(runId)) refuseUnsafeRunId(runId);
|
|
205
398
|
|
|
@@ -5,8 +5,15 @@
|
|
|
5
5
|
import { spawnSync } from "child_process";
|
|
6
6
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
7
7
|
import { join } from "path";
|
|
8
|
+
|
|
9
|
+
// ../ts-libs/packages/utils/dist/env.js
|
|
10
|
+
function getEnvVars() {
|
|
11
|
+
return process.env;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// plugins/sp/scripts/pr-reviewing.ts
|
|
8
15
|
var spawnRunner = (cmd) => {
|
|
9
|
-
const proc = spawnSync(cmd[0] ?? "", [...cmd.slice(1)], { encoding: "utf8", env:
|
|
16
|
+
const proc = spawnSync(cmd[0] ?? "", [...cmd.slice(1)], { encoding: "utf8", env: getEnvVars() });
|
|
10
17
|
return {
|
|
11
18
|
code: proc.status ?? 1,
|
|
12
19
|
stdout: proc.stdout ?? "",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
import { spawnSync } from 'node:child_process';
|
|
23
23
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
24
24
|
import { join } from 'node:path';
|
|
25
|
+
import { getEnvVars } from '@gobing-ai/ts-utils';
|
|
25
26
|
|
|
26
27
|
// ─── Types ──────────────────────────────────────────────────────────────────
|
|
27
28
|
|
|
@@ -118,7 +119,7 @@ export type CommandRunner = (cmd: readonly string[]) => CmdResult;
|
|
|
118
119
|
/** Real execution: resolve the binary from PATH and capture its output. The default runner. */
|
|
119
120
|
export const spawnRunner: CommandRunner = (cmd) => {
|
|
120
121
|
// Pass env explicitly so both Node and Bun resolve git/gh against the caller's PATH.
|
|
121
|
-
const proc = spawnSync(cmd[0] ?? '', [...cmd.slice(1)], { encoding: 'utf8', env:
|
|
122
|
+
const proc = spawnSync(cmd[0] ?? '', [...cmd.slice(1)], { encoding: 'utf8', env: getEnvVars() });
|
|
122
123
|
return {
|
|
123
124
|
code: proc.status ?? 1,
|
|
124
125
|
stdout: proc.stdout ?? '',
|
|
@@ -17,6 +17,13 @@ import {
|
|
|
17
17
|
} from "fs";
|
|
18
18
|
import { tmpdir } from "os";
|
|
19
19
|
import { join } from "path";
|
|
20
|
+
|
|
21
|
+
// ../ts-libs/packages/utils/dist/env.js
|
|
22
|
+
function getEnvVars() {
|
|
23
|
+
return process.env;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// plugins/sp/scripts/quality-gate.ts
|
|
20
27
|
var MAX_GATE_ATTEMPTS = 5;
|
|
21
28
|
var MAX_FINDINGS = 20;
|
|
22
29
|
var RETRY_DELAY_MS_DEFAULT = 1e4;
|
|
@@ -178,7 +185,7 @@ function runQualityGate(mode, env, options = {}) {
|
|
|
178
185
|
return { status, attempts: gateAttempt, logFile, findingsFile, statusFile, attemptFile };
|
|
179
186
|
}
|
|
180
187
|
var QUALITY_GATE_USAGE = "usage: quality-gate.ts <run|recheck> (env: wbs, qualityGateCmd, gateProbeCmd, proofDigest)";
|
|
181
|
-
function main(argv, env =
|
|
188
|
+
function main(argv, env = getEnvVars()) {
|
|
182
189
|
const mode = argv[0];
|
|
183
190
|
if (mode !== "run" && mode !== "recheck") {
|
|
184
191
|
process.stderr.write(`${QUALITY_GATE_USAGE}
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
} from 'node:fs';
|
|
42
42
|
import { tmpdir } from 'node:os';
|
|
43
43
|
import { join } from 'node:path';
|
|
44
|
+
import { getEnvVars } from '@gobing-ai/ts-utils';
|
|
44
45
|
|
|
45
46
|
export const MAX_GATE_ATTEMPTS = 5;
|
|
46
47
|
export const MAX_FINDINGS = 20;
|
|
@@ -268,7 +269,7 @@ export function runQualityGate(
|
|
|
268
269
|
export const QUALITY_GATE_USAGE =
|
|
269
270
|
'usage: quality-gate.ts <run|recheck> (env: wbs, qualityGateCmd, gateProbeCmd, proofDigest)';
|
|
270
271
|
|
|
271
|
-
export function main(argv: string[], env: QualityGateEnv =
|
|
272
|
+
export function main(argv: string[], env: QualityGateEnv = getEnvVars()): number {
|
|
272
273
|
const mode = argv[0];
|
|
273
274
|
if (mode !== 'run' && mode !== 'recheck') {
|
|
274
275
|
process.stderr.write(`${QUALITY_GATE_USAGE}\n`);
|
|
@@ -179,9 +179,6 @@ export function parseInvocation(spanRaw: string): Parsed | null {
|
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
if (nouns.length === 0 && verbs.length === 0 && flags.length === 0) return null;
|
|
182
|
-
if (process.env.DEBUG_SPANS === '1') {
|
|
183
|
-
process.stderr.write(`SPAN ${JSON.stringify({ spanRaw, nouns, verbs, flags })}\n`);
|
|
184
|
-
}
|
|
185
182
|
return { nouns, verbs, flags };
|
|
186
183
|
}
|
|
187
184
|
|
|
@@ -638,7 +635,7 @@ const JSON_PROBES: string[][] = [
|
|
|
638
635
|
['agent', 'list', '--json'],
|
|
639
636
|
['agent', 'doctor', 'pi', '--json'],
|
|
640
637
|
['workflow', 'list', '--json'],
|
|
641
|
-
['workflow', 'validate', '
|
|
638
|
+
['workflow', 'validate', 'task-pipeline.yaml', '--json'],
|
|
642
639
|
['status', '--json'],
|
|
643
640
|
['projects', 'list', '--json'],
|
|
644
641
|
];
|
|
@@ -37,6 +37,7 @@ import { execFileSync } from 'node:child_process';
|
|
|
37
37
|
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
38
38
|
import { join } from 'node:path';
|
|
39
39
|
import { fileURLToPath } from 'node:url';
|
|
40
|
+
import { getEnvVar } from '@gobing-ai/ts-utils';
|
|
40
41
|
|
|
41
42
|
/** Exact task-content declaration that activates the live-evidence gate (0726 R2). */
|
|
42
43
|
const DECLARATION_PREFIX = 'evidence-channel:';
|
|
@@ -58,7 +59,7 @@ function usage(): never {
|
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
function defaultSpurBin(): string {
|
|
61
|
-
if (
|
|
62
|
+
if (getEnvVar('SPUR_BIN')) return getEnvVar('SPUR_BIN');
|
|
62
63
|
const local = fileURLToPath(new URL('../../../apps/cli/src/index.ts', import.meta.url));
|
|
63
64
|
if (existsSync(local)) return `bun ${local}`;
|
|
64
65
|
return 'spur';
|
|
@@ -19,13 +19,14 @@
|
|
|
19
19
|
* bun plugins/sp/scripts/task-size-precheck.ts <wbs> [--spur-bin <path>]
|
|
20
20
|
* [--max-reqs <n>] [--max-plan-items <n>]
|
|
21
21
|
*
|
|
22
|
-
* Env: SPUR_BIN
|
|
22
|
+
* Env: SPUR_BIN (flags own the size ceilings — env overrides removed, task 0902)
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import { execFileSync } from 'node:child_process';
|
|
26
26
|
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
27
27
|
import { join } from 'node:path';
|
|
28
28
|
import { fileURLToPath } from 'node:url';
|
|
29
|
+
import { getEnvVar } from '@gobing-ai/ts-utils';
|
|
29
30
|
|
|
30
31
|
// ─── Counting (sync with packages/app/src/services/task-size-precheck.ts) ────
|
|
31
32
|
|
|
@@ -88,7 +89,7 @@ function usage(): never {
|
|
|
88
89
|
* keeps ad-hoc invocations from silently hitting a stale PATH install.
|
|
89
90
|
*/
|
|
90
91
|
function defaultSpurBin(): string {
|
|
91
|
-
if (
|
|
92
|
+
if (getEnvVar('SPUR_BIN')) return getEnvVar('SPUR_BIN');
|
|
92
93
|
// scripts/ -> plugins/sp/ -> <repo>/apps/cli/src/index.ts (fileURLToPath — raw pathname breaks
|
|
93
94
|
// on %-encoded paths, e.g. spaces in the checkout directory)
|
|
94
95
|
const local = fileURLToPath(new URL('../../../apps/cli/src/index.ts', import.meta.url));
|
|
@@ -107,8 +108,9 @@ function parseArgs(argv: string[]): {
|
|
|
107
108
|
// Doubled deterministic ceiling (0723 operator decision): 10 R-items / 16
|
|
108
109
|
// Plan items — keep in sync with DEFAULT_TASK_SIZE_LIMITS in
|
|
109
110
|
// packages/app/src/services/task-size-precheck.ts (asserted by test).
|
|
110
|
-
|
|
111
|
-
let
|
|
111
|
+
// Overridable per invocation via --max-reqs / --max-plan-items (no env fallback).
|
|
112
|
+
let maxReqs = 10;
|
|
113
|
+
let maxPlanItems = 16;
|
|
112
114
|
|
|
113
115
|
let i = 0;
|
|
114
116
|
while (i < argv.length) {
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
import { execFileSync } from 'node:child_process';
|
|
33
33
|
import { existsSync, readFileSync } from 'node:fs';
|
|
34
34
|
import { fileURLToPath } from 'node:url';
|
|
35
|
+
import { getEnvVar } from '@gobing-ai/ts-utils';
|
|
35
36
|
|
|
36
37
|
// ─── CLI (same spur-bin chain as task-evidence-precheck.ts) ─────────────────
|
|
37
38
|
|
|
@@ -41,7 +42,7 @@ function usage(): never {
|
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
function defaultSpurBin(): string {
|
|
44
|
-
if (
|
|
45
|
+
if (getEnvVar('SPUR_BIN')) return getEnvVar('SPUR_BIN');
|
|
45
46
|
const local = fileURLToPath(new URL('../../../apps/cli/src/index.ts', import.meta.url));
|
|
46
47
|
if (existsSync(local)) return `bun ${local}`;
|
|
47
48
|
return 'spur';
|
|
@@ -5,6 +5,14 @@
|
|
|
5
5
|
import { spawnSync } from "child_process";
|
|
6
6
|
import { existsSync } from "fs";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
|
+
|
|
9
|
+
// ../ts-libs/packages/utils/dist/env.js
|
|
10
|
+
function getEnvVar(name, fallback) {
|
|
11
|
+
const raw = process.env[name];
|
|
12
|
+
return raw === undefined ? fallback : raw;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// plugins/sp/scripts/workflow-step-profile.ts
|
|
8
16
|
var DEFAULT_LAST = 20;
|
|
9
17
|
var DEFAULT_WINDOW_SEC = 300;
|
|
10
18
|
function nearestRankP50(values) {
|
|
@@ -168,8 +176,8 @@ function formatStepProfileHuman(profile) {
|
|
|
168
176
|
`;
|
|
169
177
|
}
|
|
170
178
|
function defaultSpurBin() {
|
|
171
|
-
if (
|
|
172
|
-
return
|
|
179
|
+
if (getEnvVar("SPUR_BIN"))
|
|
180
|
+
return getEnvVar("SPUR_BIN");
|
|
173
181
|
const local = fileURLToPath(new URL("../../../apps/cli/src/index.ts", import.meta.url));
|
|
174
182
|
if (existsSync(local))
|
|
175
183
|
return `bun ${local}`;
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
import { spawnSync } from 'node:child_process';
|
|
26
26
|
import { existsSync } from 'node:fs';
|
|
27
27
|
import { fileURLToPath } from 'node:url';
|
|
28
|
+
import { getEnvVar } from '@gobing-ai/ts-utils';
|
|
28
29
|
|
|
29
30
|
// ── Input shapes (a subset of the live `spur workflow trace --json` payload) ─────────────
|
|
30
31
|
|
|
@@ -321,7 +322,7 @@ export interface StepProfileCliArgs {
|
|
|
321
322
|
* --spur-bin > SPUR_BIN > monorepo-local CLI entry > PATH `spur`.
|
|
322
323
|
*/
|
|
323
324
|
export function defaultSpurBin(): string {
|
|
324
|
-
if (
|
|
325
|
+
if (getEnvVar('SPUR_BIN')) return getEnvVar('SPUR_BIN');
|
|
325
326
|
const local = fileURLToPath(new URL('../../../apps/cli/src/index.ts', import.meta.url));
|
|
326
327
|
if (existsSync(local)) return `bun ${local}`;
|
|
327
328
|
return 'spur';
|
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
import { spawnSync } from "child_process";
|
|
6
6
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
7
7
|
import { join } from "path";
|
|
8
|
+
|
|
9
|
+
// ../ts-libs/packages/utils/dist/env.js
|
|
10
|
+
function getEnvVars() {
|
|
11
|
+
return process.env;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// plugins/sp/scripts/wrapup-steps.ts
|
|
8
15
|
function jqPick(...values) {
|
|
9
16
|
for (const value of values) {
|
|
10
17
|
if (value !== null && value !== undefined && value !== false)
|
|
@@ -319,7 +326,7 @@ function runFeatureTransition(env, options = {}) {
|
|
|
319
326
|
return { status: syncStatus, statusFile: relStatusFile, exitCode: 0 };
|
|
320
327
|
}
|
|
321
328
|
var WRAPUP_STEPS_USAGE = "usage: wrapup-steps.ts <resolve|metrics|feature-transition> (env: __runId, tasks, feature, featureGateCmd, spurBin)";
|
|
322
|
-
function main(argv, env =
|
|
329
|
+
function main(argv, env = getEnvVars(), options = {}) {
|
|
323
330
|
const sub = argv[0];
|
|
324
331
|
if (sub === "resolve")
|
|
325
332
|
return resolveTasks(env, options).exitCode;
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
import { spawnSync } from 'node:child_process';
|
|
32
32
|
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
33
33
|
import { join } from 'node:path';
|
|
34
|
+
import { getEnvVars } from '@gobing-ai/ts-utils';
|
|
34
35
|
|
|
35
36
|
export interface WrapupStepsEnv {
|
|
36
37
|
__runId?: string;
|
|
@@ -449,7 +450,7 @@ export function runFeatureTransition(env: WrapupStepsEnv, options: WrapupStepsOp
|
|
|
449
450
|
export const WRAPUP_STEPS_USAGE =
|
|
450
451
|
'usage: wrapup-steps.ts <resolve|metrics|feature-transition> (env: __runId, tasks, feature, featureGateCmd, spurBin)';
|
|
451
452
|
|
|
452
|
-
export function main(argv: string[], env: WrapupStepsEnv =
|
|
453
|
+
export function main(argv: string[], env: WrapupStepsEnv = getEnvVars(), options: WrapupStepsOptions = {}): number {
|
|
453
454
|
const sub = argv[0];
|
|
454
455
|
if (sub === 'resolve') return resolveTasks(env, options).exitCode;
|
|
455
456
|
if (sub === 'metrics') {
|
|
@@ -197,6 +197,10 @@ When brainstorm runs under `idea-pipeline` discovery, it MUST also emit a filled
|
|
|
197
197
|
[`spur-dev/references/idea-evaluation.md`](../spur-dev/references/idea-evaluation.md):
|
|
198
198
|
|
|
199
199
|
- Enhanced idea statement (sidecar — does **not** overwrite the operator's original idea text)
|
|
200
|
+
- **Requirement inventory** — mandatory `## Requirement inventory` section: numbered `I<n>` items
|
|
201
|
+
quoting or paraphrasing the source lines from the run's verbatim idea artifact
|
|
202
|
+
(`.spur/run/<run-id>-idea-input.md`, 0887 R1/R3), with `[unclear: ...]` markers where the ask is
|
|
203
|
+
ambiguous and `[deferred: <reason>]` markers for explicit out-of-scope items
|
|
200
204
|
- Urgency and necessity scores (0–5) with one-line rationales
|
|
201
205
|
- Premises, pros, cons, better alternatives (if any)
|
|
202
206
|
- Recommendation (`proceed` | `reshape` | `drop`) + stakes
|