@wrongstack/tools 0.293.0 → 0.295.1
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/dist/_env.d.ts +1 -1
- package/dist/_env.d.ts.map +1 -1
- package/dist/_fetch-guard.d.ts.map +1 -1
- package/dist/_spawn-stream.d.ts +1 -1
- package/dist/_spawn-stream.d.ts.map +1 -1
- package/dist/_util.d.ts +1 -1
- package/dist/_util.d.ts.map +1 -1
- package/dist/audit.d.ts +1 -1
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js +81 -16
- package/dist/audit.js.map +2 -2
- package/dist/auto-proceed-loop-guard.d.ts +31 -0
- package/dist/auto-proceed-loop-guard.d.ts.map +1 -1
- package/dist/auto-proceed-loop-guard.js +24 -1
- package/dist/auto-proceed-loop-guard.js.map +2 -2
- package/dist/bash.d.ts +1 -1
- package/dist/bash.d.ts.map +1 -1
- package/dist/bash.js +105 -75
- package/dist/bash.js.map +3 -3
- package/dist/batch-tool-use.d.ts +1 -1
- package/dist/batch-tool-use.d.ts.map +1 -1
- package/dist/batch-tool-use.js +1 -1
- package/dist/batch-tool-use.js.map +1 -1
- package/dist/browser/index.js +10 -1
- package/dist/browser/index.js.map +2 -2
- package/dist/browser/manager.d.ts +2 -0
- package/dist/browser/manager.d.ts.map +1 -1
- package/dist/browser/tools.d.ts +1 -1
- package/dist/browser/tools.d.ts.map +1 -1
- package/dist/builtin.d.ts +1 -1
- package/dist/builtin.d.ts.map +1 -1
- package/dist/builtin.js +1650 -640
- package/dist/builtin.js.map +4 -4
- package/dist/codebase-index/background-indexer.d.ts +2 -2
- package/dist/codebase-index/background-indexer.d.ts.map +1 -1
- package/dist/codebase-index/bm25.d.ts +3 -1
- package/dist/codebase-index/bm25.d.ts.map +1 -1
- package/dist/codebase-index/codebase-index-tool.d.ts +1 -1
- package/dist/codebase-index/codebase-index-tool.d.ts.map +1 -1
- package/dist/codebase-index/codebase-search-tool.d.ts +1 -1
- package/dist/codebase-index/codebase-search-tool.d.ts.map +1 -1
- package/dist/codebase-index/codebase-stats-tool.d.ts +1 -1
- package/dist/codebase-index/codebase-stats-tool.d.ts.map +1 -1
- package/dist/codebase-index/generic-parser.d.ts +34 -0
- package/dist/codebase-index/generic-parser.d.ts.map +1 -0
- package/dist/codebase-index/go-parser.d.ts +1 -1
- package/dist/codebase-index/go-parser.d.ts.map +1 -1
- package/dist/codebase-index/index.d.ts +4 -2
- package/dist/codebase-index/index.d.ts.map +1 -1
- package/dist/codebase-index/index.js +1068 -372
- package/dist/codebase-index/index.js.map +4 -4
- package/dist/codebase-index/indexer.d.ts +6 -1
- package/dist/codebase-index/indexer.d.ts.map +1 -1
- package/dist/codebase-index/json-parser.d.ts +1 -1
- package/dist/codebase-index/languages.d.ts +25 -0
- package/dist/codebase-index/languages.d.ts.map +1 -0
- package/dist/codebase-index/py-parser.d.ts +9 -1
- package/dist/codebase-index/py-parser.d.ts.map +1 -1
- package/dist/codebase-index/refs-extractor.d.ts.map +1 -1
- package/dist/codebase-index/rs-parser.d.ts +1 -1
- package/dist/codebase-index/rs-parser.d.ts.map +1 -1
- package/dist/codebase-index/schema.d.ts +9 -2
- package/dist/codebase-index/schema.d.ts.map +1 -1
- package/dist/codebase-index/spawn-gate.d.ts +15 -0
- package/dist/codebase-index/spawn-gate.d.ts.map +1 -0
- package/dist/codebase-index/ts-parser.d.ts +2 -2
- package/dist/codebase-index/ts-parser.d.ts.map +1 -1
- package/dist/codebase-index/worker.js +993 -348
- package/dist/codebase-index/worker.js.map +4 -4
- package/dist/codebase-index/writer.d.ts +83 -4
- package/dist/codebase-index/writer.d.ts.map +1 -1
- package/dist/codebase-index/yaml-parser.d.ts +1 -1
- package/dist/design.d.ts +5 -2
- package/dist/design.d.ts.map +1 -1
- package/dist/design.js +36 -3
- package/dist/design.js.map +2 -2
- package/dist/diff.d.ts +1 -1
- package/dist/diff.d.ts.map +1 -1
- package/dist/diff.js +2 -2
- package/dist/diff.js.map +1 -1
- package/dist/document.d.ts +1 -1
- package/dist/document.d.ts.map +1 -1
- package/dist/document.js +1 -1
- package/dist/document.js.map +1 -1
- package/dist/e2e.d.ts +1 -1
- package/dist/e2e.d.ts.map +1 -1
- package/dist/e2e.js +1 -1
- package/dist/e2e.js.map +1 -1
- package/dist/edit.d.ts +1 -1
- package/dist/edit.d.ts.map +1 -1
- package/dist/edit.js +3 -3
- package/dist/edit.js.map +2 -2
- package/dist/exec.d.ts +1 -1
- package/dist/exec.d.ts.map +1 -1
- package/dist/exec.js +99 -44
- package/dist/exec.js.map +3 -3
- package/dist/fetch.d.ts +1 -1
- package/dist/fetch.d.ts.map +1 -1
- package/dist/fetch.js +4 -5
- package/dist/fetch.js.map +2 -2
- package/dist/format.d.ts +1 -1
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +81 -16
- package/dist/format.js.map +2 -2
- package/dist/git.d.ts +1 -1
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +3 -2
- package/dist/git.js.map +2 -2
- package/dist/glob.d.ts +1 -1
- package/dist/glob.d.ts.map +1 -1
- package/dist/glob.js +81 -31
- package/dist/glob.js.map +4 -4
- package/dist/grep.d.ts +1 -1
- package/dist/grep.d.ts.map +1 -1
- package/dist/grep.js +90 -22
- package/dist/grep.js.map +4 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1895 -771
- package/dist/index.js.map +4 -4
- package/dist/install.d.ts +1 -1
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +85 -17
- package/dist/install.js.map +2 -2
- package/dist/json.d.ts +1 -1
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +2 -2
- package/dist/json.js.map +2 -2
- package/dist/kanban-evidence-bridge.d.ts +21 -0
- package/dist/kanban-evidence-bridge.d.ts.map +1 -0
- package/dist/kanban.d.ts +20 -3
- package/dist/kanban.d.ts.map +1 -1
- package/dist/kanban.js +256 -32
- package/dist/kanban.js.map +3 -3
- package/dist/languages/execute-tool.d.ts +1 -1
- package/dist/languages/execute-tool.d.ts.map +1 -1
- package/dist/languages/execute.d.ts +1 -1
- package/dist/languages/execute.d.ts.map +1 -1
- package/dist/languages/index.d.ts +1 -1
- package/dist/languages/index.d.ts.map +1 -1
- package/dist/languages/index.js +99 -17
- package/dist/languages/index.js.map +2 -2
- package/dist/languages/package-tool.d.ts +3 -4
- package/dist/languages/package-tool.d.ts.map +1 -1
- package/dist/languages/plan.d.ts.map +1 -1
- package/dist/languages/profiles/additional.d.ts.map +1 -1
- package/dist/languages/profiles/primary.d.ts.map +1 -1
- package/dist/languages/tool.d.ts +1 -1
- package/dist/languages/tool.d.ts.map +1 -1
- package/dist/lint.d.ts +1 -1
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +81 -16
- package/dist/lint.js.map +2 -2
- package/dist/logs.d.ts +1 -1
- package/dist/logs.d.ts.map +1 -1
- package/dist/logs.js +2 -2
- package/dist/logs.js.map +1 -1
- package/dist/memory.d.ts +1 -1
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +1 -1
- package/dist/memory.js.map +1 -1
- package/dist/mode.d.ts +1 -1
- package/dist/mode.d.ts.map +1 -1
- package/dist/mode.js.map +1 -1
- package/dist/outdated.d.ts +1 -1
- package/dist/outdated.d.ts.map +1 -1
- package/dist/outdated.js +101 -19
- package/dist/outdated.js.map +2 -2
- package/dist/pack.d.ts +1 -1
- package/dist/pack.js +1650 -640
- package/dist/pack.js.map +4 -4
- package/dist/patch.d.ts +1 -1
- package/dist/patch.d.ts.map +1 -1
- package/dist/patch.js +2 -2
- package/dist/patch.js.map +1 -1
- package/dist/plan.d.ts +1 -1
- package/dist/plan.d.ts.map +1 -1
- package/dist/plan.js +7 -7
- package/dist/plan.js.map +2 -2
- package/dist/process-guardian.d.ts +7 -0
- package/dist/process-guardian.d.ts.map +1 -1
- package/dist/process-registry-persistent.d.ts +5 -0
- package/dist/process-registry-persistent.d.ts.map +1 -1
- package/dist/process-registry.d.ts +9 -1
- package/dist/process-registry.d.ts.map +1 -1
- package/dist/process-registry.js +18 -3
- package/dist/process-registry.js.map +2 -2
- package/dist/ps-slash.js +93 -38
- package/dist/ps-slash.js.map +2 -2
- package/dist/read.d.ts +1 -1
- package/dist/read.d.ts.map +1 -1
- package/dist/read.js +3 -2
- package/dist/read.js.map +2 -2
- package/dist/replace.d.ts +1 -1
- package/dist/replace.d.ts.map +1 -1
- package/dist/replace.js +4 -4
- package/dist/replace.js.map +2 -2
- package/dist/scaffold.d.ts +1 -1
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +2 -2
- package/dist/scaffold.js.map +1 -1
- package/dist/search.d.ts +1 -1
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +4 -4
- package/dist/search.js.map +2 -2
- package/dist/session-kanban.d.ts +4 -1
- package/dist/session-kanban.d.ts.map +1 -1
- package/dist/session-kanban.js +23 -15
- package/dist/session-kanban.js.map +2 -2
- package/dist/set-working-dir.d.ts +1 -1
- package/dist/set-working-dir.d.ts.map +1 -1
- package/dist/skill.d.ts +1 -1
- package/dist/skill.d.ts.map +1 -1
- package/dist/skill.js +2 -5
- package/dist/skill.js.map +2 -2
- package/dist/spawn-background.d.ts.map +1 -1
- package/dist/task.d.ts +3 -2
- package/dist/task.d.ts.map +1 -1
- package/dist/task.js +6 -8
- package/dist/task.js.map +2 -2
- package/dist/test.d.ts +1 -1
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +81 -16
- package/dist/test.js.map +2 -2
- package/dist/todo.d.ts +2 -1
- package/dist/todo.d.ts.map +1 -1
- package/dist/todo.js +4 -4
- package/dist/todo.js.map +2 -2
- package/dist/tool-help.d.ts +1 -1
- package/dist/tool-help.d.ts.map +1 -1
- package/dist/tool-help.js.map +1 -1
- package/dist/tool-search.d.ts +1 -1
- package/dist/tool-search.d.ts.map +1 -1
- package/dist/tool-search.js.map +1 -1
- package/dist/tool-tier.d.ts +16 -0
- package/dist/tool-tier.d.ts.map +1 -0
- package/dist/tool-tier.js +22402 -0
- package/dist/tool-tier.js.map +7 -0
- package/dist/tool-use.d.ts +1 -1
- package/dist/tool-use.d.ts.map +1 -1
- package/dist/tool-use.js.map +1 -1
- package/dist/tree.d.ts +1 -1
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +3 -15
- package/dist/tree.js.map +2 -2
- package/dist/typecheck.d.ts +1 -1
- package/dist/typecheck.d.ts.map +1 -1
- package/dist/typecheck.js +81 -16
- package/dist/typecheck.js.map +2 -2
- package/dist/win32.d.ts +10 -0
- package/dist/win32.d.ts.map +1 -0
- package/dist/win32.js +67 -0
- package/dist/win32.js.map +7 -0
- package/dist/write.d.ts +1 -1
- package/dist/write.d.ts.map +1 -1
- package/dist/write.js +3 -2
- package/dist/write.js.map +2 -2
- package/package.json +11 -3
package/dist/kanban.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// src/kanban.ts
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { deserializeTaskGraph as deserializeTaskGraph2,
|
|
3
|
+
import { deserializeTaskGraph as deserializeTaskGraph2, serializeTaskGraph } from "@wrongstack/core/tasking";
|
|
4
|
+
import { loadTasks as loadTasks2 } from "@wrongstack/core/storage";
|
|
4
5
|
import {
|
|
5
6
|
addCheckToTask,
|
|
6
7
|
addColumn,
|
|
@@ -47,18 +48,45 @@ import {
|
|
|
47
48
|
updateColumn,
|
|
48
49
|
updateGoalMetricOnTask,
|
|
49
50
|
updateTask,
|
|
50
|
-
updateTaskAssignment
|
|
51
|
+
updateTaskAssignment,
|
|
52
|
+
verifyTaskCompletion,
|
|
53
|
+
finalizeTaskCompletion,
|
|
54
|
+
assessTaskAtomicity,
|
|
55
|
+
proposeTaskDecomposition
|
|
51
56
|
} from "@wrongstack/kanban";
|
|
52
57
|
|
|
58
|
+
// src/kanban-evidence-bridge.ts
|
|
59
|
+
import { recordCompletedWorkEvidence } from "@wrongstack/core/utils";
|
|
60
|
+
function kanbanEvidenceKey(boardId, taskId) {
|
|
61
|
+
return `kanban:${boardId}:${taskId}`;
|
|
62
|
+
}
|
|
63
|
+
function kanbanEvidencePointer(boardId, taskId) {
|
|
64
|
+
return `kanban://${boardId}/${taskId}#verificationReport`;
|
|
65
|
+
}
|
|
66
|
+
function recordKanbanVerificationEvidence(ctx, report) {
|
|
67
|
+
try {
|
|
68
|
+
const passed = report.checks.filter((check) => check.status === "passed").length;
|
|
69
|
+
const completedAt = Date.parse(report.completedAt);
|
|
70
|
+
recordCompletedWorkEvidence(ctx, {
|
|
71
|
+
key: kanbanEvidenceKey(report.boardId, report.taskId),
|
|
72
|
+
source: "verification",
|
|
73
|
+
summary: `${report.taskTitle} \u2014 verification ${report.verdict} (${passed}/${report.checks.length} checks)`,
|
|
74
|
+
...Number.isFinite(completedAt) ? { completedAt } : {},
|
|
75
|
+
evidence: kanbanEvidencePointer(report.boardId, report.taskId)
|
|
76
|
+
});
|
|
77
|
+
} catch {
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
53
81
|
// src/session-kanban.ts
|
|
82
|
+
import { GlobalMailbox } from "@wrongstack/core/coordination";
|
|
54
83
|
import {
|
|
55
|
-
deserializeTaskGraph,
|
|
56
|
-
GlobalMailbox,
|
|
57
84
|
loadPlan,
|
|
58
85
|
loadTasks,
|
|
59
86
|
mutatePlan,
|
|
60
87
|
mutateTasks
|
|
61
|
-
} from "@wrongstack/core";
|
|
88
|
+
} from "@wrongstack/core/storage";
|
|
89
|
+
import { deserializeTaskGraph } from "@wrongstack/core/tasking";
|
|
62
90
|
import { resolveWstackPaths } from "@wrongstack/core/utils";
|
|
63
91
|
import {
|
|
64
92
|
createBoard,
|
|
@@ -111,8 +139,8 @@ function taskFileToSerializedGraph(tasks, sessionId) {
|
|
|
111
139
|
var kanbanTool = {
|
|
112
140
|
name: "kanban",
|
|
113
141
|
category: "Project",
|
|
114
|
-
description: "Manage and audit project-scoped multi-kanban boards stored under .wrongstack/kanbans. Managed cards enforce fully specified details and adjacent Backlog \u2192 Todo \u2192 Running \u2192 Review \u2192 Done transitions with persistent comments and evidence. Successful board access records live agent/session presence.",
|
|
115
|
-
usageHint: "Use this for durable project kanban state. Reassess with get_board whenever evidence changes; agents may add, update, split, merge, reprioritize, or remove tasks so the board remains a live plan. Presence includes active/last-seen session and agent metadata. For managed boards, fully fill card details, use transition_task after every material step, attach truthful evidence, and never use update_task/move_task to bypass lifecycle guards. Worker completion enters Review; only passed acceptance criteria plus review evidence allow Done.",
|
|
142
|
+
description: "Manage and audit project-scoped multi-kanban boards stored under .wrongstack/kanbans. Managed cards enforce fully specified details and adjacent Backlog \u2192 Todo \u2192 Running \u2192 Review \u2192 Done transitions with persistent comments and evidence. Successful board access records live agent/session presence. Use verify_completion to validate a task against its success criteria and persist the verification report. Use split_atomic to split a task into child tasks with parent.atomic=true in a single atomic board mutation, enforcing subtree verification before the parent can complete.",
|
|
143
|
+
usageHint: "Use this for durable project kanban state. Reassess with get_board whenever evidence changes; agents may add, update, split, merge, reprioritize, or remove tasks so the board remains a live plan. Presence includes active/last-seen session and agent metadata. For managed boards, fully fill card details, use transition_task after every material step, attach truthful evidence, and never use update_task/move_task to bypass lifecycle guards. Worker completion enters Review; only passed acceptance criteria plus review evidence allow Done. Use verify_completion to generate a verification report (persisted automatically) and split_atomic to atomically create child subtasks with the atomic flag pre-set.",
|
|
116
144
|
permission: "confirm",
|
|
117
145
|
mutating: true,
|
|
118
146
|
capabilities: ["fs.write"],
|
|
@@ -168,7 +196,11 @@ var kanbanTool = {
|
|
|
168
196
|
"add_check",
|
|
169
197
|
"update_check",
|
|
170
198
|
"add_note",
|
|
171
|
-
"add_link"
|
|
199
|
+
"add_link",
|
|
200
|
+
"verify_completion",
|
|
201
|
+
"split_atomic",
|
|
202
|
+
"assess_atomicity",
|
|
203
|
+
"propose_decomposition"
|
|
172
204
|
]
|
|
173
205
|
},
|
|
174
206
|
boardId: { type: "string" },
|
|
@@ -223,6 +255,7 @@ var kanbanTool = {
|
|
|
223
255
|
runTaskId: { type: "string" },
|
|
224
256
|
lastResult: { type: "string" },
|
|
225
257
|
error: { type: "string" },
|
|
258
|
+
expectedLeaseId: { type: "string" },
|
|
226
259
|
assignmentStatus: {
|
|
227
260
|
type: "string",
|
|
228
261
|
enum: ["assigned", "queued", "running", "completed", "failed", "cancelled"]
|
|
@@ -257,6 +290,7 @@ var kanbanTool = {
|
|
|
257
290
|
lastFailureKind: { type: "string" },
|
|
258
291
|
dependsOn: { type: "array", items: { type: "string" } },
|
|
259
292
|
estimatedHours: { type: "number" },
|
|
293
|
+
actualHours: { type: "number" },
|
|
260
294
|
taskGraph: { type: "object" },
|
|
261
295
|
graphId: { type: "string" },
|
|
262
296
|
specId: { type: "string" },
|
|
@@ -302,7 +336,24 @@ var kanbanTool = {
|
|
|
302
336
|
includeCompletedTasks: { type: "boolean" },
|
|
303
337
|
preserveAssignment: { type: "boolean" },
|
|
304
338
|
preserveDependencies: { type: "boolean" },
|
|
305
|
-
moveTasksToColumnId: { type: "string" }
|
|
339
|
+
moveTasksToColumnId: { type: "string" },
|
|
340
|
+
atomicityMode: { type: "string", enum: ["off", "assess", "enforce"] },
|
|
341
|
+
atomicityDecomposition: { type: "string", enum: ["auto", "propose"] },
|
|
342
|
+
gateEnforcement: { type: "string", enum: ["strict", "soft", "off"] },
|
|
343
|
+
subtasks: {
|
|
344
|
+
type: "array",
|
|
345
|
+
minItems: 2,
|
|
346
|
+
items: {
|
|
347
|
+
type: "object",
|
|
348
|
+
properties: {
|
|
349
|
+
title: { type: "string" },
|
|
350
|
+
description: { type: "string" },
|
|
351
|
+
successCriteria: { type: "array", items: { type: "string" } },
|
|
352
|
+
dependsOnIndex: { type: "array", items: { type: "number" } }
|
|
353
|
+
},
|
|
354
|
+
required: ["title"]
|
|
355
|
+
}
|
|
356
|
+
}
|
|
306
357
|
},
|
|
307
358
|
required: ["action"]
|
|
308
359
|
},
|
|
@@ -342,7 +393,14 @@ var kanbanTool = {
|
|
|
342
393
|
title: input.title,
|
|
343
394
|
...input.description !== void 0 ? { description: input.description } : {},
|
|
344
395
|
...input.tags !== void 0 ? { tags: input.tags } : {},
|
|
345
|
-
...input.generatedBy !== void 0 ? { generatedBy: input.generatedBy } : {}
|
|
396
|
+
...input.generatedBy !== void 0 ? { generatedBy: input.generatedBy } : {},
|
|
397
|
+
...input.atomicityMode !== void 0 ? {
|
|
398
|
+
atomicity: {
|
|
399
|
+
mode: input.atomicityMode,
|
|
400
|
+
decomposition: input.atomicityDecomposition ?? "propose"
|
|
401
|
+
}
|
|
402
|
+
} : {},
|
|
403
|
+
...input.gateEnforcement !== void 0 ? { completionGate: { enforcement: input.gateEnforcement } } : {}
|
|
346
404
|
});
|
|
347
405
|
return { ok: true, message: `Board created: ${board.title}`, board };
|
|
348
406
|
}
|
|
@@ -351,7 +409,14 @@ var kanbanTool = {
|
|
|
351
409
|
const board = await updateBoard2(projectRoot, input.boardId, {
|
|
352
410
|
...input.title !== void 0 ? { title: input.title } : {},
|
|
353
411
|
...input.description !== void 0 ? { description: input.description } : {},
|
|
354
|
-
...input.tags !== void 0 ? { tags: input.tags } : {}
|
|
412
|
+
...input.tags !== void 0 ? { tags: input.tags } : {},
|
|
413
|
+
...input.atomicityMode !== void 0 ? {
|
|
414
|
+
atomicity: {
|
|
415
|
+
mode: input.atomicityMode,
|
|
416
|
+
decomposition: input.atomicityDecomposition ?? "propose"
|
|
417
|
+
}
|
|
418
|
+
} : {},
|
|
419
|
+
...input.gateEnforcement !== void 0 ? { completionGate: { enforcement: input.gateEnforcement } } : {}
|
|
355
420
|
});
|
|
356
421
|
return board ? okBoard(board, "Board updated.") : fail("Board not found.");
|
|
357
422
|
}
|
|
@@ -559,30 +624,18 @@ var kanbanTool = {
|
|
|
559
624
|
case "add_task": {
|
|
560
625
|
if (!input.boardId || !input.title) return fail("add_task requires boardId and title.");
|
|
561
626
|
const result2 = await addTask(projectRoot, input.boardId, taskInput(input));
|
|
562
|
-
|
|
627
|
+
if (!result2) return fail("Board not found.");
|
|
628
|
+
return okTask(
|
|
629
|
+
result2.board,
|
|
630
|
+
result2.task,
|
|
631
|
+
`Task added.${atomicityNudge(result2.task)}`
|
|
632
|
+
);
|
|
563
633
|
}
|
|
564
634
|
case "split_task": {
|
|
565
635
|
if (!input.boardId || !input.taskId || !input.childTitles?.length) {
|
|
566
636
|
return fail("split_task requires boardId, taskId, and childTitles.");
|
|
567
637
|
}
|
|
568
|
-
|
|
569
|
-
titles: input.childTitles,
|
|
570
|
-
...input.targetColumnId !== void 0 ? { columnId: input.targetColumnId } : {},
|
|
571
|
-
...input.inheritAssignment !== void 0 ? { inheritAssignment: input.inheritAssignment } : {},
|
|
572
|
-
...input.inheritLabels !== void 0 ? { inheritLabels: input.inheritLabels } : {},
|
|
573
|
-
...input.inheritSuccessCriteria !== void 0 ? { inheritSuccessCriteria: input.inheritSuccessCriteria } : {},
|
|
574
|
-
...input.inheritGoalMetrics !== void 0 ? { inheritGoalMetrics: input.inheritGoalMetrics } : {},
|
|
575
|
-
...input.inheritDependencies !== void 0 ? { inheritDependencies: input.inheritDependencies } : {},
|
|
576
|
-
...input.chainChildren !== void 0 ? { chainChildren: input.chainChildren } : {},
|
|
577
|
-
...input.rewireDependents !== void 0 ? { rewireDependents: input.rewireDependents } : {}
|
|
578
|
-
});
|
|
579
|
-
return result2 ? {
|
|
580
|
-
ok: true,
|
|
581
|
-
message: `${result2.children.length} child task(s) created.`,
|
|
582
|
-
board: result2.board,
|
|
583
|
-
task: result2.parent,
|
|
584
|
-
children: result2.children
|
|
585
|
-
} : fail("Task not found.");
|
|
638
|
+
return handleSplitTask(projectRoot, input, {});
|
|
586
639
|
}
|
|
587
640
|
case "merge_tasks": {
|
|
588
641
|
if (!input.boardId || !input.taskIds?.length || !input.title) {
|
|
@@ -656,6 +709,19 @@ var kanbanTool = {
|
|
|
656
709
|
"transition_task requires boardId, taskId, lifecycleStage, author, and transitionComment."
|
|
657
710
|
);
|
|
658
711
|
}
|
|
712
|
+
if (input.lifecycleStage === "done") {
|
|
713
|
+
const boardBefore = await getBoard2(projectRoot, input.boardId);
|
|
714
|
+
const taskBefore = boardBefore ? await getTask(projectRoot, input.boardId, input.taskId) : null;
|
|
715
|
+
if (boardBefore && taskBefore && !taskBefore.verificationReport && (taskBefore.atomic || Boolean(taskBefore.successCriteria?.length))) {
|
|
716
|
+
const preGate = await verifyTaskCompletion(projectRoot, input.boardId, taskBefore.id, {
|
|
717
|
+
persist: false
|
|
718
|
+
});
|
|
719
|
+
await updateTask(projectRoot, input.boardId, taskBefore.id, {
|
|
720
|
+
verificationReport: preGate.report,
|
|
721
|
+
successCriteria: preGate.task.successCriteria
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
}
|
|
659
725
|
const result2 = await transitionTask(projectRoot, input.boardId, input.taskId, {
|
|
660
726
|
to: input.lifecycleStage,
|
|
661
727
|
actor: input.author,
|
|
@@ -670,6 +736,9 @@ var kanbanTool = {
|
|
|
670
736
|
} : {},
|
|
671
737
|
patch: taskPatch(input)
|
|
672
738
|
});
|
|
739
|
+
if (result2 && input.lifecycleStage === "done" && result2.task.verificationReport) {
|
|
740
|
+
recordKanbanVerificationEvidence(ctx, result2.task.verificationReport);
|
|
741
|
+
}
|
|
673
742
|
return result2 ? okTask(result2.board, result2.task, `Task advanced to ${result2.transition.to}.`) : fail("Board or task not found.");
|
|
674
743
|
}
|
|
675
744
|
case "move_task": {
|
|
@@ -783,7 +852,31 @@ var kanbanTool = {
|
|
|
783
852
|
// is atomic inside updateTaskAssignment's mutateBoard lock.
|
|
784
853
|
input.expectedLeaseId !== void 0 ? { expectedLeaseId: input.expectedLeaseId } : {}
|
|
785
854
|
);
|
|
786
|
-
|
|
855
|
+
if (!board) return fail("Task not found.");
|
|
856
|
+
if (assignmentStatus === "completed") {
|
|
857
|
+
const envGate = readEnvGateEnforcement();
|
|
858
|
+
const finalized = await finalizeTaskCompletion(projectRoot, board.id, input.taskId, {
|
|
859
|
+
...board.completionGate === void 0 && envGate !== void 0 ? { enforcement: envGate } : {},
|
|
860
|
+
...ctx.agentId !== void 0 ? { eventContext: { actor: ctx.agentId } } : {}
|
|
861
|
+
});
|
|
862
|
+
if (finalized) {
|
|
863
|
+
if (finalized.gate.report) {
|
|
864
|
+
recordKanbanVerificationEvidence(ctx, finalized.gate.report);
|
|
865
|
+
}
|
|
866
|
+
const gateSummary = {
|
|
867
|
+
enforcement: finalized.gate.enforcement,
|
|
868
|
+
allowed: finalized.gate.allowed,
|
|
869
|
+
verdict: finalized.gate.verdict,
|
|
870
|
+
issues: finalized.gate.issues.map((issue) => issue.message)
|
|
871
|
+
};
|
|
872
|
+
const gateMessage = finalized.gate.allowed ? `Completion gate ${finalized.gate.verdict === "skipped" ? "skipped" : "passed"}; task completed.` : finalized.gate.enforcement === "strict" ? `Completion gate BLOCKED (verdict: ${finalized.gate.verdict}); task parked in review. Issues: ${gateSummary.issues.join(" | ")}` : `Completion gate failed softly (verdict: ${finalized.gate.verdict}); task completed with warnings. Issues: ${gateSummary.issues.join(" | ")}`;
|
|
873
|
+
return {
|
|
874
|
+
...okTask(finalized.board, finalized.task, `Assignment updated. ${gateMessage}`),
|
|
875
|
+
gate: gateSummary
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
return okBoard(board, "Assignment updated.");
|
|
787
880
|
}
|
|
788
881
|
case "heartbeat_assignment": {
|
|
789
882
|
if (!input.boardId || !input.taskId) {
|
|
@@ -947,6 +1040,83 @@ var kanbanTool = {
|
|
|
947
1040
|
});
|
|
948
1041
|
return board ? okBoard(board, "Link added.") : fail("Task not found.");
|
|
949
1042
|
}
|
|
1043
|
+
case "assess_atomicity": {
|
|
1044
|
+
if (!input.boardId || !input.taskId) {
|
|
1045
|
+
return fail("assess_atomicity requires boardId and taskId.");
|
|
1046
|
+
}
|
|
1047
|
+
const result2 = await assessTaskAtomicity(projectRoot, input.boardId, input.taskId, {
|
|
1048
|
+
assessedBy: "agent",
|
|
1049
|
+
...ctx.agentId !== void 0 ? { eventContext: { actor: ctx.agentId } } : {}
|
|
1050
|
+
});
|
|
1051
|
+
if (!result2) return fail("Task not found.");
|
|
1052
|
+
const failing = result2.assessment.criteria.filter((entry) => entry.score < 1).map((entry) => entry.reason);
|
|
1053
|
+
const guidance = result2.assessment.verdict === "needs_decomposition" ? ` This task should be split before dispatch \u2014 call propose_decomposition with 2+ subtasks (each with one verifiable success criterion). Reasons: ${failing.join(" | ")}` : result2.assessment.verdict === "composite" ? " Container task: work happens in its children; it is verified via subtask aggregation." : "";
|
|
1054
|
+
return okTask(
|
|
1055
|
+
result2.board,
|
|
1056
|
+
result2.task,
|
|
1057
|
+
`Atomicity verdict: ${result2.assessment.verdict} (score ${result2.assessment.score}).${guidance}`
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
1060
|
+
case "propose_decomposition": {
|
|
1061
|
+
if (!input.boardId || !input.taskId || !input.subtasks?.length) {
|
|
1062
|
+
return fail("propose_decomposition requires boardId, taskId, and subtasks (2+).");
|
|
1063
|
+
}
|
|
1064
|
+
if (input.subtasks.length < 2) {
|
|
1065
|
+
return fail("propose_decomposition requires at least two subtasks.");
|
|
1066
|
+
}
|
|
1067
|
+
const invalid = input.subtasks.find(
|
|
1068
|
+
(subtask) => typeof subtask?.title !== "string" || !subtask.title.trim()
|
|
1069
|
+
);
|
|
1070
|
+
if (invalid) return fail("Every proposed subtask needs a non-blank title.");
|
|
1071
|
+
const result2 = await proposeTaskDecomposition(
|
|
1072
|
+
projectRoot,
|
|
1073
|
+
input.boardId,
|
|
1074
|
+
input.taskId,
|
|
1075
|
+
{
|
|
1076
|
+
subtasks: input.subtasks,
|
|
1077
|
+
...input.note !== void 0 ? { rationale: input.note } : {},
|
|
1078
|
+
...ctx.agentId !== void 0 ? { proposedBy: ctx.agentId } : {}
|
|
1079
|
+
},
|
|
1080
|
+
ctx.agentId !== void 0 ? { actor: ctx.agentId } : {}
|
|
1081
|
+
);
|
|
1082
|
+
if (!result2) return fail("Task not found.");
|
|
1083
|
+
const message = result2.proposal.status === "applied" ? `Decomposition applied: ${result2.proposal.appliedChildTaskIds?.length ?? 0} child tasks created (parent marked atomic).` : 'Decomposition proposal recorded \u2014 awaiting approval (board policy is "propose"). It can be approved from the WebUI or via update_task.';
|
|
1084
|
+
return okTask(result2.board, result2.task, message);
|
|
1085
|
+
}
|
|
1086
|
+
case "verify_completion": {
|
|
1087
|
+
if (!input.boardId || !input.taskId) {
|
|
1088
|
+
return fail("verify_completion requires boardId and taskId.");
|
|
1089
|
+
}
|
|
1090
|
+
const verResult = await verifyTaskCompletion(projectRoot, input.boardId, input.taskId);
|
|
1091
|
+
const persistedBoard = await updateTask(projectRoot, input.boardId, input.taskId, {
|
|
1092
|
+
verificationReport: verResult.report,
|
|
1093
|
+
successCriteria: verResult.task.successCriteria
|
|
1094
|
+
});
|
|
1095
|
+
if (!persistedBoard) {
|
|
1096
|
+
return {
|
|
1097
|
+
ok: false,
|
|
1098
|
+
verdict: verResult.report.verdict,
|
|
1099
|
+
message: `Verification succeeded but persist failed: ${verResult.report.markdownSummary}. Board may be stale \u2014 re-run verify_completion.`,
|
|
1100
|
+
board: verResult.board
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
recordKanbanVerificationEvidence(ctx, verResult.report);
|
|
1104
|
+
const freshTask = persistedBoard.tasks?.find((t) => t.id === input.taskId);
|
|
1105
|
+
const deterministicVerdicts = ["passed", "failed", "needs_human", "incomplete"];
|
|
1106
|
+
return {
|
|
1107
|
+
ok: deterministicVerdicts.includes(verResult.report.verdict),
|
|
1108
|
+
verdict: verResult.report.verdict,
|
|
1109
|
+
message: verResult.report.markdownSummary,
|
|
1110
|
+
board: persistedBoard,
|
|
1111
|
+
task: freshTask ?? verResult.task
|
|
1112
|
+
};
|
|
1113
|
+
}
|
|
1114
|
+
case "split_atomic": {
|
|
1115
|
+
if (!input.boardId || !input.taskId || !input.childTitles?.length) {
|
|
1116
|
+
return fail("split_atomic requires boardId, taskId, and childTitles (at least one).");
|
|
1117
|
+
}
|
|
1118
|
+
return handleSplitTask(projectRoot, input, { atomic: true });
|
|
1119
|
+
}
|
|
950
1120
|
default:
|
|
951
1121
|
return fail(`Unknown kanban action: ${input.action}`);
|
|
952
1122
|
}
|
|
@@ -957,6 +1127,15 @@ var kanbanTool = {
|
|
|
957
1127
|
}
|
|
958
1128
|
}
|
|
959
1129
|
};
|
|
1130
|
+
function atomicityNudge(task) {
|
|
1131
|
+
if (task.atomicityAssessment?.verdict !== "needs_decomposition") return "";
|
|
1132
|
+
const reasons = task.atomicityAssessment.criteria.filter((entry) => entry.score < 1).map((entry) => entry.reason).join(" | ");
|
|
1133
|
+
return ` Atomicity: needs_decomposition (score ${task.atomicityAssessment.score}) \u2014 call propose_decomposition with 2+ subtasks before dispatch. Reasons: ${reasons}`;
|
|
1134
|
+
}
|
|
1135
|
+
function readEnvGateEnforcement() {
|
|
1136
|
+
const raw = process.env["WRONGSTACK_KANBAN_GATE"]?.trim().toLowerCase();
|
|
1137
|
+
return raw === "strict" || raw === "soft" || raw === "off" ? raw : void 0;
|
|
1138
|
+
}
|
|
960
1139
|
function fail(message) {
|
|
961
1140
|
return { ok: false, message };
|
|
962
1141
|
}
|
|
@@ -966,6 +1145,50 @@ function okBoard(board, message = "Board loaded.") {
|
|
|
966
1145
|
function okTask(board, task, message) {
|
|
967
1146
|
return { ok: true, message, board, task };
|
|
968
1147
|
}
|
|
1148
|
+
async function handleSplitTask(projectRoot, input, extraSplitOptions) {
|
|
1149
|
+
const boardId = input.boardId;
|
|
1150
|
+
const taskId = input.taskId;
|
|
1151
|
+
const childTitles = input.childTitles;
|
|
1152
|
+
if (!boardId || !taskId || !childTitles?.length) {
|
|
1153
|
+
return fail("split requires boardId, taskId, and at least one childTitles.");
|
|
1154
|
+
}
|
|
1155
|
+
const {
|
|
1156
|
+
targetColumnId,
|
|
1157
|
+
inheritAssignment,
|
|
1158
|
+
inheritLabels,
|
|
1159
|
+
inheritSuccessCriteria,
|
|
1160
|
+
inheritGoalMetrics,
|
|
1161
|
+
inheritDependencies,
|
|
1162
|
+
chainChildren,
|
|
1163
|
+
rewireDependents
|
|
1164
|
+
} = input;
|
|
1165
|
+
const result = await splitTask(projectRoot, boardId, taskId, {
|
|
1166
|
+
titles: childTitles,
|
|
1167
|
+
...extraSplitOptions,
|
|
1168
|
+
...targetColumnId !== void 0 ? { columnId: targetColumnId } : {},
|
|
1169
|
+
...inheritAssignment !== void 0 ? { inheritAssignment } : {},
|
|
1170
|
+
...inheritLabels !== void 0 ? { inheritLabels } : {},
|
|
1171
|
+
...inheritSuccessCriteria !== void 0 ? { inheritSuccessCriteria } : {},
|
|
1172
|
+
...inheritGoalMetrics !== void 0 ? { inheritGoalMetrics } : {},
|
|
1173
|
+
...inheritDependencies !== void 0 ? { inheritDependencies } : {},
|
|
1174
|
+
...chainChildren !== void 0 ? { chainChildren } : {},
|
|
1175
|
+
...rewireDependents !== void 0 ? { rewireDependents } : {}
|
|
1176
|
+
});
|
|
1177
|
+
if (!result) return fail("Task not found.");
|
|
1178
|
+
const freshParent = result.board.tasks?.find((t) => t.id === taskId);
|
|
1179
|
+
if (!freshParent) {
|
|
1180
|
+
return fail(
|
|
1181
|
+
`Split succeeded but parent ${taskId} not found in returned board. Children: [${result.children.map((c) => c.id).join(", ")}].`
|
|
1182
|
+
);
|
|
1183
|
+
}
|
|
1184
|
+
return {
|
|
1185
|
+
ok: true,
|
|
1186
|
+
message: `${result.children.length} child task(s) created.`,
|
|
1187
|
+
board: result.board,
|
|
1188
|
+
task: freshParent,
|
|
1189
|
+
children: result.children
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
969
1192
|
async function requireBoard(projectRoot, boardId) {
|
|
970
1193
|
return boardId ? getBoard2(projectRoot, boardId) : null;
|
|
971
1194
|
}
|
|
@@ -1067,7 +1290,8 @@ function taskPatch(input) {
|
|
|
1067
1290
|
labels: input.labels,
|
|
1068
1291
|
assignedAgent: input.agentId,
|
|
1069
1292
|
...mergedDependsOn(input) ? { dependsOn: mergedDependsOn(input) } : {},
|
|
1070
|
-
...input.estimatedHours !== void 0 ? { estimatedHours: input.estimatedHours } : {}
|
|
1293
|
+
...input.estimatedHours !== void 0 ? { estimatedHours: input.estimatedHours } : {},
|
|
1294
|
+
...input.actualHours !== void 0 ? { actualHours: input.actualHours } : {}
|
|
1071
1295
|
};
|
|
1072
1296
|
}
|
|
1073
1297
|
function assignmentInput(input) {
|