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
@@ -191,9 +191,105 @@ export function derivePendingReason(
191
191
  if (watcher.status === "unreadable") return "queued_but_watcher_unreadable";
192
192
  return "queued_waiting_for_watcher";
193
193
  }
194
- if (task.status !== "running") return null;
194
+ if (task.status !== "running" && task.status !== "collecting_artifacts") return null;
195
195
  if (task.phase === "executing_agent") return "agent_running";
196
196
  if (task.phase === "running_tests") return "verification_running";
197
197
  if (task.phase === "collecting_artifacts") return "collecting_artifacts";
198
198
  return "preparing";
199
199
  }
200
+
201
+ // ── v0.7.0: Watcher ownership helpers ─────────────────────────────
202
+
203
+ /**
204
+ * Read the current watcher's instance_id from the heartbeat file.
205
+ * Returns null when the heartbeat is missing or unreadable.
206
+ *
207
+ * This is the source of truth for "which watcher is currently alive".
208
+ * Tasks whose runtime.watcher_instance_id differs from this value are
209
+ * considered orphaned by the diagnosis layer.
210
+ */
211
+ export function readWatcherInstanceId(
212
+ config: PatchWardenConfig = getConfig()
213
+ ): string | null {
214
+ const heartbeatPath = getWatcherHeartbeatPath(config);
215
+ if (!existsSync(heartbeatPath)) return null;
216
+ try {
217
+ const raw = readFileSync(heartbeatPath, "utf-8");
218
+ const data = JSON.parse(raw.charCodeAt(0) === 0xfeff ? raw.slice(1) : raw);
219
+ return typeof data.instance_id === "string" ? data.instance_id : null;
220
+ } catch {
221
+ return null;
222
+ }
223
+ }
224
+
225
+ /**
226
+ * v0.7.0: Determine whether the current watcher still owns a task.
227
+ *
228
+ * Ownership is established by comparing the watcher_instance_id recorded in
229
+ * the task's runtime.json with the instance_id of the currently alive
230
+ * watcher. A task is considered "owned" only when:
231
+ * - the current watcher heartbeat is healthy, AND
232
+ * - the task runtime recorded a watcher_instance_id, AND
233
+ * - both instance IDs match.
234
+ *
235
+ * If the task has no recorded watcher_instance_id (legacy tasks created
236
+ * before v0.7.0), ownership cannot be confirmed and this returns false.
237
+ * The diagnosis layer treats "no owner" as a stale signal, not as active.
238
+ */
239
+ export function isWatcherOwningTask(
240
+ taskDir: string,
241
+ config: PatchWardenConfig = getConfig()
242
+ ): { owned: boolean; reason: "owned" | "no_runtime_record" | "watcher_missing" | "instance_mismatch" | "watcher_unhealthy"; task_watcher_instance_id: string | null; current_watcher_instance_id: string | null } {
243
+ const runtimePath = join(taskDir, "runtime.json");
244
+ let taskWatcherInstanceId: string | null = null;
245
+ if (existsSync(runtimePath)) {
246
+ try {
247
+ const runtime = JSON.parse(readFileSync(runtimePath, "utf-8"));
248
+ if (typeof runtime.watcher_instance_id === "string") {
249
+ taskWatcherInstanceId = runtime.watcher_instance_id;
250
+ }
251
+ } catch {
252
+ // corrupted runtime — treat as no record
253
+ }
254
+ }
255
+ if (!taskWatcherInstanceId) {
256
+ return {
257
+ owned: false,
258
+ reason: "no_runtime_record",
259
+ task_watcher_instance_id: null,
260
+ current_watcher_instance_id: null,
261
+ };
262
+ }
263
+ const watcher = readWatcherStatus(config);
264
+ if (watcher.status !== "healthy") {
265
+ return {
266
+ owned: false,
267
+ reason: watcher.status === "missing" ? "watcher_missing" : "watcher_unhealthy",
268
+ task_watcher_instance_id: taskWatcherInstanceId,
269
+ current_watcher_instance_id: watcher.instance_id,
270
+ };
271
+ }
272
+ const currentInstanceId = watcher.instance_id;
273
+ if (!currentInstanceId) {
274
+ return {
275
+ owned: false,
276
+ reason: "watcher_missing",
277
+ task_watcher_instance_id: taskWatcherInstanceId,
278
+ current_watcher_instance_id: null,
279
+ };
280
+ }
281
+ if (currentInstanceId !== taskWatcherInstanceId) {
282
+ return {
283
+ owned: false,
284
+ reason: "instance_mismatch",
285
+ task_watcher_instance_id: taskWatcherInstanceId,
286
+ current_watcher_instance_id: currentInstanceId,
287
+ };
288
+ }
289
+ return {
290
+ owned: true,
291
+ reason: "owned",
292
+ task_watcher_instance_id: taskWatcherInstanceId,
293
+ current_watcher_instance_id: currentInstanceId,
294
+ };
295
+ }
@@ -304,6 +304,38 @@ html.dark {
304
304
  <div id="suggestions-body" class="px-4 py-2"></div>
305
305
  </div>
306
306
 
307
+ <!-- ===== V1.5 RELEASE / POLICY / LINEAGE / EVIDENCE OVERVIEW ===== -->
308
+ <div class="grid grid-cols-1 lg:grid-cols-4 gap-4 mb-6">
309
+ <div class="rounded-lg" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
310
+ <div class="flex items-center justify-between px-4 py-3" style="border-bottom: 1px solid var(--pw-border);">
311
+ <h2 class="text-sm font-semibold truncate" style="color: var(--pw-text-primary);">Lineage</h2>
312
+ <span id="lineage-count" class="text-xs whitespace-nowrap" style="color: var(--pw-text-tertiary);">...</span>
313
+ </div>
314
+ <div id="lineage-overview-body" class="px-4 py-3 text-sm" style="color: var(--pw-text-tertiary);">Loading</div>
315
+ </div>
316
+ <div class="rounded-lg" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
317
+ <div class="flex items-center justify-between px-4 py-3" style="border-bottom: 1px solid var(--pw-border);">
318
+ <h2 class="text-sm font-semibold truncate" style="color: var(--pw-text-primary);">Project Policy</h2>
319
+ <span id="policy-status" class="text-xs whitespace-nowrap" style="color: var(--pw-text-tertiary);">...</span>
320
+ </div>
321
+ <div id="policy-overview-body" class="px-4 py-3 text-sm" style="color: var(--pw-text-tertiary);">Loading</div>
322
+ </div>
323
+ <div class="rounded-lg" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
324
+ <div class="flex items-center justify-between px-4 py-3" style="border-bottom: 1px solid var(--pw-border);">
325
+ <h2 class="text-sm font-semibold truncate" style="color: var(--pw-text-primary);">Release</h2>
326
+ <span id="release-status" class="text-xs whitespace-nowrap" style="color: var(--pw-text-tertiary);">...</span>
327
+ </div>
328
+ <div id="release-overview-body" class="px-4 py-3 text-sm" style="color: var(--pw-text-tertiary);">Loading</div>
329
+ </div>
330
+ <div id="evidence-pack-card" class="rounded-lg" style="background-color: var(--pw-bg-surface); border: 1px solid var(--pw-border);">
331
+ <div class="flex items-center justify-between px-4 py-3" style="border-bottom: 1px solid var(--pw-border);">
332
+ <h2 class="text-sm font-semibold truncate" style="color: var(--pw-text-primary);">Evidence Pack</h2>
333
+ <span id="evidence-pack-status" class="text-xs whitespace-nowrap" style="color: var(--pw-text-tertiary);">...</span>
334
+ </div>
335
+ <div id="evidence-pack-overview-body" class="px-4 py-3 text-sm" style="color: var(--pw-text-tertiary);">Loading</div>
336
+ </div>
337
+ </div>
338
+
307
339
  <!-- ===== TWO-COLUMN LAYOUT ===== -->
308
340
  <div class="grid grid-cols-1 lg:grid-cols-5 gap-4 mb-6">
309
341
 
@@ -1027,6 +1059,115 @@ html.dark {
1027
1059
  + '</details>';
1028
1060
  }
1029
1061
 
1062
+ // ---------- V1.5 panels ----------
1063
+ async function refreshLineages() {
1064
+ const body = $('lineage-overview-body');
1065
+ const count = $('lineage-count');
1066
+ if (!body) return;
1067
+ try {
1068
+ const data = await fetchJson('/api/lineages');
1069
+ const lineages = Array.isArray(data.lineages) ? data.lineages : [];
1070
+ if (count) count.textContent = String(data.total || lineages.length);
1071
+ if (!lineages.length) {
1072
+ body.innerHTML = '<div class="text-xs" style="color: var(--pw-text-tertiary);">No loop lineage yet</div>';
1073
+ return;
1074
+ }
1075
+ const top = lineages[0];
1076
+ const directSessions = top.tasks && Array.isArray(top.tasks.direct_sessions) ? top.tasks.direct_sessions : [];
1077
+ const direct = directSessions.length ? directSessions[0] : null;
1078
+ const worktree = top.worktree || {};
1079
+ const directStatus = direct
1080
+ ? ((direct.status || 'unknown') + ' / audit ' + (direct.audit_decision || 'not_run'))
1081
+ : 'not run';
1082
+ body.innerHTML =
1083
+ '<div class="flex items-center justify-between gap-2 mb-2">'
1084
+ + '<span class="text-xs truncate" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);" title="' + escapeHtml(top.lineage_id || '') + '">' + escapeHtml(shortId(top.lineage_id)) + '</span>'
1085
+ + badgeHtml(top.stop_reason || 'unknown', statusKind(top.final_status))
1086
+ + '</div>'
1087
+ + '<div class="text-xs truncate mb-1" style="color: var(--pw-text-secondary);" title="' + escapeHtml(top.goal || '') + '">' + escapeHtml(top.goal || 'No goal') + '</div>'
1088
+ + '<div class="text-xs truncate mb-1" style="color: var(--pw-text-tertiary);">Isolation: ' + escapeHtml(worktree.isolation_mode || 'current_repo') + '</div>'
1089
+ + '<div class="text-xs truncate mb-1" style="color: var(--pw-text-tertiary);" title="' + escapeHtml(direct && direct.session_id ? direct.session_id : '') + '">Direct: ' + escapeHtml(directStatus) + '</div>'
1090
+ + '<div class="text-xs" style="color: var(--pw-text-tertiary);">Next: ' + escapeHtml(top.next_action || 'none') + '</div>';
1091
+ } catch (err) {
1092
+ if (count) count.textContent = 'error';
1093
+ body.innerHTML = '<div class="text-xs" style="color: var(--pw-state-error);">' + escapeHtml(err.message) + '</div>';
1094
+ }
1095
+ }
1096
+
1097
+ async function refreshProjectPolicy() {
1098
+ const body = $('policy-overview-body');
1099
+ const status = $('policy-status');
1100
+ if (!body) return;
1101
+ try {
1102
+ const data = await fetchJson('/api/project-policy?repo_path=.');
1103
+ const valid = data && data.valid !== false;
1104
+ if (status) status.textContent = valid ? 'valid' : 'issues';
1105
+ const issueCount = Array.isArray(data.issues) ? data.issues.length : 0;
1106
+ const policy = data.effective_policy || {};
1107
+ const release = (policy.release_mode || {});
1108
+ body.innerHTML =
1109
+ '<div class="flex items-center justify-between gap-2 mb-2">'
1110
+ + '<span class="text-xs" style="color: var(--pw-text-secondary);">policy file</span>'
1111
+ + badgeHtml(data.exists ? 'present' : 'defaults', data.exists ? 'success' : 'warning')
1112
+ + '</div>'
1113
+ + '<div class="text-xs mb-1" style="color: var(--pw-text-tertiary);">issues: ' + issueCount + '</div>'
1114
+ + '<div class="text-xs truncate" style="color: var(--pw-text-tertiary);" title="' + escapeHtml(release.version_source || '') + '">version: ' + escapeHtml(release.version_source || 'package.json') + '</div>';
1115
+ } catch (err) {
1116
+ if (status) status.textContent = 'error';
1117
+ body.innerHTML = '<div class="text-xs" style="color: var(--pw-state-error);">' + escapeHtml(err.message) + '</div>';
1118
+ }
1119
+ }
1120
+
1121
+ async function refreshReleaseStatus() {
1122
+ const body = $('release-overview-body');
1123
+ const status = $('release-status');
1124
+ if (!body) return;
1125
+ try {
1126
+ const data = await fetchJson('/api/release/status?repo_path=.');
1127
+ const ready = data && data.policy_valid && data.release_readiness && data.release_readiness.version_consistent !== false;
1128
+ if (status) status.textContent = ready ? 'ready' : 'blocked';
1129
+ const readiness = data.release_readiness || {};
1130
+ const commands = Array.isArray(readiness.required_commands) ? readiness.required_commands : [];
1131
+ const blocked = commands.filter(function (entry) { return !entry.allowed; }).length;
1132
+ body.innerHTML =
1133
+ '<div class="flex items-center justify-between gap-2 mb-2">'
1134
+ + '<span class="text-xs" style="color: var(--pw-text-secondary);">version</span>'
1135
+ + '<span class="text-xs" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);">' + escapeHtml(readiness.version || 'unknown') + '</span>'
1136
+ + '</div>'
1137
+ + '<div class="text-xs mb-1" style="color: var(--pw-text-tertiary);">commands blocked: ' + blocked + ' / ' + commands.length + '</div>'
1138
+ + '<div class="text-xs" style="color: var(--pw-text-tertiary);">' + escapeHtml(data.next_action || '') + '</div>';
1139
+ } catch (err) {
1140
+ if (status) status.textContent = 'error';
1141
+ body.innerHTML = '<div class="text-xs" style="color: var(--pw-state-error);">' + escapeHtml(err.message) + '</div>';
1142
+ }
1143
+ }
1144
+
1145
+ async function refreshEvidencePacks() {
1146
+ const body = $('evidence-pack-overview-body');
1147
+ const status = $('evidence-pack-status');
1148
+ if (!body) return;
1149
+ try {
1150
+ const data = await fetchJson('/api/evidence-packs');
1151
+ const packs = Array.isArray(data.evidence_packs) ? data.evidence_packs : [];
1152
+ if (status) status.textContent = String(data.total || packs.length);
1153
+ if (!packs.length) {
1154
+ body.innerHTML = '<div class="text-xs" style="color: var(--pw-text-tertiary);">No exported evidence pack yet</div>';
1155
+ return;
1156
+ }
1157
+ const top = packs[0];
1158
+ body.innerHTML =
1159
+ '<div class="flex items-center justify-between gap-2 mb-2">'
1160
+ + '<span class="text-xs truncate" style="color: var(--pw-text-primary); font-family: var(--pw-font-mono);" title="' + escapeHtml(top.lineage_id || '') + '">' + escapeHtml(shortId(top.lineage_id)) + '</span>'
1161
+ + badgeHtml(top.lineage && top.lineage.stop_reason ? top.lineage.stop_reason : 'unknown', statusKind(top.lineage && top.lineage.final_status))
1162
+ + '</div>'
1163
+ + '<div class="text-xs mb-1" style="color: var(--pw-text-tertiary);">policy issues: ' + escapeHtml(String(top.policy ? top.policy.issue_count : 0)) + '</div>'
1164
+ + '<div class="text-xs truncate" style="color: var(--pw-text-tertiary);" title="' + escapeHtml(top.next_action || '') + '">Next: ' + escapeHtml(top.next_action || 'review') + '</div>';
1165
+ } catch (err) {
1166
+ if (status) status.textContent = 'error';
1167
+ body.innerHTML = '<div class="text-xs" style="color: var(--pw-state-error);">' + escapeHtml(err.message) + '</div>';
1168
+ }
1169
+ }
1170
+
1030
1171
  // ---------- Refresh orchestrators ----------
1031
1172
  async function refreshStatus() {
1032
1173
  try {
@@ -1067,14 +1208,14 @@ html.dark {
1067
1208
  }
1068
1209
 
1069
1210
  async function refreshAll() {
1070
- await Promise.all([refreshStatus(), refreshTasks(), refreshLogs(), refreshEvents()]);
1211
+ await Promise.all([refreshStatus(), refreshTasks(), refreshLogs(), refreshEvents(), refreshLineages(), refreshProjectPolicy(), refreshReleaseStatus(), refreshEvidencePacks()]);
1071
1212
  }
1072
1213
 
1073
1214
  // ---------- Actions ----------
1074
1215
  async function handleAction(action, btnEl) {
1075
1216
  try {
1076
1217
  if (action === 'refresh') {
1077
- await Promise.all([refreshStatus(), refreshTasks()]);
1218
+ await Promise.all([refreshStatus(), refreshTasks(), refreshLineages(), refreshProjectPolicy(), refreshReleaseStatus(), refreshEvidencePacks()]);
1078
1219
  return;
1079
1220
  }
1080
1221