coding-agent-harness 1.0.4 → 1.0.6

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 (279) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/CONTRIBUTING.md +2 -2
  3. package/LICENSE +661 -21
  4. package/LICENSE-EXCEPTION.md +37 -0
  5. package/README.md +96 -4
  6. package/README.zh-CN.md +75 -4
  7. package/SKILL.md +52 -51
  8. package/dist/build-dist.mjs +189 -0
  9. package/dist/check-dist-observation.mjs +428 -0
  10. package/dist/check-harness.mjs +489 -0
  11. package/dist/check-import-graph.mjs +511 -0
  12. package/dist/check-runtime-emit.mjs +304 -0
  13. package/dist/check-type-boundaries.mjs +139 -0
  14. package/dist/commands/dashboard-command.mjs +80 -0
  15. package/dist/commands/migration-command.mjs +152 -0
  16. package/dist/commands/preset-command.mjs +91 -0
  17. package/dist/commands/task-command.mjs +324 -0
  18. package/dist/harness.mjs +304 -0
  19. package/dist/lib/capability-registry.mjs +643 -0
  20. package/dist/lib/check-module-parallel.mjs +227 -0
  21. package/dist/lib/check-profiles.mjs +414 -0
  22. package/dist/lib/check-task-contracts.mjs +54 -0
  23. package/dist/lib/core-shared.mjs +254 -0
  24. package/dist/lib/dashboard-data.mjs +608 -0
  25. package/dist/lib/dashboard-workbench.mjs +334 -0
  26. package/dist/lib/dashboard-writer.mjs +200 -0
  27. package/dist/lib/git-status-summary.mjs +45 -0
  28. package/dist/lib/governance-index-generator.mjs +236 -0
  29. package/dist/lib/governance-sync.mjs +617 -0
  30. package/dist/lib/governance-table-boundary.mjs +161 -0
  31. package/{scripts → dist}/lib/harness-core.mjs +3 -0
  32. package/dist/lib/harness-paths.mjs +338 -0
  33. package/dist/lib/lesson-maintenance.mjs +139 -0
  34. package/dist/lib/markdown-utils.mjs +193 -0
  35. package/dist/lib/migration-planner.mjs +439 -0
  36. package/dist/lib/migration-support.mjs +317 -0
  37. package/dist/lib/phase-kind.mjs +46 -0
  38. package/dist/lib/preset-audit-contracts.mjs +40 -0
  39. package/dist/lib/preset-engine.mjs +516 -0
  40. package/dist/lib/preset-registry.mjs +831 -0
  41. package/dist/lib/preset-resource-contracts.mjs +83 -0
  42. package/dist/lib/review-confirm-git-gate.mjs +244 -0
  43. package/dist/lib/status-builder.mjs +87 -0
  44. package/{scripts → dist}/lib/status-dashboard-renderer.mjs +48 -47
  45. package/dist/lib/structure-migration.mjs +404 -0
  46. package/dist/lib/subagent-authorization-audit.mjs +198 -0
  47. package/dist/lib/task-audit-metadata.mjs +376 -0
  48. package/dist/lib/task-audit-migration.mjs +355 -0
  49. package/dist/lib/task-completion-consistency.mjs +29 -0
  50. package/dist/lib/task-index.mjs +133 -0
  51. package/dist/lib/task-lesson-candidates.mjs +239 -0
  52. package/dist/lib/task-lesson-sedimentation.mjs +300 -0
  53. package/dist/lib/task-lifecycle/create-task-helpers.mjs +84 -0
  54. package/dist/lib/task-lifecycle/phase-sync.mjs +82 -0
  55. package/dist/lib/task-lifecycle/review-confirm.mjs +93 -0
  56. package/dist/lib/task-lifecycle/review-gates.mjs +62 -0
  57. package/dist/lib/task-lifecycle/review-submission.mjs +52 -0
  58. package/dist/lib/task-lifecycle/scaffold-provenance.mjs +54 -0
  59. package/dist/lib/task-lifecycle/template-files.mjs +52 -0
  60. package/dist/lib/task-lifecycle/text-utils.mjs +26 -0
  61. package/dist/lib/task-lifecycle.mjs +611 -0
  62. package/dist/lib/task-metadata.mjs +116 -0
  63. package/dist/lib/task-review-model.mjs +474 -0
  64. package/dist/lib/task-scanner.mjs +439 -0
  65. package/dist/lib/task-tombstone-commands.mjs +125 -0
  66. package/dist/postinstall.mjs +14 -0
  67. package/dist/run-built-tests.mjs +84 -0
  68. package/docs-release/README.md +1 -0
  69. package/docs-release/architecture/overview.md +13 -13
  70. package/docs-release/architecture/overview.zh-CN.md +13 -13
  71. package/docs-release/architecture/system-explainer/01-system-overview.md +218 -0
  72. package/docs-release/architecture/system-explainer/02-module-dependency.md +257 -0
  73. package/docs-release/architecture/system-explainer/03-task-lifecycle.md +304 -0
  74. package/docs-release/architecture/system-explainer/04-check-and-governance.md +241 -0
  75. package/docs-release/architecture/system-explainer/05-data-flow.md +276 -0
  76. package/docs-release/architecture/system-explainer/06-preset-and-migration.md +300 -0
  77. package/docs-release/architecture/system-explainer/README.md +67 -0
  78. package/docs-release/architecture/system-explainer/en-US/01-system-overview.md +227 -0
  79. package/docs-release/architecture/system-explainer/en-US/02-module-dependency.md +263 -0
  80. package/docs-release/architecture/system-explainer/en-US/03-task-lifecycle.md +319 -0
  81. package/docs-release/architecture/system-explainer/en-US/04-check-and-governance.md +252 -0
  82. package/docs-release/architecture/system-explainer/en-US/05-data-flow.md +290 -0
  83. package/docs-release/architecture/system-explainer/en-US/06-preset-and-migration.md +320 -0
  84. package/docs-release/architecture/system-explainer/en-US/README.md +70 -0
  85. package/docs-release/guides/agent-installation.en-US.md +22 -15
  86. package/docs-release/guides/agent-installation.md +23 -15
  87. package/docs-release/guides/contributing.md +3 -3
  88. package/docs-release/guides/contributing.zh-CN.md +3 -3
  89. package/docs-release/guides/document-audience-and-surfaces.en-US.md +10 -10
  90. package/docs-release/guides/document-audience-and-surfaces.md +10 -10
  91. package/docs-release/guides/legacy-migration-agent-prompt.md +25 -2
  92. package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +25 -2
  93. package/docs-release/guides/migration-playbook.en-US.md +63 -1
  94. package/docs-release/guides/migration-playbook.md +59 -1
  95. package/docs-release/guides/parent-control-repository-pattern.en-US.md +25 -25
  96. package/docs-release/guides/parent-control-repository-pattern.md +25 -25
  97. package/docs-release/guides/preset-development.md +28 -4
  98. package/docs-release/guides/repository-operating-models.en-US.md +21 -21
  99. package/docs-release/guides/repository-operating-models.md +21 -21
  100. package/docs-release/guides/task-state-machine.en-US.md +35 -18
  101. package/docs-release/guides/task-state-machine.md +35 -18
  102. package/docs-release/guides/typescript-runtime-migration-closeout.md +96 -0
  103. package/examples/minimal-project/AGENTS.md +2 -2
  104. package/examples/minimal-project/coding-agent-harness/harness.yaml +14 -0
  105. package/examples/minimal-project/coding-agent-harness/planning/tasks/demo-task/INDEX.md +60 -0
  106. package/examples/minimal-project/coding-agent-harness/planning/tasks/demo-task/progress.md +11 -0
  107. package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/review.md +1 -1
  108. package/package.json +22 -13
  109. package/presets/legacy-migration/preset.yaml +5 -5
  110. package/presets/legacy-migration/templates/execution_strategy.append.md +1 -1
  111. package/presets/lesson-sedimentation/preset.yaml +3 -3
  112. package/presets/module/preset.yaml +2 -2
  113. package/presets/module/templates/execution_strategy.append.md +1 -1
  114. package/presets/module/templates/task_plan.append.md +3 -3
  115. package/presets/standard-task/preset.yaml +2 -2
  116. package/references/adversarial-review-standard.md +2 -2
  117. package/references/agents-md-pattern.md +14 -14
  118. package/references/cadence-ledger.md +1 -1
  119. package/references/ci-cd-standard.md +1 -1
  120. package/references/delivery-operating-model-standard.md +4 -4
  121. package/references/docs-directory-standard.md +65 -159
  122. package/references/external-source-intake-standard.md +10 -10
  123. package/references/harness-ledger.md +6 -6
  124. package/references/legacy-12-phase-bootstrap.md +2 -2
  125. package/references/lessons-governance.md +15 -15
  126. package/references/long-running-task-standard.md +6 -6
  127. package/references/module-parallel-standard.md +34 -34
  128. package/references/planning-loop.md +6 -6
  129. package/references/project-onboarding-audit.md +4 -4
  130. package/references/regression-system.md +2 -2
  131. package/references/repo-governance-standard.md +4 -4
  132. package/references/review-routing-standard.md +1 -1
  133. package/references/ssot-governance.md +19 -19
  134. package/references/taskr-gap-analysis.md +5 -5
  135. package/references/walkthrough-closeout.md +14 -14
  136. package/references/worktree-parallel.md +3 -3
  137. package/skills/preset-creator/references/complex-task-skeleton/brief.md +11 -0
  138. package/skills/preset-creator/references/complex-task-skeleton/task_plan.md +1 -1
  139. package/skills/preset-creator/references/preset-package-skeleton.md +5 -5
  140. package/templates/AGENTS.md.template +31 -29
  141. package/templates/architecture/README.md +4 -4
  142. package/templates/architecture/service-catalog.md +2 -2
  143. package/templates/architecture/services/service-template.md +1 -1
  144. package/templates/dashboard/assets/app-src/00-state.js +12 -0
  145. package/templates/dashboard/assets/app-src/10-router.js +3 -0
  146. package/templates/dashboard/assets/app-src/20-overview.js +13 -3
  147. package/templates/dashboard/assets/app-src/35-task-detail.js +46 -6
  148. package/templates/dashboard/assets/app-src/40-modules.js +1 -1
  149. package/templates/dashboard/assets/app-src/55-presets.js +375 -0
  150. package/templates/dashboard/assets/app-src/60-shared.js +3 -1
  151. package/templates/dashboard/assets/app-src/90-bindings.js +131 -0
  152. package/templates/dashboard/assets/app.css +583 -0
  153. package/templates/dashboard/assets/app.css.manifest.json +1 -0
  154. package/templates/dashboard/assets/app.js +585 -11
  155. package/templates/dashboard/assets/app.manifest.json +1 -0
  156. package/templates/dashboard/assets/css-src/00-foundation.css +4 -0
  157. package/templates/dashboard/assets/css-src/40-detail-modules-migration.css +62 -0
  158. package/templates/dashboard/assets/css-src/45-presets.css +516 -0
  159. package/templates/dashboard/assets/i18n.js +144 -4
  160. package/templates/development/README.md +10 -10
  161. package/templates/development/cross-repo-debugging.md +3 -3
  162. package/templates/development/external-context/service-template.md +2 -2
  163. package/templates/development/external-source-packs/README.md +4 -4
  164. package/templates/integrations/README.md +4 -4
  165. package/templates/integrations/api-contract.md +2 -2
  166. package/templates/integrations/event-contract.md +2 -2
  167. package/templates/integrations/third-party/vendor-template.md +2 -2
  168. package/templates/integrations/webhook-contract.md +2 -2
  169. package/templates/ledger/Harness-Ledger.md +1 -1
  170. package/templates/planning/INDEX.md +88 -0
  171. package/templates/planning/brief.md +1 -1
  172. package/templates/planning/module_session_prompt.md +2 -1
  173. package/templates/planning/review.md +0 -18
  174. package/templates/planning/task_plan.md +5 -44
  175. package/templates/planning/visual_map.md +13 -9
  176. package/templates/planning/visual_map.simple.md +52 -0
  177. package/templates/planning/walkthrough.md +47 -0
  178. package/templates/reference/docs-library-standard.md +8 -8
  179. package/templates/reference/execution-workflow-standard.md +29 -2
  180. package/templates/reference/external-source-intake-standard.md +15 -15
  181. package/templates/reference/repo-governance-standard.md +1 -1
  182. package/templates/ssot/Module-Registry.md +1 -1
  183. package/templates/walkthrough/walkthrough-template.md +2 -2
  184. package/templates-zh-CN/AGENTS.md.template +31 -29
  185. package/templates-zh-CN/CLAUDE.md.template +1 -1
  186. package/templates-zh-CN/architecture/README.md +4 -4
  187. package/templates-zh-CN/architecture/service-catalog.md +2 -2
  188. package/templates-zh-CN/architecture/services/service-template.md +1 -1
  189. package/templates-zh-CN/development/README.md +10 -10
  190. package/templates-zh-CN/development/cross-repo-debugging.md +3 -3
  191. package/templates-zh-CN/development/external-context/service-template.md +2 -2
  192. package/templates-zh-CN/development/external-source-packs/README.md +4 -4
  193. package/templates-zh-CN/integrations/README.md +4 -4
  194. package/templates-zh-CN/integrations/api-contract.md +2 -2
  195. package/templates-zh-CN/integrations/event-contract.md +2 -2
  196. package/templates-zh-CN/integrations/third-party/vendor-template.md +2 -2
  197. package/templates-zh-CN/integrations/webhook-contract.md +2 -2
  198. package/templates-zh-CN/ledger/Harness-Ledger.md +1 -1
  199. package/templates-zh-CN/lessons/lesson-arch-process-change.md +1 -1
  200. package/templates-zh-CN/lessons/lesson-new-doc.md +3 -3
  201. package/templates-zh-CN/lessons/lesson-ref-change.md +4 -4
  202. package/templates-zh-CN/planning/INDEX.md +87 -0
  203. package/templates-zh-CN/planning/brief.md +1 -1
  204. package/templates-zh-CN/planning/module_session_prompt.md +12 -11
  205. package/templates-zh-CN/planning/review.md +0 -18
  206. package/templates-zh-CN/planning/task_plan.md +3 -63
  207. package/templates-zh-CN/planning/visual_map.md +14 -7
  208. package/templates-zh-CN/planning/visual_map.simple.md +48 -0
  209. package/templates-zh-CN/planning/walkthrough.md +47 -0
  210. package/templates-zh-CN/reference/adversarial-review-standard.md +2 -2
  211. package/templates-zh-CN/reference/delivery-operating-model-standard.md +3 -3
  212. package/templates-zh-CN/reference/docs-library-standard.md +28 -28
  213. package/templates-zh-CN/reference/execution-workflow-standard.md +32 -7
  214. package/templates-zh-CN/reference/external-source-intake-standard.md +16 -16
  215. package/templates-zh-CN/reference/harness-ledger-standard.md +6 -6
  216. package/templates-zh-CN/reference/regression-ssot-governance.md +2 -2
  217. package/templates-zh-CN/reference/repo-governance-standard.md +1 -1
  218. package/templates-zh-CN/reference/review-routing-standard.md +1 -1
  219. package/templates-zh-CN/reference/walkthrough-standard.md +7 -7
  220. package/templates-zh-CN/reference/worktree-standard.md +1 -1
  221. package/templates-zh-CN/regression/Cadence-Ledger.md +2 -2
  222. package/templates-zh-CN/ssot/Delivery-SSoT.md +3 -3
  223. package/templates-zh-CN/ssot/Module-Registry.md +3 -3
  224. package/templates-zh-CN/ssot/Regression-SSoT.md +2 -2
  225. package/templates-zh-CN/walkthrough/walkthrough-template.md +5 -5
  226. package/tsconfig.dist.json +16 -0
  227. package/tsconfig.json +25 -0
  228. package/tsconfig.runtime.json +24 -0
  229. package/examples/minimal-project/.harness-capabilities.json +0 -8
  230. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/progress.md +0 -11
  231. package/scripts/check-harness.mjs +0 -508
  232. package/scripts/commands/dashboard-command.mjs +0 -67
  233. package/scripts/commands/migration-command.mjs +0 -96
  234. package/scripts/commands/preset-command.mjs +0 -73
  235. package/scripts/commands/task-command.mjs +0 -327
  236. package/scripts/harness.mjs +0 -287
  237. package/scripts/lib/capability-registry.mjs +0 -591
  238. package/scripts/lib/check-module-parallel.mjs +0 -237
  239. package/scripts/lib/check-profiles.mjs +0 -418
  240. package/scripts/lib/check-task-contracts.mjs +0 -47
  241. package/scripts/lib/core-shared.mjs +0 -196
  242. package/scripts/lib/dashboard-data.mjs +0 -412
  243. package/scripts/lib/dashboard-workbench.mjs +0 -257
  244. package/scripts/lib/dashboard-writer.mjs +0 -198
  245. package/scripts/lib/git-status-summary.mjs +0 -46
  246. package/scripts/lib/governance-index-generator.mjs +0 -174
  247. package/scripts/lib/governance-sync.mjs +0 -514
  248. package/scripts/lib/governance-table-boundary.mjs +0 -175
  249. package/scripts/lib/lesson-maintenance.mjs +0 -152
  250. package/scripts/lib/markdown-utils.mjs +0 -158
  251. package/scripts/lib/migration-planner.mjs +0 -478
  252. package/scripts/lib/migration-support.mjs +0 -312
  253. package/scripts/lib/preset-audit-contracts.mjs +0 -37
  254. package/scripts/lib/preset-engine.mjs +0 -497
  255. package/scripts/lib/preset-registry.mjs +0 -627
  256. package/scripts/lib/preset-resource-contracts.mjs +0 -83
  257. package/scripts/lib/review-confirm-git-gate.mjs +0 -248
  258. package/scripts/lib/subagent-authorization-audit.mjs +0 -196
  259. package/scripts/lib/task-completion-consistency.mjs +0 -16
  260. package/scripts/lib/task-index.mjs +0 -93
  261. package/scripts/lib/task-lesson-candidates.mjs +0 -242
  262. package/scripts/lib/task-lesson-sedimentation.mjs +0 -326
  263. package/scripts/lib/task-lifecycle/review-confirm.mjs +0 -101
  264. package/scripts/lib/task-lifecycle/review-gates.mjs +0 -70
  265. package/scripts/lib/task-lifecycle/text-utils.mjs +0 -24
  266. package/scripts/lib/task-lifecycle.mjs +0 -649
  267. package/scripts/lib/task-review-model.mjs +0 -469
  268. package/scripts/lib/task-scanner.mjs +0 -576
  269. package/scripts/lib/task-tombstone-commands.mjs +0 -140
  270. package/scripts/postinstall.mjs +0 -14
  271. package/templates/walkthrough/Closeout-SSoT.md +0 -43
  272. package/templates-zh-CN/walkthrough/Closeout-SSoT.md +0 -42
  273. /package/examples/minimal-project/{docs → coding-agent-harness/governance/generated}/Harness-Ledger.md +0 -0
  274. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/brief.md +0 -0
  275. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/execution_strategy.md +0 -0
  276. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/findings.md +0 -0
  277. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/lesson_candidates.md +0 -0
  278. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/task_plan.md +0 -0
  279. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/visual_map.md +0 -0
@@ -0,0 +1,404 @@
1
+ // @ts-nocheck
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { normalizeLocale, normalizeTarget, readJsonSafe, toPosix, } from "./core-shared.mjs";
5
+ import { legacyModuleRoot, legacyPath, v2HarnessRoot, } from "./harness-paths.mjs";
6
+ const legacyMappings = [
7
+ ["03-ARCHITECTURE", `${v2HarnessRoot}/context/architecture`],
8
+ ["04-DEVELOPMENT", `${v2HarnessRoot}/context/development`],
9
+ ["06-INTEGRATIONS", `${v2HarnessRoot}/context/integrations`],
10
+ ["05-TEST-QA", `${v2HarnessRoot}/governance/regression`],
11
+ ["11-REFERENCE", `${v2HarnessRoot}/governance/standards`],
12
+ ["09-PLANNING/TASKS", `${v2HarnessRoot}/planning/tasks`],
13
+ ["09-PLANNING/MODULES", `${v2HarnessRoot}/planning/modules`],
14
+ ["09-PLANNING/Module-Registry.md", `${v2HarnessRoot}/planning/modules/Module-Registry.md`],
15
+ ["10-WALKTHROUGH", `${v2HarnessRoot}/governance/archive/legacy-walkthrough`],
16
+ ["Harness-Ledger.md", `${v2HarnessRoot}/governance/archive/legacy-governance/Harness-Ledger.md`],
17
+ ];
18
+ export function planStructureMigration(targetInput = ".") {
19
+ const target = normalizeTarget(targetInput);
20
+ const legacyDocsRoot = path.join(target.projectRoot, "docs");
21
+ const manifestPath = path.join(target.projectRoot, v2HarnessRoot, "harness.yaml");
22
+ const capabilities = readLegacyCapabilities(target.projectRoot);
23
+ const actions = [];
24
+ if (!fs.existsSync(legacyDocsRoot)) {
25
+ actions.push({
26
+ action: fs.existsSync(manifestPath) ? "already-v2" : "create-v2-manifest",
27
+ source: "",
28
+ destination: toPosix(path.relative(target.projectRoot, manifestPath)),
29
+ });
30
+ }
31
+ for (const [legacyRelative, v2Relative] of legacyMappings) {
32
+ const source = path.join(legacyDocsRoot, legacyRelative);
33
+ if (!fs.existsSync(source))
34
+ continue;
35
+ actions.push({
36
+ action: "move",
37
+ source: toPosix(path.relative(target.projectRoot, source)),
38
+ destination: v2Relative,
39
+ });
40
+ }
41
+ const archiveDestination = `${v2HarnessRoot}/governance/archive/legacy-docs`;
42
+ if (fs.existsSync(legacyDocsRoot)) {
43
+ actions.push({
44
+ action: "archive-source-root",
45
+ source: "docs",
46
+ destination: archiveDestination,
47
+ });
48
+ }
49
+ for (const relative of generatedTemplateDirs()) {
50
+ const legacyEquivalent = relative
51
+ .replace(`${v2HarnessRoot}/planning/tasks`, "09-PLANNING/TASKS")
52
+ .replace(`${v2HarnessRoot}/planning/modules`, "09-PLANNING/MODULES");
53
+ if (fs.existsSync(path.join(target.projectRoot, relative)) || fs.existsSync(path.join(legacyDocsRoot, legacyEquivalent))) {
54
+ actions.push({
55
+ action: "remove-generated-template-dir",
56
+ source: "",
57
+ destination: relative,
58
+ });
59
+ }
60
+ }
61
+ const legacyRegistry = path.join(target.projectRoot, ".harness-capabilities.json");
62
+ if (fs.existsSync(legacyRegistry)) {
63
+ actions.push({
64
+ action: "archive-legacy-registry",
65
+ source: ".harness-capabilities.json",
66
+ destination: `${v2HarnessRoot}/governance/archive/legacy-governance/.harness-capabilities.json`,
67
+ });
68
+ }
69
+ return {
70
+ operation: "migrate-structure",
71
+ target: target.projectRoot,
72
+ mode: fs.existsSync(manifestPath) ? "v2-present" : "legacy-source",
73
+ manifest: toPosix(path.relative(target.projectRoot, manifestPath)),
74
+ capabilities,
75
+ actions,
76
+ summary: {
77
+ actions: actions.length,
78
+ moves: actions.filter((action) => action.action === "move").length,
79
+ willArchiveLegacyDocs: actions.some((action) => action.action === "archive-source-root"),
80
+ canApply: actions.length > 0,
81
+ },
82
+ };
83
+ }
84
+ export function applyStructureMigration(targetInput = ".", { force = false } = {}) {
85
+ const plan = planStructureMigration(targetInput);
86
+ const targetRoot = plan.target;
87
+ const manifestPath = path.join(targetRoot, plan.manifest);
88
+ const applied = [];
89
+ preflightStructureMigration(plan, { force });
90
+ if (!fs.existsSync(manifestPath) || force) {
91
+ fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
92
+ fs.writeFileSync(manifestPath, renderHarnessManifest({ locale: plan.capabilities.locale, capabilities: plan.capabilities.names }));
93
+ applied.push({ action: "write-manifest", destination: plan.manifest });
94
+ }
95
+ for (const action of plan.actions.filter((entry) => entry.action === "move")) {
96
+ const source = path.join(targetRoot, action.source);
97
+ const destination = path.join(targetRoot, action.destination);
98
+ if (!fs.existsSync(source))
99
+ continue;
100
+ if (fs.existsSync(destination) && !force) {
101
+ throw new Error(`Refusing to overwrite existing v2 destination: ${action.destination}`);
102
+ }
103
+ if (fs.existsSync(destination))
104
+ fs.rmSync(destination, { recursive: true, force: true });
105
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
106
+ fs.cpSync(source, destination, { recursive: true });
107
+ applied.push({ action: "copy", source: action.source, destination: action.destination });
108
+ }
109
+ const docsRoot = path.join(targetRoot, "docs");
110
+ if (fs.existsSync(docsRoot)) {
111
+ const archiveRoot = uniqueArchiveRoot(targetRoot);
112
+ fs.mkdirSync(path.dirname(archiveRoot), { recursive: true });
113
+ fs.renameSync(docsRoot, archiveRoot);
114
+ applied.push({ action: "archive-source-root", source: "docs", destination: toPosix(path.relative(targetRoot, archiveRoot)) });
115
+ }
116
+ archiveLegacyCapabilityRegistry(targetRoot, applied);
117
+ normalizeMigratedModuleTasks(targetRoot, applied, { force });
118
+ scaffoldMissingTaskWalkthroughs(targetRoot, applied);
119
+ removeGeneratedTemplateDirectories(targetRoot, applied);
120
+ return {
121
+ ...plan,
122
+ applied: true,
123
+ actionsApplied: applied,
124
+ summary: {
125
+ ...plan.summary,
126
+ applied: applied.length,
127
+ },
128
+ };
129
+ }
130
+ function removeGeneratedTemplateDirectories(targetRoot, applied) {
131
+ for (const relative of generatedTemplateDirs()) {
132
+ const directory = path.join(targetRoot, relative);
133
+ if (!fs.existsSync(directory))
134
+ continue;
135
+ fs.rmSync(directory, { recursive: true, force: true });
136
+ applied.push({
137
+ action: "remove-generated-template-dir",
138
+ destination: relative,
139
+ });
140
+ }
141
+ }
142
+ function generatedTemplateDirs() {
143
+ return [
144
+ `${v2HarnessRoot}/planning/tasks/_task-template`,
145
+ `${v2HarnessRoot}/planning/modules/_task-template`,
146
+ `${v2HarnessRoot}/planning/modules/_module-template`,
147
+ ];
148
+ }
149
+ function preflightStructureMigration(plan, { force = false } = {}) {
150
+ if (force)
151
+ return;
152
+ const conflicts = [];
153
+ for (const action of plan.actions.filter((entry) => entry.action === "move")) {
154
+ const source = path.join(plan.target, action.source);
155
+ const destination = path.join(plan.target, action.destination);
156
+ if (fs.existsSync(source) && fs.existsSync(destination))
157
+ conflicts.push(action.destination);
158
+ }
159
+ conflicts.push(...moduleTaskNormalizationConflicts(plan.target));
160
+ if (conflicts.length) {
161
+ throw new Error(`Refusing to overwrite existing v2 destination(s): ${conflicts.join(", ")}`);
162
+ }
163
+ }
164
+ function moduleTaskNormalizationConflicts(targetRoot) {
165
+ const modulesRoot = path.join(targetRoot, legacyPath(legacyModuleRoot));
166
+ if (!fs.existsSync(modulesRoot))
167
+ return [];
168
+ const conflicts = [];
169
+ for (const moduleName of fs.readdirSync(modulesRoot)) {
170
+ if (moduleName.startsWith("_"))
171
+ continue;
172
+ const moduleDir = path.join(modulesRoot, moduleName);
173
+ if (!fs.statSync(moduleDir).isDirectory())
174
+ continue;
175
+ const legacyTasksRoot = path.join(moduleDir, "TASKS");
176
+ const normalizedTasksRoot = path.join(moduleDir, "tasks");
177
+ if (!hasExactChild(moduleDir, "TASKS") || !hasExactChild(moduleDir, "tasks"))
178
+ continue;
179
+ for (const taskName of fs.readdirSync(legacyTasksRoot)) {
180
+ if (fs.existsSync(path.join(normalizedTasksRoot, taskName))) {
181
+ conflicts.push(`${v2HarnessRoot}/planning/modules/${moduleName}/tasks/${taskName}`);
182
+ }
183
+ }
184
+ }
185
+ return conflicts;
186
+ }
187
+ function hasExactChild(parentDir, childName) {
188
+ return fs.existsSync(parentDir) && fs.readdirSync(parentDir).includes(childName);
189
+ }
190
+ function archiveLegacyCapabilityRegistry(targetRoot, applied) {
191
+ const registry = path.join(targetRoot, ".harness-capabilities.json");
192
+ if (!fs.existsSync(registry))
193
+ return;
194
+ const destination = uniqueArchiveFile(targetRoot, `${v2HarnessRoot}/governance/archive/legacy-governance/.harness-capabilities.json`);
195
+ fs.mkdirSync(path.dirname(destination), { recursive: true });
196
+ fs.renameSync(registry, destination);
197
+ applied.push({
198
+ action: "archive-legacy-registry",
199
+ source: ".harness-capabilities.json",
200
+ destination: toPosix(path.relative(targetRoot, destination)),
201
+ });
202
+ }
203
+ function readLegacyCapabilities(projectRoot) {
204
+ const raw = readJsonSafe(path.join(projectRoot, ".harness-capabilities.json"), null);
205
+ const names = new Set(["core"]);
206
+ let locale = "en-US";
207
+ if (raw) {
208
+ locale = normalizeLocale(raw.locale);
209
+ for (const entry of raw.capabilities || [])
210
+ names.add(typeof entry === "string" ? entry : entry.name);
211
+ }
212
+ return { locale, names: [...names].filter(Boolean) };
213
+ }
214
+ function renderHarnessManifest({ locale, capabilities }) {
215
+ return [
216
+ "version: 2",
217
+ `locale: ${normalizeLocale(locale)}`,
218
+ "capabilities:",
219
+ ...[...new Set(capabilities)].map((capability) => ` - ${capability}`),
220
+ "structure:",
221
+ ` harnessRoot: ${v2HarnessRoot}`,
222
+ ` planningRoot: ${v2HarnessRoot}/planning`,
223
+ ` tasksRoot: ${v2HarnessRoot}/planning/tasks`,
224
+ ` modulesRoot: ${v2HarnessRoot}/planning/modules`,
225
+ ` externalRoot: ${v2HarnessRoot}/planning/external`,
226
+ ` governanceRoot: ${v2HarnessRoot}/governance`,
227
+ ` generatedRoot: ${v2HarnessRoot}/governance/generated`,
228
+ "",
229
+ ].join("\n");
230
+ }
231
+ function uniqueArchiveRoot(targetRoot) {
232
+ const stamp = new Date().toISOString().replace(/[-:]/g, "").replace(".", "-");
233
+ const base = path.join(targetRoot, v2HarnessRoot, "governance/archive/legacy-docs", stamp);
234
+ for (let index = 0; index < 100; index += 1) {
235
+ const candidate = index === 0 ? base : `${base}-${String(index).padStart(2, "0")}`;
236
+ if (!fs.existsSync(candidate))
237
+ return candidate;
238
+ }
239
+ throw new Error("Unable to allocate legacy docs archive directory");
240
+ }
241
+ function uniqueArchiveFile(targetRoot, relativeFile) {
242
+ const parsed = path.parse(path.join(targetRoot, relativeFile));
243
+ const base = path.join(parsed.dir, parsed.name);
244
+ for (let index = 0; index < 100; index += 1) {
245
+ const suffix = index === 0 ? "" : `-${String(index).padStart(2, "0")}`;
246
+ const candidate = `${base}${suffix}${parsed.ext}`;
247
+ if (!fs.existsSync(candidate))
248
+ return candidate;
249
+ }
250
+ throw new Error(`Unable to allocate legacy archive file: ${relativeFile}`);
251
+ }
252
+ function normalizeMigratedModuleTasks(targetRoot, applied, { force = false } = {}) {
253
+ const modulesRoot = path.join(targetRoot, v2HarnessRoot, "planning/modules");
254
+ if (!fs.existsSync(modulesRoot))
255
+ return;
256
+ for (const moduleName of fs.readdirSync(modulesRoot)) {
257
+ if (moduleName.startsWith("_"))
258
+ continue;
259
+ const moduleDir = path.join(modulesRoot, moduleName);
260
+ if (!fs.statSync(moduleDir).isDirectory())
261
+ continue;
262
+ const legacyTasksRoot = path.join(moduleDir, "TASKS");
263
+ if (!fs.existsSync(legacyTasksRoot))
264
+ continue;
265
+ const stagingRoot = uniqueModuleTaskStagingRoot(moduleDir);
266
+ fs.renameSync(legacyTasksRoot, stagingRoot);
267
+ const tasksRoot = path.join(moduleDir, "tasks");
268
+ fs.mkdirSync(tasksRoot, { recursive: true });
269
+ for (const taskName of fs.readdirSync(stagingRoot)) {
270
+ const source = path.join(stagingRoot, taskName);
271
+ const destination = path.join(tasksRoot, taskName);
272
+ if (fs.existsSync(destination) && !force) {
273
+ throw new Error(`Refusing to overwrite existing migrated module task: ${toPosix(path.relative(targetRoot, destination))}`);
274
+ }
275
+ if (fs.existsSync(destination))
276
+ fs.rmSync(destination, { recursive: true, force: true });
277
+ fs.renameSync(source, destination);
278
+ applied.push({
279
+ action: "move",
280
+ source: toPosix(path.relative(targetRoot, source)),
281
+ destination: toPosix(path.relative(targetRoot, destination)),
282
+ });
283
+ }
284
+ fs.rmSync(stagingRoot, { recursive: true, force: true });
285
+ }
286
+ }
287
+ function uniqueModuleTaskStagingRoot(moduleDir) {
288
+ for (let index = 0; index < 100; index += 1) {
289
+ const suffix = index === 0 ? "" : `-${String(index).padStart(2, "0")}`;
290
+ const candidate = path.join(moduleDir, `.TASKS-migration-${process.pid}${suffix}`);
291
+ if (!fs.existsSync(candidate))
292
+ return candidate;
293
+ }
294
+ throw new Error(`Unable to allocate module task staging directory: ${moduleDir}`);
295
+ }
296
+ function scaffoldMissingTaskWalkthroughs(targetRoot, applied) {
297
+ for (const taskDir of migratedTaskDirectories(targetRoot)) {
298
+ const taskId = path.basename(taskDir);
299
+ const index = path.join(taskDir, "INDEX.md");
300
+ if (!fs.existsSync(index)) {
301
+ fs.writeFileSync(index, renderMigratedTaskIndex(taskId));
302
+ applied.push({
303
+ action: "create",
304
+ destination: toPosix(path.relative(targetRoot, index)),
305
+ });
306
+ }
307
+ const walkthrough = path.join(taskDir, "walkthrough.md");
308
+ if (!fs.existsSync(walkthrough)) {
309
+ fs.writeFileSync(walkthrough, "# Walkthrough\n\nPending migrated closeout.\n");
310
+ applied.push({
311
+ action: "create",
312
+ destination: toPosix(path.relative(targetRoot, walkthrough)),
313
+ });
314
+ }
315
+ const visualMap = path.join(taskDir, "visual_map.md");
316
+ if (!fs.existsSync(visualMap)) {
317
+ fs.writeFileSync(visualMap, renderMigratedVisualMap());
318
+ applied.push({
319
+ action: "create",
320
+ destination: toPosix(path.relative(targetRoot, visualMap)),
321
+ });
322
+ }
323
+ }
324
+ }
325
+ function renderMigratedVisualMap() {
326
+ return `# Visual Map
327
+
328
+ Visual Map Contract: v1.0
329
+
330
+ | Phase ID | Kind | Depends On | State | Completion | Output | Required Evidence | Exit Command | Actor | Evidence Status | Blocking Risk | Owner / Handoff |
331
+ | --- | --- | --- | --- | ---: | --- | --- | --- | --- | --- | --- | --- |
332
+ | MIG-01 | execution | none | planned | 0 | migrated task validation | migrated task materials | n/a | agent | present | none | coordinator |
333
+ `;
334
+ }
335
+ function migratedTaskDirectories(targetRoot) {
336
+ const taskDirs = [];
337
+ const tasksRoot = path.join(targetRoot, v2HarnessRoot, "planning/tasks");
338
+ if (fs.existsSync(tasksRoot)) {
339
+ for (const entry of fs.readdirSync(tasksRoot)) {
340
+ const taskDir = path.join(tasksRoot, entry);
341
+ if (fs.existsSync(path.join(taskDir, "task_plan.md")))
342
+ taskDirs.push(taskDir);
343
+ }
344
+ }
345
+ const modulesRoot = path.join(targetRoot, v2HarnessRoot, "planning/modules");
346
+ if (!fs.existsSync(modulesRoot))
347
+ return taskDirs;
348
+ for (const moduleName of fs.readdirSync(modulesRoot)) {
349
+ if (moduleName.startsWith("_"))
350
+ continue;
351
+ const moduleTasksRoot = path.join(modulesRoot, moduleName, "tasks");
352
+ if (!fs.existsSync(moduleTasksRoot))
353
+ continue;
354
+ for (const entry of fs.readdirSync(moduleTasksRoot)) {
355
+ const taskDir = path.join(moduleTasksRoot, entry);
356
+ if (fs.existsSync(path.join(taskDir, "task_plan.md")))
357
+ taskDirs.push(taskDir);
358
+ }
359
+ }
360
+ return taskDirs;
361
+ }
362
+ function renderMigratedTaskIndex(taskId) {
363
+ const today = new Date().toISOString().slice(0, 10);
364
+ return `# ${taskId} - Task Package Index
365
+
366
+ Task Contract: harness-task/v1
367
+
368
+ ## Task Identity
369
+
370
+ | Field | Value |
371
+ | --- | --- |
372
+ | Task ID | \`${taskId}\` |
373
+ | Budget | \`simple\` |
374
+ | Walkthrough Path | \`walkthrough.md\` |
375
+
376
+ ## Task Audit Metadata
377
+
378
+ | Field | Value |
379
+ | --- | --- |
380
+ | Created By | historical-backfill |
381
+ | Created At | ${today} |
382
+ | Command Shape | harness migrate-structure --apply |
383
+ | Budget | simple |
384
+ | Template Source | structure-migration |
385
+ | Task Creator | migration |
386
+ | Task Creator Source | git-unavailable |
387
+ | Human Review Status | not-confirmed |
388
+ | Confirmation ID | n/a |
389
+ | Confirmed At | n/a |
390
+ | Reviewer | n/a |
391
+ | Reviewer Email | n/a |
392
+ | Confirm Text | n/a |
393
+ | Evidence Checked | n/a |
394
+ | Review Commit SHA | n/a |
395
+ | Audit Source | native-index |
396
+ | Audit Status | created |
397
+ | Exception Reason | n/a |
398
+ | Message | v2 structure migration backfill |
399
+ | Migration Status | migrated |
400
+ | Migrated From | docs |
401
+ | Legacy Extra Fields | {} |
402
+ | Migration Notes | task index created during hard-cutover structure migration |
403
+ `;
404
+ }
@@ -0,0 +1,198 @@
1
+ // @ts-nocheck
2
+ import path from "node:path";
3
+ import { readFileSafe, toPosix, walkFiles, isArchivedHarnessPath, } from "./core-shared.mjs";
4
+ import { firstColumn, splitMarkdownRow, } from "./markdown-utils.mjs";
5
+ export function validateSubagentAuthorization(target, { strict = true } = {}) {
6
+ const failures = [];
7
+ const warnings = [];
8
+ const report = (message) => {
9
+ if (strict)
10
+ failures.push(message);
11
+ else
12
+ warnings.push(`adoption-needed: ${message}`);
13
+ };
14
+ const strategyPaths = walkFiles(target.docsRoot)
15
+ .filter((file) => file.endsWith("execution_strategy.md"))
16
+ .filter((file) => !file.includes(`${path.sep}_task-template${path.sep}`))
17
+ .filter((file) => !file.includes(`${path.sep}_optional-structures${path.sep}`))
18
+ .filter((file) => !isArchivedHarnessPath(file));
19
+ for (const strategyPath of strategyPaths) {
20
+ const relative = toPosix(path.relative(target.projectRoot, strategyPath));
21
+ const content = readFileSafe(strategyPath);
22
+ const rows = subagentAuthorizationRows(content);
23
+ for (const row of rows.filter((candidate) => /worker/i.test(candidate.role))) {
24
+ if (!isWorkerAuthorizedStatus(row.status))
25
+ continue;
26
+ const missing = [];
27
+ for (const [label, value] of [
28
+ ["Authorized By", row.authorizedBy],
29
+ ["Authorized At", row.authorizedAt],
30
+ ["Scope", row.scope],
31
+ ["Worktree / Branch", row.worktreeBranch],
32
+ ]) {
33
+ if (!isConcreteAuthorizationValue(value))
34
+ missing.push(label);
35
+ }
36
+ if (missing.length > 0)
37
+ report(`${relative} worker subagent authorization is incomplete: ${missing.join(", ")}`);
38
+ }
39
+ const delegation = subagentDelegationRows(content).find((row) => /worker/i.test(row.question));
40
+ if (delegation && normalizeDecision(delegation.decision) === "ask-user") {
41
+ const userDecision = userAuthorizationRows(content).reverse().find((row) => /worker/i.test(row.gate) && isResolvedWorkerGateState(row.state));
42
+ if (!userDecision) {
43
+ report(`${relative} worker subagent ask-user decision is unresolved: missing User Authorization Decision`);
44
+ }
45
+ else {
46
+ const missing = missingUserDecisionFields(userDecision);
47
+ if (missing.length > 0)
48
+ report(`${relative} worker subagent authorization decision is incomplete: ${missing.join(", ")}`);
49
+ }
50
+ }
51
+ }
52
+ return { failures, warnings };
53
+ }
54
+ function subagentAuthorizationRows(content) {
55
+ const section = markdownSection(content, "Subagent Authorization");
56
+ if (!section)
57
+ return [];
58
+ const lines = section.split(/\r?\n/);
59
+ for (let index = 0; index < lines.length - 1; index += 1) {
60
+ if (!lines[index].trim().startsWith("|"))
61
+ continue;
62
+ const header = splitMarkdownRow(lines[index]);
63
+ const separator = splitMarkdownRow(lines[index + 1]);
64
+ if (!separator.every((cell) => /^:?-{3,}:?$/.test(cell)))
65
+ continue;
66
+ const roleIndex = firstColumn(header, ["Role"]);
67
+ const statusIndex = firstColumn(header, ["Status"]);
68
+ if (roleIndex < 0 || statusIndex < 0)
69
+ continue;
70
+ const indexes = {
71
+ role: roleIndex,
72
+ status: statusIndex,
73
+ authorizedBy: firstColumn(header, ["Authorized By"]),
74
+ authorizedAt: firstColumn(header, ["Authorized At"]),
75
+ scope: firstColumn(header, ["Scope"]),
76
+ worktreeBranch: firstColumn(header, ["Worktree / Branch", "Worktree", "Branch"]),
77
+ };
78
+ return lines
79
+ .slice(index + 2)
80
+ .filter((line) => line.trim().startsWith("|"))
81
+ .map(splitMarkdownRow)
82
+ .filter((row) => row.length === header.length)
83
+ .map((row) => Object.fromEntries(Object.entries(indexes).map(([key, column]) => [key, column >= 0 ? row[column] || "" : ""])));
84
+ }
85
+ return [];
86
+ }
87
+ function subagentDelegationRows(content) {
88
+ const section = markdownSection(content, "Subagent Delegation Decision");
89
+ if (!section)
90
+ return [];
91
+ return parseFirstTable(section, ["Question", "Decision"]).map((row) => ({
92
+ question: row.Question || "",
93
+ decision: row.Decision || "",
94
+ }));
95
+ }
96
+ function userAuthorizationRows(content) {
97
+ return parseMatchingTables(content, ["Gate", "State"]).map((row) => ({
98
+ gate: row.Gate || "",
99
+ state: row.State || "",
100
+ decidedBy: row["Decided By"] || "",
101
+ decidedAt: row["Decided At"] || "",
102
+ scope: row.Scope || "",
103
+ worktreeBranch: row["Worktree / Branch"] || row.Worktree || row.Branch || "",
104
+ }));
105
+ }
106
+ function parseMatchingTables(content, requiredColumns) {
107
+ const lines = String(content || "").split(/\r?\n/);
108
+ const rows = [];
109
+ for (let index = 0; index < lines.length - 1; index += 1) {
110
+ if (!lines[index].trim().startsWith("|"))
111
+ continue;
112
+ const header = splitMarkdownRow(lines[index]);
113
+ const separator = splitMarkdownRow(lines[index + 1]);
114
+ if (!separator.every((cell) => /^:?-{3,}:?$/.test(cell)))
115
+ continue;
116
+ if (requiredColumns.some((column) => firstColumn(header, [column]) < 0))
117
+ continue;
118
+ let rowIndex = index + 2;
119
+ while (rowIndex < lines.length && lines[rowIndex].trim().startsWith("|")) {
120
+ const row = splitMarkdownRow(lines[rowIndex]);
121
+ if (row.length === header.length)
122
+ rows.push(Object.fromEntries(header.map((column, columnIndex) => [column, row[columnIndex] || ""])));
123
+ rowIndex += 1;
124
+ }
125
+ }
126
+ return rows;
127
+ }
128
+ function parseFirstTable(content, requiredColumns) {
129
+ const lines = String(content || "").split(/\r?\n/);
130
+ for (let index = 0; index < lines.length - 1; index += 1) {
131
+ if (!lines[index].trim().startsWith("|"))
132
+ continue;
133
+ const header = splitMarkdownRow(lines[index]);
134
+ const separator = splitMarkdownRow(lines[index + 1]);
135
+ if (!separator.every((cell) => /^:?-{3,}:?$/.test(cell)))
136
+ continue;
137
+ if (requiredColumns.some((column) => firstColumn(header, [column]) < 0))
138
+ continue;
139
+ return lines
140
+ .slice(index + 2)
141
+ .filter((line) => line.trim().startsWith("|"))
142
+ .map(splitMarkdownRow)
143
+ .filter((row) => row.length === header.length)
144
+ .map((row) => Object.fromEntries(header.map((column, columnIndex) => [column, row[columnIndex] || ""])));
145
+ }
146
+ return [];
147
+ }
148
+ function markdownSection(content, heading) {
149
+ const lines = String(content || "").split(/\r?\n/);
150
+ const escaped = heading.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
151
+ const start = lines.findIndex((line) => new RegExp(`^##\\s+${escaped}\\s*$`, "i").test(line.trim()));
152
+ if (start < 0)
153
+ return "";
154
+ const end = lines.findIndex((line, index) => index > start && /^##\s+/.test(line));
155
+ return lines.slice(start + 1, end < 0 ? undefined : end).join("\n");
156
+ }
157
+ function isConcreteAuthorizationValue(value) {
158
+ const raw = String(value || "").replace(/`/g, "").trim();
159
+ return Boolean(raw) && !/^\[.*\]$/.test(raw) && !/^(pending|n\/a|na|none|-|—|–|待授权|待定|无)$/i.test(raw);
160
+ }
161
+ function isWorkerAuthorizedStatus(value) {
162
+ const raw = String(value || "")
163
+ .replace(/`/g, "")
164
+ .trim()
165
+ .toLowerCase()
166
+ .replaceAll("_", "-")
167
+ .replace(/\s+/g, "-");
168
+ if (!raw || /^(not-authorized|unauthorized|pending|no|false|未授权|待授权)$/.test(raw))
169
+ return false;
170
+ return /(^|\b)(authorized|used|active|approved)(\b|$)|已授权|已使用/.test(raw);
171
+ }
172
+ function normalizeDecision(value) {
173
+ return String(value || "")
174
+ .replace(/`/g, "")
175
+ .trim()
176
+ .toLowerCase()
177
+ .replaceAll("_", "-")
178
+ .replace(/\s+/g, "-");
179
+ }
180
+ function isResolvedWorkerGateState(value) {
181
+ const raw = normalizeDecision(value);
182
+ return /^(authorized|approved|denied|rejected|not-needed|not-authorized|no|yes|无需|拒绝|已授权)$/.test(raw);
183
+ }
184
+ function missingUserDecisionFields(row) {
185
+ const state = normalizeDecision(row.state);
186
+ const required = state === "authorized" || state === "approved" || state === "yes" || state === "已授权"
187
+ ? [
188
+ ["Decided By", row.decidedBy],
189
+ ["Decided At", row.decidedAt],
190
+ ["Scope", row.scope],
191
+ ["Worktree / Branch", row.worktreeBranch],
192
+ ]
193
+ : [
194
+ ["Decided By", row.decidedBy],
195
+ ["Decided At", row.decidedAt],
196
+ ];
197
+ return required.filter(([, value]) => !isConcreteAuthorizationValue(value)).map(([label]) => label);
198
+ }