@gobing-ai/spur 0.3.13 → 0.3.14
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/config/workflows/task-lifecycle.yaml +10 -0
- package/config/workflows/wayfinder-resolution.yaml +199 -0
- package/package.json +1 -1
- package/spur.js +229 -50
- package/web/_astro/BoardApp.BdyUxAQU.js +1 -0
- package/web/_astro/{BoardApp.CqBOrPzf.js → BoardApp.oOZMY52P.js} +65 -65
- package/web/_astro/{TaskDetail.BmxycoeS.js → TaskDetail.DZVfe6fy.js} +1 -1
- package/web/_astro/{arc.-ZkWbWcN.js → arc.CmzWyu5T.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.fJ8ETc6n.js → architectureDiagram-3BPJPVTR.DEvXeSWP.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.CdhoveXr.js → blockDiagram-GPEHLZMM.DULgqAsN.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.CWv7Jy0p.js → c4Diagram-AAUBKEIU.CtwqGUxK.js} +1 -1
- package/web/_astro/channel.C6f9ncOV.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.CvYWDwal.js → chunk-2J33WTMH.lyKyWJjV.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.BVp_y1oc.js → chunk-4BX2VUAB.COAavIDd.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.CQCviGQk.js → chunk-55IACEB6.EoRbsXM1.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.B3E6Bisv.js → chunk-727SXJPM.DhjfPs_B.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.BvWfYj9E.js → chunk-AQP2D5EJ.BgSRWTUm.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.DcLP2Ypw.js → chunk-FMBD7UC4.Dj0MuLrK.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.DvXZJEqI.js → chunk-ND2GUHAM.B2_roVTb.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.D4rK2HTI.js → chunk-QZHKN3VN.Cr3PSROo.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.DSCYtdVf.js → classDiagram-4FO5ZUOK.CXEuLRp6.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.DSCYtdVf.js → classDiagram-v2-Q7XG4LA2.CXEuLRp6.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.Bf7HMV4Q.js → cose-bilkent-S5V4N54A._ulIkUyI.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.DsBoJTBN.js → dagre-BM42HDAG.D5SRsv4S.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.BMVxdKV3.js → diagram-2AECGRRQ.rMwI7OR9.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.BllW5zst.js → diagram-5GNKFQAL.DWZ2ui2V.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.CNHoSD6q.js → diagram-KO2AKTUF.DuqJ_D0B.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.DZT6otFZ.js → diagram-LMA3HP47.BDxKBl9B.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.EwFWHBRO.js → diagram-OG6HWLK6.D6IMfAPf.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.CaXPcR5W.js → erDiagram-TEJ5UH35.DSsh9EFG.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.FEcfML44.js → flowDiagram-I6XJVG4X.DwjNzc4e.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.Bs7t3AQM.js → ganttDiagram-6RSMTGT7.DbYs_nXA.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.DmxLV9JC.js → gitGraphDiagram-PVQCEYII.DX5-xqVb.js} +1 -1
- package/web/_astro/{infoDiagram-5YYISTIA.E7_NrFUE.js → infoDiagram-5YYISTIA.jP6EGbwr.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.Bs5Q4r-U.js → ishikawaDiagram-YF4QCWOH.Cn89YzBJ.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.D3U7T1y1.js → journeyDiagram-JHISSGLW.CNT2WvMT.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.DfdWsO2E.js → kanban-definition-UN3LZRKU.CLEZjEjD.js} +1 -1
- package/web/_astro/{linear.CAqUTUf3.js → linear.Cn4ewR2z.js} +1 -1
- package/web/_astro/{mermaid.core.ZlPUJ21K.js → mermaid.core.BEACwKCZ.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.ClTJkcTG.js → mindmap-definition-RKZ34NQL.BE9cnWaH.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.B9kJfBw_.js → pieDiagram-4H26LBE5.CLcCK_JM.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.CtTJH5Fo.js → quadrantDiagram-W4KKPZXB.qw8LmkgG.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.WVLq9rr2.js → requirementDiagram-4Y6WPE33.D02534qk.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.L_XUdyqe.js → sankeyDiagram-5OEKKPKP.I6nOztAU.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.DCdtsEdj.js → sequenceDiagram-3UESZ5HK.CXTuiAV8.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.-Koi9kLh.js → stateDiagram-AJRCARHV.BV-mNJRM.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.DHXu_l8w.js → stateDiagram-v2-BHNVJYJU.kF2_t5fy.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.Czotaq0j.js → timeline-definition-PNZ67QCA.BiIzvhe5.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.BfEI3zhc.js → vennDiagram-CIIHVFJN.9cR-p_zq.js} +1 -1
- package/web/_astro/{wardley-L42UT6IY.C33mahwT.js → wardley-L42UT6IY.CwwpzGfz.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.D8IzxB1w.js → wardleyDiagram-YWT4CUSO.Q3hswh_f.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.D-cfi9uT.js → xychartDiagram-2RQKCTM6.Co-1OW2E.js} +1 -1
- package/web/index.html +1 -1
- package/web/_astro/BoardApp.XG2FoRlA.js +0 -1
- package/web/_astro/channel.BrBVc6tg.js +0 -1
package/spur.js
CHANGED
|
@@ -48,10 +48,7 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
|
48
48
|
var __require = import.meta.require;
|
|
49
49
|
|
|
50
50
|
// ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.10/node_modules/@gobing-ai/ts-utils/dist/errors.js
|
|
51
|
-
|
|
52
|
-
return error instanceof AppError;
|
|
53
|
-
}
|
|
54
|
-
var ErrorCode, AppError, NotFoundError;
|
|
51
|
+
var ErrorCode;
|
|
55
52
|
var init_errors = __esm(() => {
|
|
56
53
|
ErrorCode = {
|
|
57
54
|
NotFound: "NOT_FOUND",
|
|
@@ -59,20 +56,6 @@ var init_errors = __esm(() => {
|
|
|
59
56
|
Conflict: "CONFLICT",
|
|
60
57
|
Internal: "INTERNAL"
|
|
61
58
|
};
|
|
62
|
-
AppError = class AppError extends Error {
|
|
63
|
-
code;
|
|
64
|
-
constructor(code, message) {
|
|
65
|
-
super(message);
|
|
66
|
-
this.name = "AppError";
|
|
67
|
-
this.code = code;
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
NotFoundError = class NotFoundError extends AppError {
|
|
71
|
-
constructor(message) {
|
|
72
|
-
super(ErrorCode.NotFound, message);
|
|
73
|
-
this.name = "NotFoundError";
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
59
|
});
|
|
77
60
|
|
|
78
61
|
// ../../node_modules/.bun/@gobing-ai+ts-utils@0.4.10/node_modules/@gobing-ai/ts-utils/dist/api-response.js
|
|
@@ -58399,6 +58382,8 @@ var init_schema4 = __esm(() => {
|
|
|
58399
58382
|
priority: exports_external.enum(PRIORITIES).optional(),
|
|
58400
58383
|
tags: exports_external.array(exports_external.string()).optional(),
|
|
58401
58384
|
dependencies: exports_external.array(exports_external.string()).optional(),
|
|
58385
|
+
done_forced: exports_external.preprocess((v) => typeof v === "string" ? v === "true" : v, exports_external.boolean().optional()),
|
|
58386
|
+
done_reason: exports_external.string().optional(),
|
|
58402
58387
|
created_at: isoDateString,
|
|
58403
58388
|
updated_at: isoDateString
|
|
58404
58389
|
});
|
|
@@ -59046,6 +59031,110 @@ var init_corpus_migrator = __esm(() => {
|
|
|
59046
59031
|
NULL_PRESERVING_KEYS = new Set(["parent_wbs"]);
|
|
59047
59032
|
});
|
|
59048
59033
|
|
|
59034
|
+
// ../../packages/app/src/services/done-transition-guard.ts
|
|
59035
|
+
async function readVerdictArtifact(fs3, runDir, wbs) {
|
|
59036
|
+
const path9 = `${runDir}/${wbs}-verdict.json`;
|
|
59037
|
+
let exists2;
|
|
59038
|
+
try {
|
|
59039
|
+
exists2 = await fs3.exists(path9);
|
|
59040
|
+
} catch {
|
|
59041
|
+
exists2 = true;
|
|
59042
|
+
}
|
|
59043
|
+
if (!exists2) {
|
|
59044
|
+
return { artifact: undefined, path: path9 };
|
|
59045
|
+
}
|
|
59046
|
+
let raw;
|
|
59047
|
+
try {
|
|
59048
|
+
raw = await fs3.readFile(path9);
|
|
59049
|
+
} catch (err) {
|
|
59050
|
+
return { artifact: undefined, path: path9, readError: `unreadable artifact: ${err.message}` };
|
|
59051
|
+
}
|
|
59052
|
+
let parsed;
|
|
59053
|
+
try {
|
|
59054
|
+
parsed = JSON.parse(raw);
|
|
59055
|
+
} catch (err) {
|
|
59056
|
+
return { artifact: undefined, path: path9, readError: `malformed JSON: ${err.message}` };
|
|
59057
|
+
}
|
|
59058
|
+
if (typeof parsed !== "object" || parsed === null || !("verdict" in parsed)) {
|
|
59059
|
+
return { artifact: undefined, path: path9, readError: "missing required `verdict` field" };
|
|
59060
|
+
}
|
|
59061
|
+
return { artifact: parsed, path: path9 };
|
|
59062
|
+
}
|
|
59063
|
+
function computeAggregate(artifact) {
|
|
59064
|
+
const reqs = artifact.requirements ?? [];
|
|
59065
|
+
const acs = artifact.acceptanceCriteria ?? [];
|
|
59066
|
+
if (reqs.length === 0 && acs.length === 0) {
|
|
59067
|
+
return artifact.verdict;
|
|
59068
|
+
}
|
|
59069
|
+
if (reqs.some((r) => r.status === "UNMET") || acs.some((a2) => a2.status === "UNMET")) {
|
|
59070
|
+
return "FAIL";
|
|
59071
|
+
}
|
|
59072
|
+
if (reqs.some((r) => r.status === "PARTIAL") || acs.some((a2) => a2.status === "PARTIAL")) {
|
|
59073
|
+
return "PARTIAL";
|
|
59074
|
+
}
|
|
59075
|
+
return "PASS";
|
|
59076
|
+
}
|
|
59077
|
+
function formatDenialMessage(args) {
|
|
59078
|
+
const { wbs, taskFilePath, verdictPath, verdict, inconsistency, artifact } = args;
|
|
59079
|
+
const lines = [
|
|
59080
|
+
`Cannot transition task ${wbs} to done: verify verdict is ${verdict}.`,
|
|
59081
|
+
` task: ${taskFilePath}`,
|
|
59082
|
+
` verdict: ${verdictPath}`
|
|
59083
|
+
];
|
|
59084
|
+
if (inconsistency) {
|
|
59085
|
+
lines.push(` warning: artifact is self-inconsistent \u2014 stored aggregate ${inconsistency.stored} contradicts rows (computed ${inconsistency.computed}). Treated as non-PASS.`);
|
|
59086
|
+
}
|
|
59087
|
+
const reqCount = artifact?.requirements?.length ?? 0;
|
|
59088
|
+
const acCount = artifact?.acceptanceCriteria?.length ?? 0;
|
|
59089
|
+
const rowCount = reqCount + acCount;
|
|
59090
|
+
if (verdict === "UNKNOWN" && artifact !== undefined && (artifact.source === "spur-task-verdict" || rowCount === 0)) {
|
|
59091
|
+
const source = artifact.source ?? "unknown";
|
|
59092
|
+
lines.push(` source: ${source} artifact contains ${rowCount} structured row${rowCount === 1 ? "" : "s"} ` + `(${reqCount} requirement${reqCount === 1 ? "" : "s"}, ${acCount} AC${acCount === 1 ? "" : "s"}). ` + `UNKNOWN means the verify answer file carried no parseable markdown tables ` + `(\`| Req | Status | Evidence |\` and \`| AC | Status | Evidence Type | Evidence |\`).`);
|
|
59093
|
+
lines.push(` see sp:spur-cli \`tasks/verbs.md\` \xA7Answer-file shape for the expected format. ` + `Re-run \`/sp:dev-verify ${wbs}\` so the skill authors the tables; do not loosen the parser.`);
|
|
59094
|
+
}
|
|
59095
|
+
lines.push(" remediation: re-run `/sp:dev-verify " + wbs + "` until PASS, or override with `spur task update " + wbs + ' done --force-done --reason "<why>"`.');
|
|
59096
|
+
return lines.join(`
|
|
59097
|
+
`);
|
|
59098
|
+
}
|
|
59099
|
+
function formatNoopMessage(wbs, status) {
|
|
59100
|
+
return `${wbs}: already ${status} \u2014 no transition`;
|
|
59101
|
+
}
|
|
59102
|
+
function evaluateDoneTransition(input) {
|
|
59103
|
+
const { wbs, taskFilePath, currentStatus, targetStatus, forced, reason, artifact } = input;
|
|
59104
|
+
if (targetStatus === currentStatus) {
|
|
59105
|
+
return { kind: "noop", fromStatus: currentStatus, message: formatNoopMessage(wbs, currentStatus) };
|
|
59106
|
+
}
|
|
59107
|
+
if (artifact === undefined) {
|
|
59108
|
+
return { kind: "allow", reason: "no-artifact" };
|
|
59109
|
+
}
|
|
59110
|
+
if (forced) {
|
|
59111
|
+
return { kind: "allow", reason: "forced" };
|
|
59112
|
+
}
|
|
59113
|
+
const computed = computeAggregate(artifact);
|
|
59114
|
+
const effective = harshnessMax(artifact.verdict, computed);
|
|
59115
|
+
if (effective === "PASS") {
|
|
59116
|
+
return { kind: "allow", reason: "pass" };
|
|
59117
|
+
}
|
|
59118
|
+
const verdictPath = `.spur/run/${wbs}-verdict.json`;
|
|
59119
|
+
const inconsistency = artifact.verdict !== computed ? { stored: artifact.verdict, computed } : undefined;
|
|
59120
|
+
return {
|
|
59121
|
+
kind: "deny",
|
|
59122
|
+
verdict: effective,
|
|
59123
|
+
message: formatDenialMessage({
|
|
59124
|
+
wbs,
|
|
59125
|
+
taskFilePath,
|
|
59126
|
+
verdictPath,
|
|
59127
|
+
verdict: effective,
|
|
59128
|
+
inconsistency,
|
|
59129
|
+
artifact
|
|
59130
|
+
})
|
|
59131
|
+
};
|
|
59132
|
+
}
|
|
59133
|
+
function harshnessMax(a2, b) {
|
|
59134
|
+
const rank = { PASS: 0, UNKNOWN: 1, PARTIAL: 2, FAIL: 3 };
|
|
59135
|
+
return rank[a2] >= rank[b] ? a2 : b;
|
|
59136
|
+
}
|
|
59137
|
+
|
|
59049
59138
|
// ../../packages/app/src/services/event-names.ts
|
|
59050
59139
|
function event(name, source, renderer, payloadPolicy = "metadata-only", tier = "default") {
|
|
59051
59140
|
return {
|
|
@@ -61663,6 +61752,18 @@ var init_task_check = __esm(() => {
|
|
|
61663
61752
|
}
|
|
61664
61753
|
}
|
|
61665
61754
|
async runL4(doc2, fm, findings, featuresDir, tasksDir) {
|
|
61755
|
+
const template = typeof fm.template === "string" ? fm.template : DEFAULT_TASK_VARIANT;
|
|
61756
|
+
if (template === DEFAULT_TASK_VARIANT) {
|
|
61757
|
+
const designBody = doc2.getSection("Design");
|
|
61758
|
+
if (designBody !== null && isPlaceholderBody(designBody)) {
|
|
61759
|
+
findings.push({
|
|
61760
|
+
layer: "L4",
|
|
61761
|
+
severity: "warning",
|
|
61762
|
+
section: "Design",
|
|
61763
|
+
message: "Design section is present but empty (placeholder-only) \u2014 populate it so the task retains an explicit implementation approach"
|
|
61764
|
+
});
|
|
61765
|
+
}
|
|
61766
|
+
}
|
|
61666
61767
|
const featureId2 = fm.feature_id ?? fm["feature-id"];
|
|
61667
61768
|
const parentWbs = fm.parent_wbs ?? fm["parent-wbs"];
|
|
61668
61769
|
const deps = fm.dependencies;
|
|
@@ -62360,9 +62461,15 @@ class TaskService {
|
|
|
62360
62461
|
return this.writeService.transition(ref, toStatus, actor ?? this.ctx.actor ?? "system");
|
|
62361
62462
|
}
|
|
62362
62463
|
async updateField(wbs, key, value) {
|
|
62363
|
-
const allowed =
|
|
62364
|
-
|
|
62365
|
-
|
|
62464
|
+
const allowed = {
|
|
62465
|
+
feature_id: true,
|
|
62466
|
+
parent_wbs: true,
|
|
62467
|
+
priority: true,
|
|
62468
|
+
done_forced: true,
|
|
62469
|
+
done_reason: true
|
|
62470
|
+
};
|
|
62471
|
+
if (!(key in allowed)) {
|
|
62472
|
+
throw new Error(`Field "${key}" is not settable via update; allowed: ${Object.keys(allowed).join(", ")}.`);
|
|
62366
62473
|
}
|
|
62367
62474
|
const filePath = await this.resolveTaskFile(wbs);
|
|
62368
62475
|
const ref = { kind: "task", id: wbs, filePath, folder: this.ctx.tasksDir };
|
|
@@ -65068,6 +65175,7 @@ __export(exports_src2, {
|
|
|
65068
65175
|
renderReview: () => renderReview,
|
|
65069
65176
|
registerSystemEventTap: () => registerSystemEventTap,
|
|
65070
65177
|
registerSpurBuiltins: () => registerSpurBuiltins,
|
|
65178
|
+
readVerdictArtifact: () => readVerdictArtifact,
|
|
65071
65179
|
readVerdict: () => readVerdict,
|
|
65072
65180
|
parseVerdict: () => parseVerdict,
|
|
65073
65181
|
parsePsOutput: () => parsePsOutput,
|
|
@@ -65077,10 +65185,14 @@ __export(exports_src2, {
|
|
|
65077
65185
|
hitlConfirmDefault: () => hitlConfirmDefault,
|
|
65078
65186
|
hitlAutoApproveEnabled: () => hitlAutoApproveEnabled,
|
|
65079
65187
|
hasPopulatedPriorityTable: () => hasPopulatedPriorityTable,
|
|
65188
|
+
formatNoopMessage: () => formatNoopMessage,
|
|
65189
|
+
formatDenialMessage: () => formatDenialMessage,
|
|
65080
65190
|
extractSystemEventActor: () => extractSystemEventActor,
|
|
65081
65191
|
extractReviewSectionBody: () => extractReviewSectionBody,
|
|
65192
|
+
evaluateDoneTransition: () => evaluateDoneTransition,
|
|
65082
65193
|
deriveVerdict: () => deriveVerdict,
|
|
65083
65194
|
createPsProcessInspector: () => createPsProcessInspector,
|
|
65195
|
+
computeAggregate: () => computeAggregate,
|
|
65084
65196
|
clampToolUseLimit: () => clampToolUseLimit,
|
|
65085
65197
|
bridgeEventBus: () => bridgeEventBus,
|
|
65086
65198
|
WorkflowAppService: () => WorkflowAppService,
|
|
@@ -73122,7 +73234,7 @@ import { join as join15, resolve as resolve5 } from "path";
|
|
|
73122
73234
|
var CLI_CONFIG = {
|
|
73123
73235
|
binaryName: "spur",
|
|
73124
73236
|
binaryLabel: "spur",
|
|
73125
|
-
binaryVersion: "0.3.
|
|
73237
|
+
binaryVersion: "0.3.14",
|
|
73126
73238
|
configDir: ".spur",
|
|
73127
73239
|
configFile: ".spur/config.yaml",
|
|
73128
73240
|
databaseFile: ".spur/spur.db"
|
|
@@ -78973,18 +79085,15 @@ var contextInjector = (appRt) => async function contextInjectorMiddleware(c3, ne
|
|
|
78973
79085
|
await next();
|
|
78974
79086
|
};
|
|
78975
79087
|
|
|
78976
|
-
// ../server/src/middleware/error-handler.ts
|
|
78977
|
-
init_dist();
|
|
78978
|
-
|
|
78979
79088
|
// ../server/src/errors.ts
|
|
78980
|
-
|
|
79089
|
+
init_errors5();
|
|
78981
79090
|
|
|
78982
79091
|
// ../server/src/middleware/error-handler.ts
|
|
78983
79092
|
function hasHttpStatus(err) {
|
|
78984
79093
|
return typeof err === "object" && err !== null && "status" in err && typeof err.status === "number";
|
|
78985
79094
|
}
|
|
78986
79095
|
function isAppErrorLike(err) {
|
|
78987
|
-
return
|
|
79096
|
+
return typeof err === "object" && err !== null && "code" in err && typeof err.code === "string" && "message" in err && typeof err.message === "string";
|
|
78988
79097
|
}
|
|
78989
79098
|
function errorEnvelope(code, message, details) {
|
|
78990
79099
|
return { ok: false, error: { code, message, ...details !== undefined ? { details } : {} } };
|
|
@@ -79661,7 +79770,6 @@ var contract = {
|
|
|
79661
79770
|
|
|
79662
79771
|
// ../server/src/modules/feature/handlers.ts
|
|
79663
79772
|
init_schema4();
|
|
79664
|
-
init_dist();
|
|
79665
79773
|
var os2 = implement(contract);
|
|
79666
79774
|
function createResponseShape(r2) {
|
|
79667
79775
|
return { id: r2.ref.id, filePath: r2.ref.filePath };
|
|
@@ -79684,7 +79792,7 @@ function createFeatureHandlers(ctx) {
|
|
|
79684
79792
|
show: os2.feature.show.handler(async ({ input }) => {
|
|
79685
79793
|
const result = await ctx.featureService().show(input.id);
|
|
79686
79794
|
if (!result)
|
|
79687
|
-
throw new
|
|
79795
|
+
throw new HTTPException(404, { message: `Feature ${input.id} not found` });
|
|
79688
79796
|
return {
|
|
79689
79797
|
ok: true,
|
|
79690
79798
|
data: {
|
|
@@ -79713,7 +79821,7 @@ function createFeatureHandlers(ctx) {
|
|
|
79713
79821
|
const folders = ctx.planningFolders();
|
|
79714
79822
|
const feature = await ctx.featureService().show(input.id);
|
|
79715
79823
|
if (!feature)
|
|
79716
|
-
throw new
|
|
79824
|
+
throw new HTTPException(404, { message: `Feature ${input.id} not found` });
|
|
79717
79825
|
const { FeatureCheckService: FeatureCheckService2 } = await Promise.resolve().then(() => (init_feature_check(), exports_feature_check));
|
|
79718
79826
|
const svc = new FeatureCheckService2(ctx.fs);
|
|
79719
79827
|
return {
|
|
@@ -79727,7 +79835,7 @@ function createFeatureHandlers(ctx) {
|
|
|
79727
79835
|
body: os2.feature.body.handler(async ({ input }) => {
|
|
79728
79836
|
const feature = await ctx.featureService().show(input.id);
|
|
79729
79837
|
if (!feature)
|
|
79730
|
-
throw new
|
|
79838
|
+
throw new HTTPException(404, { message: `Feature ${input.id} not found` });
|
|
79731
79839
|
await ctx.featureService().updateBody(input.id, input.body);
|
|
79732
79840
|
return { ok: true, data: {} };
|
|
79733
79841
|
}),
|
|
@@ -79899,17 +80007,33 @@ function parseLimit(raw2, fallback) {
|
|
|
79899
80007
|
}
|
|
79900
80008
|
|
|
79901
80009
|
// ../server/src/modules/observability/index.ts
|
|
79902
|
-
init_src3();
|
|
79903
80010
|
var ledgerWatcher;
|
|
80011
|
+
var ledgerWatcherLoad;
|
|
79904
80012
|
var watcherPath;
|
|
79905
|
-
function getLedgerWatcher(path9) {
|
|
79906
|
-
if (
|
|
79907
|
-
ledgerWatcher
|
|
79908
|
-
|
|
79909
|
-
|
|
79910
|
-
|
|
80013
|
+
async function getLedgerWatcher(path9) {
|
|
80014
|
+
if (watcherPath === path9) {
|
|
80015
|
+
if (ledgerWatcher)
|
|
80016
|
+
return ledgerWatcher;
|
|
80017
|
+
if (ledgerWatcherLoad)
|
|
80018
|
+
return ledgerWatcherLoad;
|
|
80019
|
+
}
|
|
80020
|
+
ledgerWatcher?.stop();
|
|
80021
|
+
ledgerWatcher = undefined;
|
|
80022
|
+
watcherPath = path9;
|
|
80023
|
+
const load2 = Promise.resolve().then(() => (init_src3(), exports_src2)).then(({ TokenLedgerWatcher: TokenLedgerWatcher2 }) => {
|
|
80024
|
+
const watcher2 = new TokenLedgerWatcher2({ ledgerPath: path9 });
|
|
80025
|
+
watcher2.start();
|
|
80026
|
+
return watcher2;
|
|
80027
|
+
});
|
|
80028
|
+
ledgerWatcherLoad = load2;
|
|
80029
|
+
const watcher = await load2;
|
|
80030
|
+
if (watcherPath === path9 && ledgerWatcherLoad === load2) {
|
|
80031
|
+
ledgerWatcher = watcher;
|
|
80032
|
+
ledgerWatcherLoad = undefined;
|
|
80033
|
+
} else {
|
|
80034
|
+
watcher.stop();
|
|
79911
80035
|
}
|
|
79912
|
-
return
|
|
80036
|
+
return watcher;
|
|
79913
80037
|
}
|
|
79914
80038
|
function toolUseSsePayload(event2) {
|
|
79915
80039
|
return {
|
|
@@ -79924,7 +80048,7 @@ function handleProcesses(ctx) {
|
|
|
79924
80048
|
const snapshot = await ctx.processInventory().snapshot();
|
|
79925
80049
|
return c3.json(snapshot);
|
|
79926
80050
|
} catch (err) {
|
|
79927
|
-
if (err instanceof
|
|
80051
|
+
if (err instanceof Error && "code" in err && err.code === "UNSUPPORTED_PLATFORM") {
|
|
79928
80052
|
return c3.json({ error: err.message, code: err.code }, 501);
|
|
79929
80053
|
}
|
|
79930
80054
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -79943,7 +80067,7 @@ function handleToolUseGet(ctx) {
|
|
|
79943
80067
|
limitRaw = parsed;
|
|
79944
80068
|
}
|
|
79945
80069
|
const before = c3.req.query("before") ?? undefined;
|
|
79946
|
-
const limit =
|
|
80070
|
+
const limit = limitRaw === undefined || !Number.isFinite(limitRaw) || limitRaw <= 0 ? 200 : Math.min(limitRaw, 1000);
|
|
79947
80071
|
const snapshot = ctx.tokenLedger().snapshot({ limit, before });
|
|
79948
80072
|
return c3.json(snapshot);
|
|
79949
80073
|
} catch (err) {
|
|
@@ -79972,7 +80096,7 @@ function handleToolUseStream(ctx) {
|
|
|
79972
80096
|
closeController();
|
|
79973
80097
|
};
|
|
79974
80098
|
const stream = new ReadableStream({
|
|
79975
|
-
start(controller) {
|
|
80099
|
+
async start(controller) {
|
|
79976
80100
|
const encoder = new TextEncoder;
|
|
79977
80101
|
closeController = () => {
|
|
79978
80102
|
try {
|
|
@@ -79985,17 +80109,17 @@ function handleToolUseStream(ctx) {
|
|
|
79985
80109
|
}
|
|
79986
80110
|
signal.addEventListener("abort", teardown);
|
|
79987
80111
|
heartbeatInterval = setInterval(sendSseKeepalive, 15000, closed, controller, encoder);
|
|
79988
|
-
enqueueSseFrame(closed, controller, encoder, {
|
|
79989
|
-
type: "connected",
|
|
79990
|
-
occurredAt: new Date().toISOString()
|
|
79991
|
-
});
|
|
79992
80112
|
try {
|
|
79993
80113
|
const path9 = ctx.tokenLedger().path;
|
|
79994
|
-
const watcher = getLedgerWatcher(path9);
|
|
80114
|
+
const watcher = await getLedgerWatcher(path9);
|
|
79995
80115
|
unsubscribe = watcher.subscribe((event2) => {
|
|
79996
80116
|
if (!enqueueSseFrame(closed, controller, encoder, toolUseSsePayload(event2)))
|
|
79997
80117
|
teardown();
|
|
79998
80118
|
});
|
|
80119
|
+
enqueueSseFrame(closed, controller, encoder, {
|
|
80120
|
+
type: "connected",
|
|
80121
|
+
occurredAt: new Date().toISOString()
|
|
80122
|
+
});
|
|
79999
80123
|
} catch {
|
|
80000
80124
|
enqueueSseFrame(closed, controller, encoder, {
|
|
80001
80125
|
type: "error",
|
|
@@ -80027,7 +80151,6 @@ var observabilityModule = {
|
|
|
80027
80151
|
|
|
80028
80152
|
// ../server/src/modules/task/handlers.ts
|
|
80029
80153
|
init_schema4();
|
|
80030
|
-
init_dist();
|
|
80031
80154
|
var os3 = implement(contract);
|
|
80032
80155
|
function toFilters(query) {
|
|
80033
80156
|
if (!query)
|
|
@@ -80068,7 +80191,7 @@ function createTaskHandlers(ctx) {
|
|
|
80068
80191
|
show: os3.task.show.handler(async ({ input }) => {
|
|
80069
80192
|
const result = await ctx.taskService().show(input.wbs);
|
|
80070
80193
|
if (!result)
|
|
80071
|
-
throw new
|
|
80194
|
+
throw new HTTPException(404, { message: `Task ${input.wbs} not found` });
|
|
80072
80195
|
return {
|
|
80073
80196
|
ok: true,
|
|
80074
80197
|
data: {
|
|
@@ -83546,6 +83669,13 @@ var STATUS_TITLE = {
|
|
|
83546
83669
|
done: "Done",
|
|
83547
83670
|
cancelled: "Canceled"
|
|
83548
83671
|
};
|
|
83672
|
+
function canonicalStatusOrRaw(raw2) {
|
|
83673
|
+
try {
|
|
83674
|
+
return normalizeTaskStatus(raw2);
|
|
83675
|
+
} catch {
|
|
83676
|
+
return raw2;
|
|
83677
|
+
}
|
|
83678
|
+
}
|
|
83549
83679
|
function renderTaskBoard(tasks, boardTitle, color, columns2) {
|
|
83550
83680
|
const lines = ["", ` ${color.blue(`Kanban Board \u2014 ${boardTitle}`)}`, ""];
|
|
83551
83681
|
for (const status of columns2) {
|
|
@@ -83631,7 +83761,7 @@ ${result.content}`);
|
|
|
83631
83761
|
context4.setExitCode(1);
|
|
83632
83762
|
}
|
|
83633
83763
|
});
|
|
83634
|
-
task.command("update").summary("Update a task status or replace a section.").argument("<wbs>", "Task WBS number").argument("[status]", "New status (for lifecycle transition)").option("--section <name>", "Section name to replace").option("--from-file <path>", "File to read section body from (requires --section)").option("--feature <id>", "Set the feature_id frontmatter field (traceability edge)").option("--priority <p>", "Set the priority frontmatter field (P0\u2013P3)").option("--no-lifecycle", "Suppress lifecycle workflow run creation (use during pipeline runs to avoid orphaned lifecycle runs)").option("--folder <path>", "Custom tasks folder").option("--json", "Output machine-readable JSON").action(async (wbs, status, options) => {
|
|
83764
|
+
task.command("update").summary("Update a task status or replace a section.").argument("<wbs>", "Task WBS number").argument("[status]", "New status (for lifecycle transition)").option("--section <name>", "Section name to replace").option("--from-file <path>", "File to read section body from (requires --section)").option("--feature <id>", "Set the feature_id frontmatter field (traceability edge)").option("--priority <p>", "Set the priority frontmatter field (P0\u2013P3)").option("--no-lifecycle", "Suppress lifecycle workflow run creation (use during pipeline runs to avoid orphaned lifecycle runs)").option("--force-done", "Allow transitioning to `done` even when the verify verdict is not PASS; records an override (task 0292)").option("--reason <text>", "Rationale for a forced-done override (paired with --force-done; persisted as done_reason)").option("--verdict-dir <path>", "Directory holding <wbs>-verdict.json artifacts (default: .spur/run)").option("--folder <path>", "Custom tasks folder").option("--json", "Output machine-readable JSON").action(async (wbs, status, options) => {
|
|
83635
83765
|
const svc = await makeService2(context4, options.folder, options.lifecycle === false);
|
|
83636
83766
|
try {
|
|
83637
83767
|
if (options.section !== undefined) {
|
|
@@ -83659,6 +83789,7 @@ ${result.content}`);
|
|
|
83659
83789
|
context4.output.write(`Set ${key}=${value2} on task ${result.ref.id}`);
|
|
83660
83790
|
}
|
|
83661
83791
|
} else if (status !== undefined) {
|
|
83792
|
+
status = canonicalStatusOrRaw(status);
|
|
83662
83793
|
if (options.lifecycle !== false && (status === "done" || status === "testing")) {
|
|
83663
83794
|
const adapter = makeLifecycleAdapter(context4, TASK_LIFECYCLE_PROFILE);
|
|
83664
83795
|
if (adapter === undefined) {
|
|
@@ -83671,11 +83802,59 @@ ${result.content}`);
|
|
|
83671
83802
|
}
|
|
83672
83803
|
}
|
|
83673
83804
|
}
|
|
83805
|
+
let forcedDone = false;
|
|
83806
|
+
let forcedDoneReason;
|
|
83807
|
+
let forcedDoneVerdict;
|
|
83808
|
+
if (status === "done") {
|
|
83809
|
+
const current = await svc.show(wbs);
|
|
83810
|
+
const verdictDir = options.verdictDir ?? join22(context4.cwd, ".spur", "run");
|
|
83811
|
+
const loaded = await readVerdictArtifact(context4.fs, verdictDir, wbs);
|
|
83812
|
+
const guardOutcome = evaluateDoneTransition({
|
|
83813
|
+
wbs,
|
|
83814
|
+
taskFilePath: current.filePath,
|
|
83815
|
+
currentStatus: canonicalStatusOrRaw(String(current.frontmatter.status)),
|
|
83816
|
+
targetStatus: "done",
|
|
83817
|
+
forced: options.forceDone === true,
|
|
83818
|
+
reason: options.reason,
|
|
83819
|
+
artifact: loaded.artifact
|
|
83820
|
+
});
|
|
83821
|
+
if (guardOutcome.kind === "noop") {
|
|
83822
|
+
if (options.json) {
|
|
83823
|
+
context4.output.write(toJson2({ ok: true, noop: true, wbs, status: "done" }));
|
|
83824
|
+
} else {
|
|
83825
|
+
context4.output.write(guardOutcome.message);
|
|
83826
|
+
}
|
|
83827
|
+
return;
|
|
83828
|
+
}
|
|
83829
|
+
if (guardOutcome.kind === "deny") {
|
|
83830
|
+
context4.output.error(guardOutcome.message);
|
|
83831
|
+
context4.setExitCode(1);
|
|
83832
|
+
return;
|
|
83833
|
+
}
|
|
83834
|
+
if (guardOutcome.reason === "forced" && loaded.artifact !== undefined) {
|
|
83835
|
+
forcedDone = true;
|
|
83836
|
+
forcedDoneReason = options.reason;
|
|
83837
|
+
forcedDoneVerdict = loaded.artifact.verdict;
|
|
83838
|
+
}
|
|
83839
|
+
}
|
|
83674
83840
|
const result = await svc.updateStatus(wbs, status);
|
|
83841
|
+
if (forcedDone) {
|
|
83842
|
+
try {
|
|
83843
|
+
await svc.updateField(wbs, "done_forced", "true");
|
|
83844
|
+
if (forcedDoneReason !== undefined && forcedDoneReason.length > 0) {
|
|
83845
|
+
await svc.updateField(wbs, "done_reason", forcedDoneReason);
|
|
83846
|
+
}
|
|
83847
|
+
} catch (auditErr) {
|
|
83848
|
+
context4.output.error(`warning: failed to record done-forced audit fields: ${String(auditErr)}`);
|
|
83849
|
+
}
|
|
83850
|
+
}
|
|
83675
83851
|
if (options.json) {
|
|
83676
83852
|
context4.output.write(toJson2(result));
|
|
83677
83853
|
} else {
|
|
83678
83854
|
context4.output.write(`${result.ref.id}: ${result.fromStatus} \u2192 ${result.toStatus}`);
|
|
83855
|
+
if (forcedDone && forcedDoneVerdict !== undefined) {
|
|
83856
|
+
context4.output.write(`\u24D8 Override recorded: task advanced to done despite ${forcedDoneVerdict} verdict (done_forced=true).`);
|
|
83857
|
+
}
|
|
83679
83858
|
}
|
|
83680
83859
|
if (status === "done" && !options.json) {
|
|
83681
83860
|
const task2 = await svc.show(wbs);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as t}from"./BoardApp.oOZMY52P.js";import"./index.A0eX93qW.js";export{t as default};
|