coding-agent-harness 1.0.7 → 1.1.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 (238) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/CONTRIBUTING.md +9 -5
  3. package/README.md +12 -2
  4. package/README.zh-CN.md +10 -2
  5. package/SKILL.md +14 -3
  6. package/dist/build-dist.mjs +32 -6
  7. package/dist/check-dist-observation.mjs +73 -28
  8. package/dist/check-harness.mjs +0 -1
  9. package/dist/check-import-graph.mjs +44 -27
  10. package/dist/check-lite-forbidden-surfaces.mjs +121 -0
  11. package/dist/check-no-ts-nocheck.mjs +88 -0
  12. package/dist/check-runtime-emit.mjs +10 -3
  13. package/dist/check-type-boundaries.mjs +67 -8
  14. package/dist/commands/dashboard-command.mjs +52 -14
  15. package/dist/commands/migration-command.mjs +18 -8
  16. package/dist/commands/module-command.mjs +142 -0
  17. package/dist/commands/preset-command.mjs +65 -4
  18. package/dist/commands/registry.mjs +483 -0
  19. package/dist/commands/task-command.mjs +111 -53
  20. package/dist/harness.mjs +6 -303
  21. package/dist/lib/capability-registry.mjs +229 -53
  22. package/dist/lib/check-module-parallel.mjs +1 -6
  23. package/dist/lib/check-profiles.mjs +39 -46
  24. package/dist/lib/check-task-contracts.mjs +6 -4
  25. package/dist/lib/command-registry.mjs +248 -0
  26. package/dist/lib/core-shared.mjs +78 -3
  27. package/dist/lib/dashboard-data.mjs +203 -22
  28. package/dist/lib/dashboard-workbench.mjs +245 -21
  29. package/dist/lib/dashboard-writer.mjs +4 -1
  30. package/dist/lib/git-status-summary.mjs +0 -1
  31. package/dist/lib/governance-index-generator.mjs +7 -5
  32. package/dist/lib/governance-sync.mjs +46 -121
  33. package/dist/lib/governance-table-boundary.mjs +1 -14
  34. package/dist/lib/harness-core.mjs +5 -1
  35. package/dist/lib/harness-paths.mjs +115 -1
  36. package/dist/lib/impact-classifier.mjs +420 -0
  37. package/dist/lib/lesson-maintenance.mjs +1 -2
  38. package/dist/lib/markdown-utils.mjs +50 -1
  39. package/dist/lib/migration-planner.mjs +31 -16
  40. package/dist/lib/migration-support.mjs +5 -4
  41. package/dist/lib/module-registry.mjs +296 -0
  42. package/dist/lib/preset-audit-contracts.mjs +24 -1
  43. package/dist/lib/preset-engine.mjs +68 -29
  44. package/dist/lib/preset-registry.mjs +374 -72
  45. package/dist/lib/preset-runner.mjs +560 -0
  46. package/dist/lib/review-confirm-git-gate.mjs +73 -19
  47. package/dist/lib/status-builder.mjs +23 -8
  48. package/dist/lib/structure-migration.mjs +6 -4
  49. package/dist/lib/subagent-authorization-audit.mjs +8 -2
  50. package/dist/lib/task-archive-eligibility.mjs +65 -0
  51. package/dist/lib/task-audit-metadata.mjs +25 -11
  52. package/dist/lib/task-audit-migration.mjs +21 -14
  53. package/dist/lib/task-discovery-contract.mjs +32 -0
  54. package/dist/lib/task-index.mjs +4 -2
  55. package/dist/lib/task-lesson-candidates.mjs +1 -2
  56. package/dist/lib/task-lesson-sedimentation.mjs +310 -9
  57. package/dist/lib/task-lifecycle/create-task-helpers.mjs +6 -3
  58. package/dist/lib/task-lifecycle/phase-sync.mjs +0 -1
  59. package/dist/lib/task-lifecycle/preset-interop.mjs +16 -0
  60. package/dist/lib/task-lifecycle/review-confirm.mjs +34 -2
  61. package/dist/lib/task-lifecycle/review-gates.mjs +12 -5
  62. package/dist/lib/task-lifecycle/review-submission.mjs +1 -2
  63. package/dist/lib/task-lifecycle/scaffold-provenance.mjs +0 -1
  64. package/dist/lib/task-lifecycle/template-files.mjs +2 -5
  65. package/dist/lib/task-lifecycle.mjs +117 -159
  66. package/dist/lib/task-metadata.mjs +10 -5
  67. package/dist/lib/task-preset-contract-drift.mjs +45 -0
  68. package/dist/lib/task-repository.mjs +192 -0
  69. package/dist/lib/task-review-model.mjs +38 -17
  70. package/dist/lib/task-scanner.mjs +75 -23
  71. package/dist/lib/task-template-materials.mjs +131 -0
  72. package/dist/lib/task-tombstone-commands.mjs +187 -18
  73. package/dist/lib/types/check-profiles.js +1 -0
  74. package/dist/lib/types/impact.js +1 -0
  75. package/dist/lib/types/preset.js +1 -0
  76. package/dist/lib/types/task-lifecycle.js +1 -0
  77. package/dist/lib/types/task-scanner.js +1 -0
  78. package/dist/postinstall.mjs +2 -2
  79. package/dist/run-built-tests.mjs +10 -3
  80. package/docs-release/README.md +2 -1
  81. package/docs-release/architecture/document-contract-kernel/README.md +150 -0
  82. package/docs-release/architecture/document-contract-kernel/products/full-skill-overlay.md +29 -0
  83. package/docs-release/architecture/document-contract-kernel/products/lite-forbidden-surfaces.txt +26 -0
  84. package/docs-release/architecture/document-contract-kernel/products/lite-skill-overlay.md +37 -0
  85. package/docs-release/architecture/overview.md +2 -2
  86. package/docs-release/architecture/overview.zh-CN.md +2 -2
  87. package/docs-release/architecture/system-explainer/01-system-overview.md +11 -7
  88. package/docs-release/architecture/system-explainer/02-module-dependency.md +4 -4
  89. package/docs-release/architecture/system-explainer/03-task-lifecycle.md +17 -12
  90. package/docs-release/architecture/system-explainer/05-data-flow.md +6 -6
  91. package/docs-release/architecture/system-explainer/06-preset-and-migration.md +2 -2
  92. package/docs-release/architecture/system-explainer/README.md +1 -1
  93. package/docs-release/architecture/system-explainer/en-US/01-system-overview.md +12 -8
  94. package/docs-release/architecture/system-explainer/en-US/02-module-dependency.md +5 -5
  95. package/docs-release/architecture/system-explainer/en-US/03-task-lifecycle.md +19 -11
  96. package/docs-release/architecture/system-explainer/en-US/05-data-flow.md +5 -5
  97. package/docs-release/architecture/system-explainer/en-US/06-preset-and-migration.md +2 -2
  98. package/docs-release/architecture/system-explainer/en-US/README.md +1 -1
  99. package/docs-release/guides/agent-installation.en-US.md +4 -6
  100. package/docs-release/guides/agent-installation.md +11 -8
  101. package/docs-release/guides/contributing.md +10 -3
  102. package/docs-release/guides/contributing.zh-CN.md +10 -3
  103. package/docs-release/guides/legacy-migration-agent-prompt.md +1 -1
  104. package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +1 -1
  105. package/docs-release/guides/migration-playbook.en-US.md +9 -6
  106. package/docs-release/guides/migration-playbook.md +9 -6
  107. package/docs-release/guides/preset-development.md +68 -2
  108. package/docs-release/guides/task-state-machine.en-US.md +8 -8
  109. package/docs-release/guides/task-state-machine.md +7 -7
  110. package/docs-release/guides/typescript-runtime-migration-closeout.md +17 -13
  111. package/package.json +19 -11
  112. package/postinstall.mjs +37 -0
  113. package/presets/legacy-migration/preset.yaml +5 -5
  114. package/presets/legacy-migration/templates/execution_strategy.append.md +1 -1
  115. package/presets/lesson-sedimentation/preset.yaml +3 -3
  116. package/presets/module/preset.yaml +2 -2
  117. package/presets/module/templates/execution_strategy.append.md +1 -1
  118. package/presets/module/templates/task_plan.append.md +3 -3
  119. package/presets/release-closeout/checks/check-release-package.mjs +29 -0
  120. package/presets/release-closeout/preset.yaml +100 -0
  121. package/presets/release-closeout/scripts/generate-release-package.mjs +572 -0
  122. package/presets/release-closeout/templates/execution_strategy.append.md +7 -0
  123. package/presets/release-closeout/templates/findings.seed.md +5 -0
  124. package/presets/release-closeout/templates/review.seed.md +3 -0
  125. package/presets/release-closeout/templates/task_plan.append.md +24 -0
  126. package/presets/standard-task/preset.yaml +2 -2
  127. package/references/agents-md-pattern.md +23 -17
  128. package/references/lessons-governance.md +2 -2
  129. package/references/module-parallel-standard.md +3 -6
  130. package/references/pull-request-standard.md +2 -2
  131. package/references/ssot-governance.md +2 -2
  132. package/references/taskr-gap-analysis.md +3 -3
  133. package/run-dist.mjs +34 -0
  134. package/skills/preset-creator/SKILL.md +40 -8
  135. package/skills/preset-creator/references/complex-task-skeleton/brief.md +32 -8
  136. package/skills/preset-creator/references/preset-package-skeleton.md +15 -5
  137. package/skills/preset-creator/references/structure-aware-paths.md +112 -0
  138. package/templates/AGENTS.md.template +28 -26
  139. package/templates/architecture/README.md +2 -2
  140. package/templates/architecture/service-catalog.md +2 -2
  141. package/templates/architecture/services/service-template.md +1 -1
  142. package/templates/dashboard/assets/app-src/00-state.js +5 -1
  143. package/templates/dashboard/assets/app-src/10-router.js +7 -0
  144. package/templates/dashboard/assets/app-src/20-overview.js +8 -8
  145. package/templates/dashboard/assets/app-src/30-tasks.js +132 -40
  146. package/templates/dashboard/assets/app-src/32-task-swimlane.js +314 -0
  147. package/templates/dashboard/assets/app-src/35-task-detail.js +35 -5
  148. package/templates/dashboard/assets/app-src/40-modules.js +257 -41
  149. package/templates/dashboard/assets/app-src/45-review.js +127 -1
  150. package/templates/dashboard/assets/app-src/90-bindings.js +185 -2
  151. package/templates/dashboard/assets/app.css +928 -53
  152. package/templates/dashboard/assets/app.css.manifest.json +2 -0
  153. package/templates/dashboard/assets/app.js +1071 -98
  154. package/templates/dashboard/assets/app.manifest.json +1 -0
  155. package/templates/dashboard/assets/css-src/00-foundation.css +12 -6
  156. package/templates/dashboard/assets/css-src/10-panels-flow.css +2 -2
  157. package/templates/dashboard/assets/css-src/30-task-index.css +21 -13
  158. package/templates/dashboard/assets/css-src/31-archive.css +94 -0
  159. package/templates/dashboard/assets/css-src/32-task-swimlane.css +487 -0
  160. package/templates/dashboard/assets/css-src/35-review-workspace.css +78 -0
  161. package/templates/dashboard/assets/css-src/40-detail-modules-migration.css +191 -14
  162. package/templates/dashboard/assets/css-src/50-responsive-overrides.css +23 -0
  163. package/templates/dashboard/assets/i18n.js +166 -2
  164. package/templates/development/README.md +9 -9
  165. package/templates/development/cross-repo-debugging.md +3 -3
  166. package/templates/development/external-context/service-template.md +1 -1
  167. package/templates/development/external-source-packs/README.md +2 -2
  168. package/templates/integrations/README.md +4 -4
  169. package/templates/integrations/api-contract.md +1 -1
  170. package/templates/integrations/event-contract.md +1 -1
  171. package/templates/integrations/third-party/vendor-template.md +1 -1
  172. package/templates/integrations/webhook-contract.md +1 -1
  173. package/templates/ledger/Harness-Ledger.md +1 -1
  174. package/templates/modules/module_brief.md +50 -0
  175. package/templates/modules/module_plan.md +49 -0
  176. package/templates/modules/registry_view.md +9 -0
  177. package/templates/modules/session_prompt_pack.md +55 -0
  178. package/templates/planning/brief.md +32 -8
  179. package/templates/planning/module_brief.md +28 -3
  180. package/templates/planning/module_plan.md +26 -11
  181. package/templates/planning/module_session_prompt.md +11 -2
  182. package/templates/planning/optional/slices/_slice-template/brief.md +28 -0
  183. package/templates/planning/review.md +1 -1
  184. package/templates/planning/visual_map.md +1 -1
  185. package/templates/reference/docs-library-standard.md +7 -7
  186. package/templates/reference/execution-workflow-standard.md +13 -0
  187. package/templates/reference/external-source-intake-standard.md +10 -10
  188. package/templates/reference/pull-request-standard.md +2 -2
  189. package/templates/reference/repo-governance-standard.md +1 -1
  190. package/templates/reference/review-routing-standard.md +4 -0
  191. package/templates/ssot/Module-Registry.md +4 -38
  192. package/templates/walkthrough/walkthrough-template.md +1 -1
  193. package/templates-zh-CN/AGENTS.md.template +27 -25
  194. package/templates-zh-CN/CLAUDE.md.template +1 -1
  195. package/templates-zh-CN/architecture/README.md +2 -2
  196. package/templates-zh-CN/architecture/service-catalog.md +2 -2
  197. package/templates-zh-CN/architecture/services/service-template.md +1 -1
  198. package/templates-zh-CN/development/README.md +9 -9
  199. package/templates-zh-CN/development/cross-repo-debugging.md +3 -3
  200. package/templates-zh-CN/development/external-context/service-template.md +1 -1
  201. package/templates-zh-CN/development/external-source-packs/README.md +2 -2
  202. package/templates-zh-CN/integrations/README.md +4 -4
  203. package/templates-zh-CN/integrations/api-contract.md +1 -1
  204. package/templates-zh-CN/integrations/event-contract.md +1 -1
  205. package/templates-zh-CN/integrations/third-party/vendor-template.md +1 -1
  206. package/templates-zh-CN/integrations/webhook-contract.md +1 -1
  207. package/templates-zh-CN/ledger/Harness-Ledger.md +1 -1
  208. package/templates-zh-CN/lessons/lesson-arch-process-change.md +1 -1
  209. package/templates-zh-CN/lessons/lesson-new-doc.md +3 -3
  210. package/templates-zh-CN/lessons/lesson-ref-change.md +4 -4
  211. package/templates-zh-CN/modules/module_brief.md +47 -0
  212. package/templates-zh-CN/modules/module_plan.md +48 -0
  213. package/templates-zh-CN/modules/registry_view.md +9 -0
  214. package/templates-zh-CN/modules/session_prompt_pack.md +50 -0
  215. package/templates-zh-CN/planning/INDEX.md +1 -0
  216. package/templates-zh-CN/planning/brief.md +26 -7
  217. package/templates-zh-CN/planning/module_brief.md +24 -2
  218. package/templates-zh-CN/planning/module_plan.md +35 -29
  219. package/templates-zh-CN/planning/module_session_prompt.md +15 -11
  220. package/templates-zh-CN/planning/optional/slices/_slice-template/brief.md +28 -11
  221. package/templates-zh-CN/planning/review.md +1 -1
  222. package/templates-zh-CN/reference/adversarial-review-standard.md +1 -1
  223. package/templates-zh-CN/reference/delivery-operating-model-standard.md +3 -3
  224. package/templates-zh-CN/reference/docs-library-standard.md +27 -27
  225. package/templates-zh-CN/reference/execution-workflow-standard.md +12 -2
  226. package/templates-zh-CN/reference/external-source-intake-standard.md +10 -10
  227. package/templates-zh-CN/reference/harness-ledger-standard.md +3 -3
  228. package/templates-zh-CN/reference/pull-request-standard.md +1 -1
  229. package/templates-zh-CN/reference/regression-ssot-governance.md +2 -2
  230. package/templates-zh-CN/reference/repo-governance-standard.md +1 -1
  231. package/templates-zh-CN/reference/review-routing-standard.md +3 -0
  232. package/templates-zh-CN/reference/walkthrough-standard.md +2 -2
  233. package/templates-zh-CN/reference/worktree-standard.md +1 -1
  234. package/templates-zh-CN/regression/Cadence-Ledger.md +2 -2
  235. package/templates-zh-CN/ssot/Delivery-SSoT.md +2 -2
  236. package/templates-zh-CN/ssot/Module-Registry.md +5 -44
  237. package/templates-zh-CN/ssot/Regression-SSoT.md +2 -2
  238. package/templates-zh-CN/walkthrough/walkthrough-template.md +4 -4
@@ -4,12 +4,26 @@ window.setModulePage = function(moduleKey, page) {
4
4
  app();
5
5
  };
6
6
 
7
+ function rerenderPreservingFieldFocus(field, selector) {
8
+ const shouldRestore = document.activeElement === field;
9
+ const selectionStart = typeof field.selectionStart === "number" ? field.selectionStart : null;
10
+ const selectionEnd = typeof field.selectionEnd === "number" ? field.selectionEnd : selectionStart;
11
+ app();
12
+ if (!shouldRestore) return;
13
+ const nextField = document.querySelector(selector);
14
+ if (!nextField || typeof nextField.focus !== "function") return;
15
+ nextField.focus({ preventScroll: true });
16
+ if (typeof nextField.setSelectionRange === "function" && selectionStart !== null) {
17
+ nextField.setSelectionRange(selectionStart, selectionEnd);
18
+ }
19
+ }
20
+
7
21
  function bind() {
8
22
  document.querySelectorAll("[data-search]").forEach((input) => input.addEventListener("input", () => {
9
23
  state.query = input.value;
10
24
  state.taskPageByGroup = {};
11
25
  state.taskGroupPage = 1;
12
- app();
26
+ rerenderPreservingFieldFocus(input, "[data-search]");
13
27
  }));
14
28
  document.querySelectorAll("[data-state-filter]").forEach((select) => select.addEventListener("change", () => {
15
29
  state.taskState = select.value;
@@ -19,6 +33,7 @@ function bind() {
19
33
  }));
20
34
  document.querySelectorAll("[data-group-mode]").forEach((select) => select.addEventListener("change", () => {
21
35
  state.taskGroupMode = select.value;
36
+ localStorage.setItem("harness.taskGroupMode", state.taskGroupMode);
22
37
  state.taskPageByGroup = {};
23
38
  state.taskGroupPage = 1;
24
39
  app();
@@ -51,7 +66,7 @@ function bind() {
51
66
  }));
52
67
  document.querySelectorAll("[data-preset-search]").forEach((input) => input.addEventListener("input", () => {
53
68
  state.presetQuery = input.value;
54
- app();
69
+ rerenderPreservingFieldFocus(input, "[data-preset-search]");
55
70
  }));
56
71
  document.querySelectorAll("[data-preset-source-filter]").forEach((button) => button.addEventListener("click", () => {
57
72
  state.presetSourceFilter = button.dataset.presetSourceFilter || "all";
@@ -114,6 +129,9 @@ function bind() {
114
129
  document.querySelectorAll("[data-review-queue-tab]").forEach((button) => button.addEventListener("click", () => {
115
130
  state.reviewQueueTab = button.dataset.reviewQueueTab || "review";
116
131
  state.reviewQueuePage = 1;
132
+ state.reviewBulkSelection = {};
133
+ state.reviewBulkResult = null;
134
+ state.lessonBulkResult = null;
117
135
  app();
118
136
  }));
119
137
  document.querySelectorAll("[data-review-reason-filter]").forEach((select) => select.addEventListener("change", () => {
@@ -126,6 +144,51 @@ function bind() {
126
144
  state.reviewQueuePage = 1;
127
145
  app();
128
146
  }));
147
+ document.querySelectorAll("[data-review-bulk-select]").forEach((input) => input.addEventListener("change", () => {
148
+ state.reviewBulkSelection = state.reviewBulkSelection || {};
149
+ state.reviewBulkSelection[input.dataset.reviewBulkSelect || ""] = input.checked;
150
+ state.reviewBulkResult = null;
151
+ app();
152
+ }));
153
+ document.querySelectorAll("[data-review-bulk-select-all]").forEach((input) => input.addEventListener("change", () => {
154
+ const activeTab = reviewQueueTabs().find((tab) => tab.id === state.reviewQueueTab) || reviewQueueTabs()[0];
155
+ const tasks = activeTab.id === "review" ? reviewFilteredTasks(reviewQueueBaseTasks(activeTab)).filter(taskCanBeHumanConfirmed) : [];
156
+ state.reviewBulkSelection = state.reviewBulkSelection || {};
157
+ tasks.forEach((task) => {
158
+ if (input.checked) state.reviewBulkSelection[task.id] = true;
159
+ else delete state.reviewBulkSelection[task.id];
160
+ });
161
+ state.reviewBulkResult = null;
162
+ app();
163
+ }));
164
+ document.querySelectorAll("[data-review-bulk-clear]").forEach((button) => button.addEventListener("click", () => {
165
+ state.reviewBulkSelection = {};
166
+ state.reviewBulkResult = null;
167
+ app();
168
+ }));
169
+ document.querySelectorAll("[data-review-bulk-confirm]").forEach((button) => button.addEventListener("click", () => confirmSelectedReviewsFromDashboard(button)));
170
+ document.querySelectorAll("[data-lesson-bulk-select]").forEach((input) => input.addEventListener("change", () => {
171
+ state.lessonBulkSelection = state.lessonBulkSelection || {};
172
+ state.lessonBulkSelection[input.dataset.lessonBulkSelect || ""] = input.checked;
173
+ state.lessonBulkResult = null;
174
+ app();
175
+ }));
176
+ document.querySelectorAll("[data-lesson-bulk-select-all]").forEach((input) => input.addEventListener("change", () => {
177
+ state.lessonBulkSelection = state.lessonBulkSelection || {};
178
+ lessonBulkActionableSelections().forEach((selection) => {
179
+ const key = lessonBulkSelectionKey(selection.taskId, selection.candidateId);
180
+ if (input.checked) state.lessonBulkSelection[key] = true;
181
+ else delete state.lessonBulkSelection[key];
182
+ });
183
+ state.lessonBulkResult = null;
184
+ app();
185
+ }));
186
+ document.querySelectorAll("[data-lesson-bulk-clear]").forEach((button) => button.addEventListener("click", () => {
187
+ state.lessonBulkSelection = {};
188
+ state.lessonBulkResult = null;
189
+ app();
190
+ }));
191
+ document.querySelectorAll("[data-lesson-bulk-create]").forEach((button) => button.addEventListener("click", () => createSelectedLessonSedimentationFromDashboard(button)));
129
192
  document.querySelectorAll("[data-page-kind]").forEach((button) => button.addEventListener("click", () => {
130
193
  const page = Math.max(1, Number(button.dataset.page) || 1);
131
194
  if (button.dataset.pageKind === "warning") state.warningPage = page;
@@ -166,6 +229,7 @@ function bind() {
166
229
  openLessonDrawer(lessonId);
167
230
  }));
168
231
  document.querySelectorAll("[data-review-complete]").forEach((button) => button.addEventListener("click", () => completeReviewFromDashboard(button.dataset.reviewComplete)));
232
+ document.querySelectorAll("[data-task-complete]").forEach((button) => button.addEventListener("click", () => completeTaskFromDashboard(button.dataset.taskComplete)));
169
233
  const overlay = document.getElementById("drawer-overlay");
170
234
  if (overlay) overlay.addEventListener("click", closeDrawer);
171
235
  }
@@ -240,6 +304,90 @@ async function completeReviewFromDashboard(taskId) {
240
304
  }
241
305
  }
242
306
 
307
+ async function completeTaskFromDashboard(taskId) {
308
+ const result = document.querySelector(`[data-task-complete-result="${CSS.escape(taskId)}"]`);
309
+ if (result) result.textContent = t("taskCloseoutSubmitting");
310
+ try {
311
+ const response = await fetch("/api/tasks/task-complete", {
312
+ method: "POST",
313
+ headers: {
314
+ "content-type": "application/json",
315
+ "x-harness-csrf": state.runtime?.csrfToken || "",
316
+ },
317
+ body: JSON.stringify({
318
+ taskId,
319
+ message: "closed from dashboard workbench",
320
+ evidence: "dashboard:task-complete",
321
+ }),
322
+ });
323
+ const payload = await response.json();
324
+ if (!response.ok) throw new Error(payload.error || t("taskCloseoutFailed"));
325
+ if (result) result.textContent = t("taskCloseoutSuccess");
326
+ setTimeout(() => window.location.reload(), 500);
327
+ } catch (error) {
328
+ if (result) result.textContent = `${t("taskCloseoutFailed")}: ${error.message}`;
329
+ }
330
+ }
331
+
332
+ function dashboardActionErrorDetail(error, fallback) {
333
+ const direct = error?.error || error?.message;
334
+ if (direct) return direct;
335
+ const failedResults = Array.isArray(error?.results) ? error.results.filter((result) => result?.ok === false) : [];
336
+ if (failedResults.length > 0) {
337
+ const reasons = [];
338
+ for (const result of failedResults) {
339
+ const reason = result?.error || result?.message;
340
+ if (reason && !reasons.includes(reason)) reasons.push(reason);
341
+ }
342
+ if (reasons.length > 0) {
343
+ return formatMessage("bulkActionFailedWithReason", {
344
+ failed: error?.failed || failedResults.length,
345
+ reason: reasons.slice(0, 3).join("; "),
346
+ });
347
+ }
348
+ return formatMessage("bulkActionFailedSummary", { failed: error?.failed || failedResults.length });
349
+ }
350
+ return String(error || fallback);
351
+ }
352
+
353
+ async function confirmSelectedReviewsFromDashboard(button) {
354
+ const taskIds = reviewBulkSelectedIds();
355
+ if (!taskIds.length) {
356
+ state.reviewBulkResult = { ok: false, message: t("reviewBulkNone") };
357
+ app();
358
+ return;
359
+ }
360
+ button.disabled = true;
361
+ state.reviewBulkResult = { ok: true, message: t("reviewBulkSubmitting") };
362
+ app();
363
+ try {
364
+ const response = await fetch("/api/tasks/review-complete-bulk", {
365
+ method: "POST",
366
+ headers: {
367
+ "content-type": "application/json",
368
+ "x-harness-csrf": state.runtime?.csrfToken || "",
369
+ },
370
+ body: JSON.stringify({
371
+ taskIds,
372
+ reviewer: "Human Reviewer",
373
+ message: "bulk confirmed from dashboard workbench",
374
+ }),
375
+ });
376
+ const payload = await response.json();
377
+ if (!response.ok) throw payload;
378
+ state.reviewBulkSelection = {};
379
+ state.reviewBulkResult = {
380
+ ok: payload.failed === 0,
381
+ message: payload.failed ? formatMessage("reviewBulkPartial", { confirmed: payload.confirmed || 0, failed: payload.failed || 0 }) : formatMessage("reviewBulkSuccess", { confirmed: payload.confirmed || 0 }),
382
+ };
383
+ app();
384
+ if ((payload.confirmed || 0) > 0) setTimeout(() => window.location.reload(), 1500);
385
+ } catch (error) {
386
+ state.reviewBulkResult = { ok: false, message: `${t("reviewCompleteFailed")}: ${dashboardActionErrorDetail(error, t("reviewCompleteFailed"))}` };
387
+ app();
388
+ }
389
+ }
390
+
243
391
  async function runPresetAction(action, body) {
244
392
  state.presetActionResult = { ok: true, title: t("presetActionRunning"), message: action };
245
393
  app();
@@ -338,6 +486,7 @@ function openDrawer(taskId) {
338
486
  bindRepairPromptButtons(drawer);
339
487
  bindLessonSedimentationButtons(drawer);
340
488
  drawer.querySelectorAll("[data-review-complete]").forEach((button) => button.addEventListener("click", () => completeReviewFromDashboard(button.dataset.reviewComplete)));
489
+ drawer.querySelectorAll("[data-task-complete]").forEach((button) => button.addEventListener("click", () => completeTaskFromDashboard(button.dataset.taskComplete)));
341
490
  }
342
491
 
343
492
  function bindCopyTaskNameButtons(root) {
@@ -456,6 +605,40 @@ async function createLessonSedimentationFromDashboard(button) {
456
605
  }
457
606
  }
458
607
 
608
+ async function createSelectedLessonSedimentationFromDashboard(button) {
609
+ const selections = lessonBulkSelectedSelections();
610
+ if (!selections.length) {
611
+ state.lessonBulkResult = { ok: false, message: t("lessonBulkNone") };
612
+ app();
613
+ return;
614
+ }
615
+ button.disabled = true;
616
+ state.lessonBulkResult = { ok: true, message: t("lessonBulkSubmitting") };
617
+ app();
618
+ try {
619
+ const response = await fetch("/api/tasks/lesson-sedimentation-bulk", {
620
+ method: "POST",
621
+ headers: {
622
+ "content-type": "application/json",
623
+ "x-harness-csrf": state.runtime?.csrfToken || "",
624
+ },
625
+ body: JSON.stringify({ selections }),
626
+ });
627
+ const payload = await response.json();
628
+ if (!response.ok) throw payload;
629
+ state.lessonBulkSelection = {};
630
+ state.lessonBulkResult = {
631
+ ok: payload.failed === 0,
632
+ message: payload.failed ? formatMessage("lessonBulkPartial", { created: payload.created || 0, failed: payload.failed || 0 }) : formatMessage("lessonBulkSuccess", { candidates: payload.candidates || selections.length }),
633
+ };
634
+ app();
635
+ if ((payload.created || 0) > 0) setTimeout(() => window.location.reload(), 1500);
636
+ } catch (error) {
637
+ state.lessonBulkResult = { ok: false, message: `${t("lessonTaskCreateFailed")}: ${error?.error || error?.message || String(error)}` };
638
+ app();
639
+ }
640
+ }
641
+
459
642
  function lessonSedimentationSuccess(payload) {
460
643
  const followUp = payload?.followUpTask || {};
461
644
  const prompt = payload?.prompt || "";