patchwarden 0.6.4 → 1.5.0

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.
Files changed (254) hide show
  1. package/README.en.md +54 -20
  2. package/README.md +48 -21
  3. package/dist/agents/agentRouter.d.ts +40 -0
  4. package/dist/agents/agentRouter.js +95 -0
  5. package/dist/config.d.ts +1 -1
  6. package/dist/config.js +6 -2
  7. package/dist/controlCenter.js +212 -3
  8. package/dist/direct/directSessionStore.d.ts +2 -0
  9. package/dist/direct/directVerification.js +7 -0
  10. package/dist/doctor.js +32 -1
  11. package/dist/goal/acceptanceEngine.d.ts +62 -0
  12. package/dist/goal/acceptanceEngine.js +103 -0
  13. package/dist/goal/acceptanceTemplate.d.ts +10 -0
  14. package/dist/goal/acceptanceTemplate.js +104 -0
  15. package/dist/goal/goalGraph.d.ts +58 -0
  16. package/dist/goal/goalGraph.js +189 -0
  17. package/dist/goal/goalProgress.d.ts +81 -0
  18. package/dist/goal/goalProgress.js +216 -0
  19. package/dist/goal/goalStatus.d.ts +60 -0
  20. package/dist/goal/goalStatus.js +137 -0
  21. package/dist/goal/goalStore.d.ts +79 -0
  22. package/dist/goal/goalStore.js +211 -0
  23. package/dist/goal/handoffExport.d.ts +34 -0
  24. package/dist/goal/handoffExport.js +183 -0
  25. package/dist/goal/subgoalSync.d.ts +40 -0
  26. package/dist/goal/subgoalSync.js +72 -0
  27. package/dist/goal/worktreeManager.d.ts +96 -0
  28. package/dist/goal/worktreeManager.js +292 -0
  29. package/dist/logging.d.ts +44 -0
  30. package/dist/logging.js +68 -0
  31. package/dist/policy/projectPolicy.d.ts +55 -0
  32. package/dist/policy/projectPolicy.js +286 -0
  33. package/dist/release/releaseGate.d.ts +99 -0
  34. package/dist/release/releaseGate.js +475 -0
  35. package/dist/runner/postTaskCleanup.d.ts +13 -0
  36. package/dist/runner/postTaskCleanup.js +147 -0
  37. package/dist/runner/runTask.js +50 -10
  38. package/dist/security/discoveryTokenStore.d.ts +62 -0
  39. package/dist/security/discoveryTokenStore.js +100 -0
  40. package/dist/security/toolInvocationGuard.d.ts +35 -0
  41. package/dist/security/toolInvocationGuard.js +153 -0
  42. package/dist/smoke-test.js +18 -10
  43. package/dist/taskRuntime.d.ts +17 -0
  44. package/dist/test/unit/acceptance-engine.test.d.ts +1 -0
  45. package/dist/test/unit/acceptance-engine.test.js +228 -0
  46. package/dist/test/unit/agent-router.test.d.ts +1 -0
  47. package/dist/test/unit/agent-router.test.js +287 -0
  48. package/dist/test/unit/audit-checks.test.d.ts +1 -0
  49. package/dist/test/unit/audit-checks.test.js +350 -0
  50. package/dist/test/unit/diagnose-task.test.d.ts +1 -0
  51. package/dist/test/unit/diagnose-task.test.js +457 -0
  52. package/dist/test/unit/discovery-token-store.test.d.ts +1 -0
  53. package/dist/test/unit/discovery-token-store.test.js +139 -0
  54. package/dist/test/unit/evidence-pack.test.d.ts +1 -0
  55. package/dist/test/unit/evidence-pack.test.js +130 -0
  56. package/dist/test/unit/goal-graph.test.d.ts +1 -0
  57. package/dist/test/unit/goal-graph.test.js +298 -0
  58. package/dist/test/unit/goal-progress.test.d.ts +1 -0
  59. package/dist/test/unit/goal-progress.test.js +381 -0
  60. package/dist/test/unit/goal-status.test.d.ts +1 -0
  61. package/dist/test/unit/goal-status.test.js +215 -0
  62. package/dist/test/unit/goal-store.test.d.ts +1 -0
  63. package/dist/test/unit/goal-store.test.js +253 -0
  64. package/dist/test/unit/goal-subgoal-task.test.d.ts +1 -0
  65. package/dist/test/unit/goal-subgoal-task.test.js +55 -0
  66. package/dist/test/unit/goal-tools-registry.test.d.ts +1 -0
  67. package/dist/test/unit/goal-tools-registry.test.js +190 -0
  68. package/dist/test/unit/handoff-export.test.d.ts +1 -0
  69. package/dist/test/unit/handoff-export.test.js +263 -0
  70. package/dist/test/unit/invoke-discovered-tool.test.d.ts +1 -0
  71. package/dist/test/unit/invoke-discovered-tool.test.js +167 -0
  72. package/dist/test/unit/logging.test.js +127 -5
  73. package/dist/test/unit/post-task-cleanup.test.d.ts +1 -0
  74. package/dist/test/unit/post-task-cleanup.test.js +48 -0
  75. package/dist/test/unit/project-policy-release-mode.test.d.ts +1 -0
  76. package/dist/test/unit/project-policy-release-mode.test.js +125 -0
  77. package/dist/test/unit/reconcile-tasks.test.d.ts +1 -0
  78. package/dist/test/unit/reconcile-tasks.test.js +456 -0
  79. package/dist/test/unit/release-gate.test.d.ts +1 -0
  80. package/dist/test/unit/release-gate.test.js +242 -0
  81. package/dist/test/unit/run-task-loop.test.d.ts +1 -0
  82. package/dist/test/unit/run-task-loop.test.js +380 -0
  83. package/dist/test/unit/safe-views.test.d.ts +1 -0
  84. package/dist/test/unit/safe-views.test.js +171 -0
  85. package/dist/test/unit/schema-drift-check.test.d.ts +1 -0
  86. package/dist/test/unit/schema-drift-check.test.js +176 -0
  87. package/dist/test/unit/subgoal-sync.test.d.ts +1 -0
  88. package/dist/test/unit/subgoal-sync.test.js +183 -0
  89. package/dist/test/unit/tool-invocation-guard.test.d.ts +1 -0
  90. package/dist/test/unit/tool-invocation-guard.test.js +432 -0
  91. package/dist/test/unit/tool-usage-stats.test.d.ts +1 -0
  92. package/dist/test/unit/tool-usage-stats.test.js +300 -0
  93. package/dist/test/unit/toolSearch.test.d.ts +1 -0
  94. package/dist/test/unit/toolSearch.test.js +571 -0
  95. package/dist/test/unit/worktree-manager.test.d.ts +1 -0
  96. package/dist/test/unit/worktree-manager.test.js +176 -0
  97. package/dist/tools/auditTask.d.ts +103 -1
  98. package/dist/tools/auditTask.js +461 -8
  99. package/dist/tools/cancelTask.js +1 -1
  100. package/dist/tools/checkReleaseGate.d.ts +21 -0
  101. package/dist/tools/checkReleaseGate.js +22 -0
  102. package/dist/tools/createTask.d.ts +18 -2
  103. package/dist/tools/createTask.js +29 -1
  104. package/dist/tools/diagnoseTask.d.ts +45 -0
  105. package/dist/tools/diagnoseTask.js +347 -0
  106. package/dist/tools/discardWorktree.d.ts +24 -0
  107. package/dist/tools/discardWorktree.js +27 -0
  108. package/dist/tools/discoverTools.d.ts +11 -0
  109. package/dist/tools/discoverTools.js +39 -0
  110. package/dist/tools/evidencePack.d.ts +39 -0
  111. package/dist/tools/evidencePack.js +168 -0
  112. package/dist/tools/explainTool.d.ts +11 -0
  113. package/dist/tools/explainTool.js +21 -0
  114. package/dist/tools/getTaskSummary.js +2 -1
  115. package/dist/tools/goalSubgoalTask.d.ts +51 -0
  116. package/dist/tools/goalSubgoalTask.js +110 -0
  117. package/dist/tools/healthCheck.d.ts +1 -0
  118. package/dist/tools/healthCheck.js +5 -1
  119. package/dist/tools/invokeDiscoveredTool.d.ts +37 -0
  120. package/dist/tools/invokeDiscoveredTool.js +112 -0
  121. package/dist/tools/listTasks.d.ts +3 -1
  122. package/dist/tools/listTasks.js +14 -1
  123. package/dist/tools/mergeWorktree.d.ts +24 -0
  124. package/dist/tools/mergeWorktree.js +27 -0
  125. package/dist/tools/recommendAgentForTask.d.ts +19 -0
  126. package/dist/tools/recommendAgentForTask.js +56 -0
  127. package/dist/tools/reconcileTasks.d.ts +41 -0
  128. package/dist/tools/reconcileTasks.js +352 -0
  129. package/dist/tools/registry.js +926 -2
  130. package/dist/tools/releaseMode.d.ts +50 -0
  131. package/dist/tools/releaseMode.js +370 -0
  132. package/dist/tools/runDirectVerificationBundle.d.ts +26 -0
  133. package/dist/tools/runDirectVerificationBundle.js +64 -0
  134. package/dist/tools/runTaskLoop.d.ts +57 -0
  135. package/dist/tools/runTaskLoop.js +417 -0
  136. package/dist/tools/runVerification.d.ts +4 -0
  137. package/dist/tools/runVerification.js +4 -0
  138. package/dist/tools/safeStatus.d.ts +31 -1
  139. package/dist/tools/safeStatus.js +43 -1
  140. package/dist/tools/safeViews.d.ts +262 -0
  141. package/dist/tools/safeViews.js +252 -0
  142. package/dist/tools/schemaDriftCheck.d.ts +46 -0
  143. package/dist/tools/schemaDriftCheck.js +80 -0
  144. package/dist/tools/taskLineage.d.ts +91 -0
  145. package/dist/tools/taskLineage.js +175 -0
  146. package/dist/tools/toolCatalog.d.ts +4 -3
  147. package/dist/tools/toolCatalog.js +39 -11
  148. package/dist/tools/toolRegistry.d.ts +61 -0
  149. package/dist/tools/toolRegistry.js +834 -0
  150. package/dist/tools/toolSearch.d.ts +110 -0
  151. package/dist/tools/toolSearch.js +331 -0
  152. package/dist/tools/toolUsageStats.d.ts +41 -0
  153. package/dist/tools/toolUsageStats.js +116 -0
  154. package/dist/tools/waitForTask.js +1 -0
  155. package/dist/version.d.ts +2 -2
  156. package/dist/version.js +2 -2
  157. package/dist/watcherStatus.d.ts +29 -0
  158. package/dist/watcherStatus.js +92 -1
  159. package/docs/chatgpt-usage.md +31 -0
  160. package/docs/control-center/README.md +9 -0
  161. package/package.json +3 -2
  162. package/scripts/checks/control-center-smoke.js +87 -0
  163. package/scripts/checks/control-smoke.js +2 -2
  164. package/scripts/checks/doctor-smoke.js +1 -0
  165. package/scripts/checks/lifecycle-smoke.js +13 -9
  166. package/scripts/checks/mcp-manifest-check.js +65 -6
  167. package/scripts/checks/mcp-smoke.js +60 -9
  168. package/scripts/checks/watcher-supervisor-smoke.js +2 -2
  169. package/src/agents/agentRouter.ts +149 -0
  170. package/src/config.ts +9 -3
  171. package/src/controlCenter.ts +215 -4
  172. package/src/direct/directSessionStore.ts +2 -0
  173. package/src/direct/directVerification.ts +7 -0
  174. package/src/doctor.ts +36 -1
  175. package/src/goal/acceptanceEngine.ts +160 -0
  176. package/src/goal/acceptanceTemplate.ts +121 -0
  177. package/src/goal/goalGraph.ts +225 -0
  178. package/src/goal/goalProgress.ts +301 -0
  179. package/src/goal/goalStatus.ts +234 -0
  180. package/src/goal/goalStore.ts +306 -0
  181. package/src/goal/handoffExport.ts +211 -0
  182. package/src/goal/subgoalSync.ts +82 -0
  183. package/src/goal/worktreeManager.ts +404 -0
  184. package/src/logging.ts +91 -0
  185. package/src/policy/projectPolicy.ts +344 -0
  186. package/src/release/releaseGate.ts +567 -0
  187. package/src/runner/postTaskCleanup.ts +154 -0
  188. package/src/runner/runTask.ts +49 -10
  189. package/src/security/discoveryTokenStore.ts +157 -0
  190. package/src/security/toolInvocationGuard.ts +251 -0
  191. package/src/smoke-test.ts +15 -7
  192. package/src/taskRuntime.ts +17 -0
  193. package/src/test/unit/acceptance-engine.test.ts +261 -0
  194. package/src/test/unit/agent-router.test.ts +342 -0
  195. package/src/test/unit/audit-checks.test.ts +567 -0
  196. package/src/test/unit/diagnose-task.test.ts +544 -0
  197. package/src/test/unit/discovery-token-store.test.ts +181 -0
  198. package/src/test/unit/evidence-pack.test.ts +142 -0
  199. package/src/test/unit/goal-graph.test.ts +347 -0
  200. package/src/test/unit/goal-progress.test.ts +538 -0
  201. package/src/test/unit/goal-status.test.ts +270 -0
  202. package/src/test/unit/goal-store.test.ts +318 -0
  203. package/src/test/unit/goal-subgoal-task.test.ts +72 -0
  204. package/src/test/unit/goal-tools-registry.test.ts +243 -0
  205. package/src/test/unit/handoff-export.test.ts +295 -0
  206. package/src/test/unit/invoke-discovered-tool.test.ts +216 -0
  207. package/src/test/unit/logging.test.ts +177 -5
  208. package/src/test/unit/post-task-cleanup.test.ts +53 -0
  209. package/src/test/unit/project-policy-release-mode.test.ts +156 -0
  210. package/src/test/unit/reconcile-tasks.test.ts +551 -0
  211. package/src/test/unit/release-gate.test.ts +314 -0
  212. package/src/test/unit/run-task-loop.test.ts +425 -0
  213. package/src/test/unit/safe-views.test.ts +184 -0
  214. package/src/test/unit/schema-drift-check.test.ts +260 -0
  215. package/src/test/unit/subgoal-sync.test.ts +236 -0
  216. package/src/test/unit/tool-invocation-guard.test.ts +542 -0
  217. package/src/test/unit/tool-usage-stats.test.ts +384 -0
  218. package/src/test/unit/toolSearch.test.ts +756 -0
  219. package/src/test/unit/worktree-manager.test.ts +247 -0
  220. package/src/tools/auditTask.ts +831 -402
  221. package/src/tools/cancelTask.ts +1 -1
  222. package/src/tools/checkReleaseGate.ts +45 -0
  223. package/src/tools/createTask.ts +64 -6
  224. package/src/tools/diagnoseTask.ts +460 -0
  225. package/src/tools/discardWorktree.ts +42 -0
  226. package/src/tools/discoverTools.ts +51 -0
  227. package/src/tools/evidencePack.ts +205 -0
  228. package/src/tools/explainTool.ts +26 -0
  229. package/src/tools/getTaskSummary.ts +2 -1
  230. package/src/tools/goalSubgoalTask.ts +170 -0
  231. package/src/tools/healthCheck.ts +4 -1
  232. package/src/tools/invokeDiscoveredTool.ts +163 -0
  233. package/src/tools/listTasks.ts +16 -2
  234. package/src/tools/listWorkspace.ts +71 -71
  235. package/src/tools/mergeWorktree.ts +42 -0
  236. package/src/tools/recommendAgentForTask.ts +79 -0
  237. package/src/tools/reconcileTasks.ts +464 -0
  238. package/src/tools/registry.ts +1093 -73
  239. package/src/tools/releaseMode.ts +450 -0
  240. package/src/tools/runDirectVerificationBundle.ts +98 -0
  241. package/src/tools/runTaskLoop.ts +526 -0
  242. package/src/tools/runVerification.ts +8 -0
  243. package/src/tools/safeStatus.ts +73 -2
  244. package/src/tools/safeViews.ts +273 -0
  245. package/src/tools/schemaDriftCheck.ts +120 -0
  246. package/src/tools/taskLineage.ts +300 -0
  247. package/src/tools/toolCatalog.ts +40 -11
  248. package/src/tools/toolRegistry.ts +896 -0
  249. package/src/tools/toolSearch.ts +464 -0
  250. package/src/tools/toolUsageStats.ts +151 -0
  251. package/src/tools/waitForTask.ts +1 -0
  252. package/src/version.ts +2 -2
  253. package/src/watcherStatus.ts +97 -1
  254. package/ui/pages/dashboard.html +143 -2
@@ -0,0 +1,176 @@
1
+ import { describe, it, before, after } from "node:test";
2
+ import { strict as assert } from "node:assert";
3
+ import { mkdtempSync, rmSync, writeFileSync, readFileSync, existsSync, readdirSync, unlinkSync, } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { tmpdir } from "node:os";
6
+ import { execSync } from "node:child_process";
7
+ import { createWorktree, mergeWorktree, discardWorktree, readWorktreeStatus, getWorktreesDir, } from "../../goal/worktreeManager.js";
8
+ import { PatchWardenError } from "../../errors.js";
9
+ // ── Test fixtures ─────────────────────────────────────────────────
10
+ /**
11
+ * 在 dir 创建一个真实的 git 仓库:git init + 配置 user + 初始 commit。
12
+ * createWorktree 要求 workspaceRoot 是一个至少有一个 commit 的 git 仓库。
13
+ */
14
+ function initGitRepo(dir) {
15
+ execSync("git init", { cwd: dir, stdio: "ignore" });
16
+ execSync('git config user.email "test@patchwarden.local"', { cwd: dir, stdio: "ignore" });
17
+ execSync('git config user.name "PatchWarden Test"', { cwd: dir, stdio: "ignore" });
18
+ execSync('git config commit.gpgsign false', { cwd: dir, stdio: "ignore" });
19
+ writeFileSync(join(dir, "README.md"), "init\n", "utf-8");
20
+ execSync("git add README.md", { cwd: dir, stdio: "ignore" });
21
+ execSync("git commit -m init", { cwd: dir, stdio: "ignore" });
22
+ }
23
+ function gitBranchList(cwd) {
24
+ return execSync("git branch --list", { cwd, stdio: ["pipe", "pipe", "ignore"] }).toString("utf-8");
25
+ }
26
+ function assertPatchWardenError(fn, reason) {
27
+ try {
28
+ fn();
29
+ assert.fail(`expected PatchWardenError("${reason}") to be thrown`);
30
+ }
31
+ catch (err) {
32
+ assert.ok(err instanceof PatchWardenError, `expected PatchWardenError, got: ${err}`);
33
+ assert.equal(err.reason, reason);
34
+ }
35
+ }
36
+ // ── Tests ─────────────────────────────────────────────────────────
37
+ describe("worktreeManager", () => {
38
+ let workspaceRoot;
39
+ before(() => {
40
+ workspaceRoot = mkdtempSync(join(tmpdir(), "pw-wt-"));
41
+ initGitRepo(workspaceRoot);
42
+ });
43
+ after(() => {
44
+ rmSync(workspaceRoot, { recursive: true, force: true });
45
+ });
46
+ // ── 1. createWorktree 成功 ──
47
+ it("createWorktree creates an active worktree with valid status.json", () => {
48
+ const result = createWorktree("goal_20260101_demo", "subgoal-001", workspaceRoot);
49
+ assert.ok(result.worktreeId.startsWith("wt_"), `worktreeId should start with "wt_", got: ${result.worktreeId}`);
50
+ assert.ok(result.branch.startsWith("pw-goal_20260101_demo-subgoal-001"), `branch should be pw-<goal>-<subgoal>, got: ${result.branch}`);
51
+ assert.ok(existsSync(result.worktreePath), "worktreePath should exist");
52
+ const statusFile = join(result.worktreePath, "worktree_status.json");
53
+ assert.ok(existsSync(statusFile), "worktree_status.json should exist");
54
+ // 原子写:不应残留 .tmp 文件
55
+ assert.ok(!existsSync(statusFile + ".tmp"), "status.json.tmp should not remain after atomic write");
56
+ // JSON 合法且字段正确
57
+ const status = JSON.parse(readFileSync(statusFile, "utf-8"));
58
+ assert.equal(status.worktree_id, result.worktreeId);
59
+ assert.equal(status.goal_id, "goal_20260101_demo");
60
+ assert.equal(status.subgoal_id, "subgoal-001");
61
+ assert.equal(status.status, "active");
62
+ assert.equal(status.branch, result.branch);
63
+ assert.equal(status.path, result.worktreePath);
64
+ assert.ok(typeof status.created_at === "string" && status.created_at.length > 0);
65
+ // worktree 内应检出主仓库的文件(README.md)
66
+ assert.ok(existsSync(join(result.worktreePath, "README.md")), "worktree should check out README.md");
67
+ // branch 应在 git 中存在
68
+ assert.ok(gitBranchList(workspaceRoot).includes(result.branch), "branch should exist in git");
69
+ // readWorktreeStatus 应返回一致的状态
70
+ const readBack = readWorktreeStatus(result.worktreeId, workspaceRoot);
71
+ assert.ok(readBack !== null);
72
+ assert.equal(readBack.status, "active");
73
+ });
74
+ // ── 2. createWorktree git 命令失败清理 ──
75
+ it("createWorktree cleans up and throws when git fails (non-git workspaceRoot)", () => {
76
+ const nonGitDir = mkdtempSync(join(tmpdir(), "pw-wt-nogit-"));
77
+ try {
78
+ // 非 git 目录 → git worktree add 失败
79
+ assertPatchWardenError(() => createWorktree("goal_x", "subgoal-001", nonGitDir), "worktree_create_failed");
80
+ // 不应残留任何 worktree 目录
81
+ const worktreesDir = getWorktreesDir(nonGitDir);
82
+ if (existsSync(worktreesDir)) {
83
+ // 若 _workspacetrees 被创建,应为空(无半成品 worktree)
84
+ const entries = readdirSync(worktreesDir);
85
+ assert.equal(entries.length, 0, "no leftover worktree directories should remain after failure");
86
+ }
87
+ }
88
+ finally {
89
+ rmSync(nonGitDir, { recursive: true, force: true });
90
+ }
91
+ });
92
+ // ── 3. mergeWorktree 成功 ──
93
+ it("mergeWorktree merges the branch back and marks status merged", () => {
94
+ const created = createWorktree("goal_merge", "subgoal-001", workspaceRoot);
95
+ // 在 worktree 中产生一个新 commit,使 merge 有实际内容
96
+ writeFileSync(join(created.worktreePath, "feature.txt"), "merged feature\n", "utf-8");
97
+ execSync("git add feature.txt", { cwd: created.worktreePath, stdio: "ignore" });
98
+ execSync("git commit -m feature", { cwd: created.worktreePath, stdio: "ignore" });
99
+ const result = mergeWorktree(created.worktreeId, workspaceRoot);
100
+ assert.equal(result.status, "merged");
101
+ // 主工作区应出现 feature.txt(fast-forward 合并)
102
+ assert.ok(existsSync(join(workspaceRoot, "feature.txt")), "feature.txt should appear in main workspace after merge");
103
+ // status 应更新为 merged,merged_at 非空
104
+ const status = readWorktreeStatus(created.worktreeId, workspaceRoot);
105
+ assert.ok(status !== null);
106
+ assert.equal(status.status, "merged");
107
+ assert.ok(typeof status.merged_at === "string" && status.merged_at.length > 0, "merged_at should be set");
108
+ });
109
+ // ── 4. discardWorktree 成功 ──
110
+ it("discardWorktree removes worktree, deletes branch, and archives status", () => {
111
+ const created = createWorktree("goal_discard", "subgoal-001", workspaceRoot);
112
+ const branch = created.branch;
113
+ const worktreePath = created.worktreePath;
114
+ const result = discardWorktree(created.worktreeId, workspaceRoot);
115
+ assert.equal(result.status, "discarded");
116
+ // worktree 目录应被移除
117
+ assert.ok(!existsSync(worktreePath), "worktree directory should be removed after discard");
118
+ // 临时 branch 应被删除
119
+ assert.ok(!gitBranchList(workspaceRoot).includes(branch), "temporary branch should be deleted after discard");
120
+ // 归档文件应存在且 status="discarded"
121
+ const archiveFile = join(workspaceRoot, ".patchwarden", "worktree-archive", `${created.worktreeId}.json`);
122
+ assert.ok(existsSync(archiveFile), "archived status file should exist");
123
+ const archived = JSON.parse(readFileSync(archiveFile, "utf-8"));
124
+ assert.equal(archived.status, "discarded");
125
+ assert.ok(typeof archived.discarded_at === "string" && archived.discarded_at.length > 0, "discarded_at should be set");
126
+ assert.equal(archived.worktree_id, created.worktreeId);
127
+ });
128
+ // ── 5. 路径逃逸拦截 ──
129
+ it("blocks path escape via worktreeId containing ..", () => {
130
+ // worktreeId 在 read/merge/discard 中由调用方提供,构造逃逸路径
131
+ assertPatchWardenError(() => readWorktreeStatus("../../../outside", workspaceRoot), "workspace_path_escape");
132
+ assertPatchWardenError(() => mergeWorktree("../../../outside", workspaceRoot), "workspace_path_escape");
133
+ assertPatchWardenError(() => discardWorktree("../../../outside", workspaceRoot), "workspace_path_escape");
134
+ });
135
+ // ── 6. 敏感路径拦截 ──
136
+ it("blocks sensitive worktreeId via guardSensitivePath", () => {
137
+ // "credentials" 命中 sensitiveGuard 的敏感模式
138
+ assertPatchWardenError(() => readWorktreeStatus("credentials", workspaceRoot), "sensitive_path_blocked");
139
+ assertPatchWardenError(() => mergeWorktree("credentials", workspaceRoot), "sensitive_path_blocked");
140
+ assertPatchWardenError(() => discardWorktree("credentials", workspaceRoot), "sensitive_path_blocked");
141
+ });
142
+ // ── 7. readWorktreeStatus 返回 null 当 status 不存在 ──
143
+ it("readWorktreeStatus returns null when status file does not exist", () => {
144
+ // 创建一个 worktree 目录但不含 status 文件
145
+ const created = createWorktree("goal_missing", "subgoal-001", workspaceRoot);
146
+ // 手动删除 status 文件模拟缺失
147
+ unlinkSync(join(created.worktreePath, "worktree_status.json"));
148
+ assert.equal(readWorktreeStatus(created.worktreeId, workspaceRoot), null);
149
+ // 清理:删除该 worktree(避免污染后续测试)
150
+ try {
151
+ execSync(`git worktree remove --force "${created.worktreePath}"`, { cwd: workspaceRoot, stdio: "ignore" });
152
+ }
153
+ catch { /* ignore */ }
154
+ try {
155
+ execSync(`git branch -D ${created.branch}`, { cwd: workspaceRoot, stdio: "ignore" });
156
+ }
157
+ catch { /* ignore */ }
158
+ });
159
+ // ── 8. 非法状态转换:merge/discard 已 merged 的 worktree 抛错 ──
160
+ it("rejects merge/discard on non-active worktree", () => {
161
+ // 复用 #3 已 merged 的 worktree(goal_merge/subgoal-001)
162
+ // 先找到它的 worktreeId:扫描 _workspacetrees 目录
163
+ const worktreesDir = getWorktreesDir(workspaceRoot);
164
+ let mergedId = null;
165
+ for (const entry of readdirSync(worktreesDir)) {
166
+ const s = readWorktreeStatus(entry, workspaceRoot);
167
+ if (s && s.status === "merged" && s.goal_id === "goal_merge") {
168
+ mergedId = entry;
169
+ break;
170
+ }
171
+ }
172
+ assert.ok(mergedId !== null, "should find the merged worktree from the merge test");
173
+ assertPatchWardenError(() => mergeWorktree(mergedId, workspaceRoot), "invalid_worktree_state");
174
+ assertPatchWardenError(() => discardWorktree(mergedId, workspaceRoot), "invalid_worktree_state");
175
+ });
176
+ });
@@ -10,6 +10,16 @@ interface AuditRisk {
10
10
  export interface AuditTaskOutput {
11
11
  task_id: string;
12
12
  verdict: "pass" | "warn" | "fail";
13
+ acceptance: {
14
+ verdict: string;
15
+ status: string;
16
+ reason: string;
17
+ reasons: string[];
18
+ required_evidence: string[];
19
+ next_suggested_task: string;
20
+ fail_checks: AuditCheck[];
21
+ warn_checks: AuditCheck[];
22
+ };
13
23
  summary: string;
14
24
  checks: AuditCheck[];
15
25
  risks: AuditRisk[];
@@ -22,5 +32,97 @@ export interface AuditTaskOutput {
22
32
  manual_verification_items: string[];
23
33
  recommended_next_actions: string[];
24
34
  }
25
- export declare function auditTask(taskId: string): AuditTaskOutput;
35
+ /**
36
+ * Check 1: forbidden_scope_violation (fail level).
37
+ * Returns null when no forbidden patterns are configured (skip).
38
+ * Returns pass when changed files do not match any forbidden pattern.
39
+ * Returns fail when any changed file path (or old_path) matches a forbidden glob.
40
+ */
41
+ export declare function checkForbiddenScope(changedFiles: any, forbidden: any): {
42
+ name: string;
43
+ result: string;
44
+ detail: string;
45
+ } | null;
46
+ /**
47
+ * Check 2: done_evidence_missing (warn level).
48
+ * Returns null when no done_evidence list is configured (skip).
49
+ * Returns pass when all listed files exist in the task directory.
50
+ * Returns warn when any listed file is missing.
51
+ */
52
+ export declare function checkDoneEvidenceMissing(taskDir: any, doneEvidence: any): {
53
+ name: string;
54
+ result: string;
55
+ detail: string;
56
+ } | null;
57
+ /**
58
+ * Check 3: readme_changelog_sync (warn level).
59
+ * Returns null when no code files (by extension) were changed (skip).
60
+ * Returns pass when README.md or CHANGELOG.md (basename, case-insensitive) is also changed.
61
+ * Returns warn when code changed but neither documentation file was updated.
62
+ */
63
+ export declare function checkReadmeChangelogSync(changedFiles: any): {
64
+ name: string;
65
+ result: string;
66
+ detail: string;
67
+ } | null;
68
+ /**
69
+ * Check 4: package_manifest_consistency (warn level).
70
+ * Returns null when package.json is not among changed files (skip).
71
+ * Returns pass when package.json parses as JSON and has non-empty name and version strings.
72
+ * Returns warn otherwise.
73
+ */
74
+ export declare function checkPackageManifestConsistency(changedFiles: any, repoPathSafe: any): {
75
+ name: string;
76
+ result: string;
77
+ detail: string;
78
+ } | null;
79
+ /**
80
+ * Check 5: sensitive_path_access (fail level).
81
+ * Returns null when there are no changed files (skip).
82
+ * Returns pass when no changed file path (or old_path) is sensitive.
83
+ * Returns fail when any changed file path is flagged by isSensitivePath.
84
+ */
85
+ export declare function checkSensitivePathAccess(changedFiles: any): {
86
+ name: string;
87
+ result: string;
88
+ detail: string;
89
+ } | null;
90
+ export declare function checkUnrecordedCommandExecution(testLogContent: string | null, resultMdContent: string | null, verifyCommands: string[], testCommand: string | null, repoPath?: string | null): {
91
+ name: string;
92
+ result: string;
93
+ detail: string;
94
+ } | null;
95
+ export declare function auditTask(taskId: any): {
96
+ task_id: any;
97
+ verdict: string;
98
+ acceptance: {
99
+ verdict: import("../goal/acceptanceEngine.js").AcceptanceVerdict;
100
+ status: string;
101
+ reason: string;
102
+ reasons: string[];
103
+ required_evidence: string[];
104
+ next_suggested_task: string;
105
+ fail_checks: import("../goal/acceptanceEngine.js").AcceptanceCheck[];
106
+ warn_checks: import("../goal/acceptanceEngine.js").AcceptanceCheck[];
107
+ };
108
+ summary: string;
109
+ checks: {
110
+ name: string;
111
+ result: string;
112
+ detail: string;
113
+ }[];
114
+ risks: {
115
+ severity: string;
116
+ description: string;
117
+ }[];
118
+ confirmed_failures: {
119
+ name: string;
120
+ result: string;
121
+ detail: string;
122
+ }[];
123
+ possible_false_positives: any[];
124
+ manual_verification_required: boolean;
125
+ manual_verification_items: any[];
126
+ recommended_next_actions: string[];
127
+ };
26
128
  export {};