coding-agent-harness 1.0.5 → 1.0.7

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 (260) hide show
  1. package/CONTRIBUTING.md +2 -2
  2. package/README.md +63 -3
  3. package/README.zh-CN.md +52 -3
  4. package/SKILL.md +43 -43
  5. package/dist/build-dist.mjs +189 -0
  6. package/dist/check-dist-observation.mjs +428 -0
  7. package/dist/check-harness.mjs +489 -0
  8. package/dist/check-import-graph.mjs +511 -0
  9. package/dist/check-runtime-emit.mjs +304 -0
  10. package/dist/check-type-boundaries.mjs +139 -0
  11. package/dist/commands/dashboard-command.mjs +80 -0
  12. package/dist/commands/migration-command.mjs +152 -0
  13. package/dist/commands/preset-command.mjs +91 -0
  14. package/dist/commands/task-command.mjs +324 -0
  15. package/dist/harness.mjs +304 -0
  16. package/dist/lib/capability-registry.mjs +643 -0
  17. package/dist/lib/check-module-parallel.mjs +227 -0
  18. package/dist/lib/check-profiles.mjs +414 -0
  19. package/dist/lib/check-task-contracts.mjs +54 -0
  20. package/dist/lib/core-shared.mjs +254 -0
  21. package/dist/lib/dashboard-data.mjs +608 -0
  22. package/dist/lib/dashboard-workbench.mjs +334 -0
  23. package/dist/lib/dashboard-writer.mjs +200 -0
  24. package/dist/lib/git-status-summary.mjs +45 -0
  25. package/dist/lib/governance-index-generator.mjs +236 -0
  26. package/dist/lib/governance-sync.mjs +617 -0
  27. package/dist/lib/governance-table-boundary.mjs +161 -0
  28. package/{scripts → dist}/lib/harness-core.mjs +2 -0
  29. package/dist/lib/harness-paths.mjs +338 -0
  30. package/dist/lib/lesson-maintenance.mjs +139 -0
  31. package/dist/lib/markdown-utils.mjs +193 -0
  32. package/dist/lib/migration-planner.mjs +439 -0
  33. package/dist/lib/migration-support.mjs +317 -0
  34. package/dist/lib/phase-kind.mjs +46 -0
  35. package/dist/lib/preset-audit-contracts.mjs +40 -0
  36. package/dist/lib/preset-engine.mjs +516 -0
  37. package/dist/lib/preset-registry.mjs +831 -0
  38. package/dist/lib/preset-resource-contracts.mjs +83 -0
  39. package/dist/lib/review-confirm-git-gate.mjs +244 -0
  40. package/dist/lib/status-builder.mjs +87 -0
  41. package/{scripts → dist}/lib/status-dashboard-renderer.mjs +44 -46
  42. package/dist/lib/structure-migration.mjs +404 -0
  43. package/dist/lib/subagent-authorization-audit.mjs +198 -0
  44. package/dist/lib/task-audit-metadata.mjs +376 -0
  45. package/dist/lib/task-audit-migration.mjs +355 -0
  46. package/dist/lib/task-completion-consistency.mjs +29 -0
  47. package/dist/lib/task-index.mjs +133 -0
  48. package/dist/lib/task-lesson-candidates.mjs +239 -0
  49. package/dist/lib/task-lesson-sedimentation.mjs +300 -0
  50. package/dist/lib/task-lifecycle/create-task-helpers.mjs +84 -0
  51. package/dist/lib/task-lifecycle/phase-sync.mjs +82 -0
  52. package/dist/lib/task-lifecycle/review-confirm.mjs +93 -0
  53. package/dist/lib/task-lifecycle/review-gates.mjs +62 -0
  54. package/dist/lib/task-lifecycle/review-submission.mjs +52 -0
  55. package/dist/lib/task-lifecycle/scaffold-provenance.mjs +54 -0
  56. package/dist/lib/task-lifecycle/template-files.mjs +52 -0
  57. package/dist/lib/task-lifecycle/text-utils.mjs +26 -0
  58. package/dist/lib/task-lifecycle.mjs +611 -0
  59. package/dist/lib/task-metadata.mjs +116 -0
  60. package/dist/lib/task-review-model.mjs +474 -0
  61. package/dist/lib/task-scanner.mjs +439 -0
  62. package/dist/lib/task-tombstone-commands.mjs +125 -0
  63. package/dist/postinstall.mjs +14 -0
  64. package/dist/run-built-tests.mjs +84 -0
  65. package/docs-release/README.md +1 -0
  66. package/docs-release/architecture/overview.md +12 -12
  67. package/docs-release/architecture/overview.zh-CN.md +12 -12
  68. package/docs-release/architecture/system-explainer/01-system-overview.md +15 -14
  69. package/docs-release/architecture/system-explainer/02-module-dependency.md +8 -8
  70. package/docs-release/architecture/system-explainer/03-task-lifecycle.md +3 -3
  71. package/docs-release/architecture/system-explainer/04-check-and-governance.md +9 -7
  72. package/docs-release/architecture/system-explainer/05-data-flow.md +5 -5
  73. package/docs-release/architecture/system-explainer/06-preset-and-migration.md +1 -4
  74. package/docs-release/architecture/system-explainer/en-US/01-system-overview.md +15 -14
  75. package/docs-release/architecture/system-explainer/en-US/02-module-dependency.md +8 -8
  76. package/docs-release/architecture/system-explainer/en-US/03-task-lifecycle.md +3 -3
  77. package/docs-release/architecture/system-explainer/en-US/04-check-and-governance.md +10 -8
  78. package/docs-release/architecture/system-explainer/en-US/05-data-flow.md +5 -5
  79. package/docs-release/architecture/system-explainer/en-US/06-preset-and-migration.md +1 -4
  80. package/docs-release/guides/agent-installation.en-US.md +14 -8
  81. package/docs-release/guides/agent-installation.md +14 -8
  82. package/docs-release/guides/contributing.md +3 -3
  83. package/docs-release/guides/contributing.zh-CN.md +3 -3
  84. package/docs-release/guides/document-audience-and-surfaces.en-US.md +10 -10
  85. package/docs-release/guides/document-audience-and-surfaces.md +10 -10
  86. package/docs-release/guides/legacy-migration-agent-prompt.md +25 -2
  87. package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +25 -2
  88. package/docs-release/guides/migration-playbook.en-US.md +63 -1
  89. package/docs-release/guides/migration-playbook.md +59 -1
  90. package/docs-release/guides/parent-control-repository-pattern.en-US.md +25 -25
  91. package/docs-release/guides/parent-control-repository-pattern.md +25 -25
  92. package/docs-release/guides/preset-development.md +2 -2
  93. package/docs-release/guides/repository-operating-models.en-US.md +21 -21
  94. package/docs-release/guides/repository-operating-models.md +21 -21
  95. package/docs-release/guides/task-state-machine.en-US.md +5 -5
  96. package/docs-release/guides/task-state-machine.md +5 -5
  97. package/docs-release/guides/typescript-runtime-migration-closeout.md +96 -0
  98. package/examples/minimal-project/AGENTS.md +2 -2
  99. package/examples/minimal-project/coding-agent-harness/harness.yaml +14 -0
  100. package/examples/minimal-project/coding-agent-harness/planning/tasks/demo-task/progress.md +11 -0
  101. package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/review.md +1 -1
  102. package/package.json +20 -12
  103. package/presets/legacy-migration/preset.yaml +5 -5
  104. package/presets/legacy-migration/templates/execution_strategy.append.md +1 -1
  105. package/presets/lesson-sedimentation/preset.yaml +3 -3
  106. package/presets/module/preset.yaml +2 -2
  107. package/presets/module/templates/execution_strategy.append.md +1 -1
  108. package/presets/module/templates/task_plan.append.md +3 -3
  109. package/presets/standard-task/preset.yaml +2 -2
  110. package/references/adversarial-review-standard.md +2 -2
  111. package/references/agents-md-pattern.md +14 -14
  112. package/references/cadence-ledger.md +1 -1
  113. package/references/ci-cd-standard.md +1 -1
  114. package/references/delivery-operating-model-standard.md +4 -4
  115. package/references/docs-directory-standard.md +65 -159
  116. package/references/external-source-intake-standard.md +10 -10
  117. package/references/harness-ledger.md +5 -5
  118. package/references/legacy-12-phase-bootstrap.md +2 -2
  119. package/references/lessons-governance.md +15 -15
  120. package/references/long-running-task-standard.md +6 -6
  121. package/references/module-parallel-standard.md +34 -34
  122. package/references/planning-loop.md +6 -6
  123. package/references/project-onboarding-audit.md +4 -4
  124. package/references/regression-system.md +2 -2
  125. package/references/repo-governance-standard.md +4 -4
  126. package/references/review-routing-standard.md +1 -1
  127. package/references/ssot-governance.md +19 -19
  128. package/references/taskr-gap-analysis.md +5 -5
  129. package/references/walkthrough-closeout.md +14 -14
  130. package/references/worktree-parallel.md +3 -3
  131. package/skills/preset-creator/references/complex-task-skeleton/task_plan.md +1 -1
  132. package/skills/preset-creator/references/preset-package-skeleton.md +5 -5
  133. package/templates/AGENTS.md.template +26 -26
  134. package/templates/architecture/README.md +4 -4
  135. package/templates/architecture/service-catalog.md +2 -2
  136. package/templates/architecture/services/service-template.md +1 -1
  137. package/templates/dashboard/assets/app-src/20-overview.js +11 -5
  138. package/templates/dashboard/assets/app-src/40-modules.js +1 -1
  139. package/templates/dashboard/assets/app.js +12 -6
  140. package/templates/dashboard/assets/i18n.js +4 -2
  141. package/templates/development/README.md +10 -10
  142. package/templates/development/cross-repo-debugging.md +3 -3
  143. package/templates/development/external-context/service-template.md +2 -2
  144. package/templates/development/external-source-packs/README.md +4 -4
  145. package/templates/integrations/README.md +4 -4
  146. package/templates/integrations/api-contract.md +2 -2
  147. package/templates/integrations/event-contract.md +2 -2
  148. package/templates/integrations/third-party/vendor-template.md +2 -2
  149. package/templates/integrations/webhook-contract.md +2 -2
  150. package/templates/ledger/Harness-Ledger.md +1 -1
  151. package/templates/planning/INDEX.md +1 -0
  152. package/templates/planning/module_session_prompt.md +1 -1
  153. package/templates/planning/task_plan.md +1 -1
  154. package/templates/planning/walkthrough.md +47 -0
  155. package/templates/reference/docs-library-standard.md +8 -8
  156. package/templates/reference/external-source-intake-standard.md +15 -15
  157. package/templates/reference/repo-governance-standard.md +1 -1
  158. package/templates/ssot/Module-Registry.md +1 -1
  159. package/templates/walkthrough/walkthrough-template.md +2 -2
  160. package/templates-zh-CN/AGENTS.md.template +26 -26
  161. package/templates-zh-CN/CLAUDE.md.template +1 -1
  162. package/templates-zh-CN/architecture/README.md +4 -4
  163. package/templates-zh-CN/architecture/service-catalog.md +2 -2
  164. package/templates-zh-CN/architecture/services/service-template.md +1 -1
  165. package/templates-zh-CN/development/README.md +10 -10
  166. package/templates-zh-CN/development/cross-repo-debugging.md +3 -3
  167. package/templates-zh-CN/development/external-context/service-template.md +2 -2
  168. package/templates-zh-CN/development/external-source-packs/README.md +4 -4
  169. package/templates-zh-CN/integrations/README.md +4 -4
  170. package/templates-zh-CN/integrations/api-contract.md +2 -2
  171. package/templates-zh-CN/integrations/event-contract.md +2 -2
  172. package/templates-zh-CN/integrations/third-party/vendor-template.md +2 -2
  173. package/templates-zh-CN/integrations/webhook-contract.md +2 -2
  174. package/templates-zh-CN/ledger/Harness-Ledger.md +1 -1
  175. package/templates-zh-CN/lessons/lesson-arch-process-change.md +1 -1
  176. package/templates-zh-CN/lessons/lesson-new-doc.md +3 -3
  177. package/templates-zh-CN/lessons/lesson-ref-change.md +4 -4
  178. package/templates-zh-CN/planning/module_session_prompt.md +11 -11
  179. package/templates-zh-CN/planning/walkthrough.md +47 -0
  180. package/templates-zh-CN/reference/adversarial-review-standard.md +2 -2
  181. package/templates-zh-CN/reference/delivery-operating-model-standard.md +3 -3
  182. package/templates-zh-CN/reference/docs-library-standard.md +28 -28
  183. package/templates-zh-CN/reference/execution-workflow-standard.md +1 -1
  184. package/templates-zh-CN/reference/external-source-intake-standard.md +16 -16
  185. package/templates-zh-CN/reference/harness-ledger-standard.md +6 -6
  186. package/templates-zh-CN/reference/regression-ssot-governance.md +2 -2
  187. package/templates-zh-CN/reference/repo-governance-standard.md +1 -1
  188. package/templates-zh-CN/reference/review-routing-standard.md +1 -1
  189. package/templates-zh-CN/reference/walkthrough-standard.md +7 -7
  190. package/templates-zh-CN/reference/worktree-standard.md +1 -1
  191. package/templates-zh-CN/regression/Cadence-Ledger.md +2 -2
  192. package/templates-zh-CN/ssot/Delivery-SSoT.md +3 -3
  193. package/templates-zh-CN/ssot/Module-Registry.md +3 -3
  194. package/templates-zh-CN/ssot/Regression-SSoT.md +2 -2
  195. package/templates-zh-CN/walkthrough/walkthrough-template.md +5 -5
  196. package/tsconfig.dist.json +16 -0
  197. package/tsconfig.json +25 -0
  198. package/tsconfig.runtime.json +24 -0
  199. package/examples/minimal-project/.harness-capabilities.json +0 -8
  200. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/progress.md +0 -11
  201. package/scripts/check-harness.mjs +0 -508
  202. package/scripts/commands/dashboard-command.mjs +0 -67
  203. package/scripts/commands/migration-command.mjs +0 -126
  204. package/scripts/commands/preset-command.mjs +0 -73
  205. package/scripts/commands/task-command.mjs +0 -328
  206. package/scripts/harness.mjs +0 -291
  207. package/scripts/lib/capability-registry.mjs +0 -587
  208. package/scripts/lib/check-module-parallel.mjs +0 -230
  209. package/scripts/lib/check-profiles.mjs +0 -372
  210. package/scripts/lib/check-task-contracts.mjs +0 -55
  211. package/scripts/lib/core-shared.mjs +0 -249
  212. package/scripts/lib/dashboard-data.mjs +0 -520
  213. package/scripts/lib/dashboard-workbench.mjs +0 -336
  214. package/scripts/lib/dashboard-writer.mjs +0 -202
  215. package/scripts/lib/git-status-summary.mjs +0 -46
  216. package/scripts/lib/governance-index-generator.mjs +0 -174
  217. package/scripts/lib/governance-sync.mjs +0 -611
  218. package/scripts/lib/governance-table-boundary.mjs +0 -175
  219. package/scripts/lib/lesson-maintenance.mjs +0 -152
  220. package/scripts/lib/markdown-utils.mjs +0 -191
  221. package/scripts/lib/migration-planner.mjs +0 -476
  222. package/scripts/lib/migration-support.mjs +0 -312
  223. package/scripts/lib/phase-kind.mjs +0 -50
  224. package/scripts/lib/preset-audit-contracts.mjs +0 -37
  225. package/scripts/lib/preset-engine.mjs +0 -494
  226. package/scripts/lib/preset-registry.mjs +0 -776
  227. package/scripts/lib/preset-resource-contracts.mjs +0 -83
  228. package/scripts/lib/review-confirm-git-gate.mjs +0 -248
  229. package/scripts/lib/status-builder.mjs +0 -88
  230. package/scripts/lib/subagent-authorization-audit.mjs +0 -196
  231. package/scripts/lib/task-audit-metadata.mjs +0 -385
  232. package/scripts/lib/task-audit-migration.mjs +0 -350
  233. package/scripts/lib/task-completion-consistency.mjs +0 -26
  234. package/scripts/lib/task-index.mjs +0 -93
  235. package/scripts/lib/task-lesson-candidates.mjs +0 -242
  236. package/scripts/lib/task-lesson-sedimentation.mjs +0 -326
  237. package/scripts/lib/task-lifecycle/create-task-helpers.mjs +0 -67
  238. package/scripts/lib/task-lifecycle/phase-sync.mjs +0 -88
  239. package/scripts/lib/task-lifecycle/review-confirm.mjs +0 -112
  240. package/scripts/lib/task-lifecycle/review-gates.mjs +0 -73
  241. package/scripts/lib/task-lifecycle/review-submission.mjs +0 -63
  242. package/scripts/lib/task-lifecycle/scaffold-provenance.mjs +0 -49
  243. package/scripts/lib/task-lifecycle/template-files.mjs +0 -53
  244. package/scripts/lib/task-lifecycle/text-utils.mjs +0 -24
  245. package/scripts/lib/task-lifecycle.mjs +0 -616
  246. package/scripts/lib/task-metadata.mjs +0 -118
  247. package/scripts/lib/task-review-model.mjs +0 -455
  248. package/scripts/lib/task-scanner.mjs +0 -503
  249. package/scripts/lib/task-tombstone-commands.mjs +0 -140
  250. package/scripts/postinstall.mjs +0 -14
  251. package/templates/walkthrough/Closeout-SSoT.md +0 -43
  252. package/templates-zh-CN/walkthrough/Closeout-SSoT.md +0 -42
  253. /package/examples/minimal-project/{docs → coding-agent-harness/governance/generated}/Harness-Ledger.md +0 -0
  254. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/INDEX.md +0 -0
  255. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/brief.md +0 -0
  256. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/execution_strategy.md +0 -0
  257. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/findings.md +0 -0
  258. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/lesson_candidates.md +0 -0
  259. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/task_plan.md +0 -0
  260. /package/examples/minimal-project/{docs/09-PLANNING/TASKS → coding-agent-harness/planning/tasks}/demo-task/visual_map.md +0 -0
@@ -1,616 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import crypto from "node:crypto";
4
- import {
5
- visualMapFile,
6
- legacyVisualRoadmapFile,
7
- lessonCandidatesFile,
8
- allowedTaskStates,
9
- allowedTaskBudgets,
10
- allowedPhaseStates,
11
- allowedEvidenceStatus,
12
- normalizeTarget,
13
- normalizeLocale,
14
- toPosix,
15
- readFileSafe,
16
- readBundledTemplate,
17
- todayDate,
18
- localDate,
19
- datePrefix,
20
- normalizeTaskId,
21
- renderTaskTemplate,
22
- } from "./core-shared.mjs";
23
- import { readCapabilityRegistry } from "./capability-registry.mjs";
24
- import { readPresetPackage } from "./preset-registry.mjs";
25
- import {
26
- assertPresetWriteScope,
27
- buildPresetContext,
28
- evaluateTemplateValues,
29
- resolvePresetInputs,
30
- renderPresetResourceIndex,
31
- renderPresetTaskTemplate,
32
- } from "./preset-engine.mjs";
33
- import {
34
- collectTasks,
35
- listTaskPlanPaths,
36
- parseTaskBudget,
37
- taskIdForDirectory,
38
- } from "./task-scanner.mjs";
39
- import { getColumn, firstColumn, updateMarkdownTableRow } from "./markdown-utils.mjs";
40
- import { validateLifecycleTransition, validateReviewEntryGate } from "./task-lifecycle/review-gates.mjs";
41
- import { advanceLifecyclePhase, autoRecordNoLessonCandidateDecision } from "./task-lifecycle/phase-sync.mjs";
42
- import { confirmTaskReview as confirmTaskReviewWithContext } from "./task-lifecycle/review-confirm.mjs";
43
- import { appendProgressLog } from "./task-lifecycle/text-utils.mjs";
44
- import { buildScaffoldProvenance } from "./task-lifecycle/scaffold-provenance.mjs";
45
- import { buildCreationTaskAudit } from "./task-audit-metadata.mjs";
46
- import {
47
- renderAgentReviewSubmission,
48
- replaceAgentReviewSubmission,
49
- } from "./task-lifecycle/review-submission.mjs";
50
- import {
51
- appendLongRunningContractFile,
52
- moduleTemplateFiles,
53
- taskFilesForBudget,
54
- } from "./task-lifecycle/template-files.mjs";
55
- import {
56
- planCreateTaskChanges,
57
- refreshPresetCommandAudit,
58
- } from "./task-lifecycle/create-task-helpers.mjs";
59
- import {
60
- beginGovernanceSync,
61
- commitGovernanceSync,
62
- governanceRelativePaths,
63
- releaseGovernanceSync,
64
- syncModuleStepGovernance,
65
- syncTaskGovernance,
66
- } from "./governance-sync.mjs";
67
-
68
- function taskRoot(target, taskId, { moduleKey = "" } = {}) {
69
- const normalizedTaskId = normalizeTaskId(taskId);
70
- if (moduleKey) return path.join(target.docsRoot, "09-PLANNING/MODULES", normalizeTaskId(moduleKey), normalizedTaskId);
71
- return path.join(target.docsRoot, "09-PLANNING/TASKS", normalizedTaskId);
72
- }
73
-
74
- export function resolveTaskDirectory(target, taskRef) {
75
- const raw = String(taskRef || "").replace(/^docs\/09-PLANNING\//, "").replace(/^\/+/, "");
76
- if (!raw) throw new Error("Missing task id");
77
- const direct = raw.startsWith("TASKS/") || raw.startsWith("MODULES/") ? path.join(target.docsRoot, "09-PLANNING", raw) : "";
78
- if (direct && fs.existsSync(path.join(direct, "task_plan.md"))) return direct;
79
- const normalized = normalizeTaskId(raw);
80
- const candidates = listTaskPlanPaths(target)
81
- .map((taskPlanPath) => path.dirname(taskPlanPath))
82
- .filter((taskDir) => {
83
- const id = taskIdForDirectory(target, taskDir);
84
- const dirName = path.basename(taskDir);
85
- return id === raw || id.endsWith(`/${raw}`) || dirName === normalized;
86
- });
87
- if (candidates.length === 1) return candidates[0];
88
- if (candidates.length > 1) {
89
- const options = candidates.map((taskDir) => `- ${taskIdForDirectory(target, taskDir)}`).join("\n");
90
- throw new Error(`Ambiguous task reference: ${taskRef}\n${options}`);
91
- }
92
- // Try bare slug resolution: match normalized slug against dated directories
93
- if (!datePrefix.test(normalized)) {
94
- const datedCandidates = listTaskPlanPaths(target)
95
- .map((taskPlanPath) => path.dirname(taskPlanPath))
96
- .filter((taskDir) => {
97
- const dirName = path.basename(taskDir);
98
- return datePrefix.test(dirName) && dirName.replace(datePrefix, "") === normalized;
99
- });
100
- if (datedCandidates.length === 1) return datedCandidates[0];
101
- if (datedCandidates.length > 1) {
102
- const options = datedCandidates.map((taskDir) => `- ${taskIdForDirectory(target, taskDir)}`).join("\n");
103
- throw new Error(`Ambiguous task reference: ${taskRef}\n${options}`);
104
- }
105
- }
106
- const legacy = taskRoot(target, normalized);
107
- if (fs.existsSync(path.join(legacy, "task_plan.md"))) return legacy;
108
- throw new Error(`Task not found: ${taskRef}`);
109
- }
110
-
111
- function findTaskByDirectory(target, taskDir) {
112
- const id = taskIdForDirectory(target, taskDir);
113
- return collectTasks(target).find((task) => task.id === id) || null;
114
- }
115
-
116
- function stateLabel(state, locale) {
117
- if (normalizeLocale(locale) !== "zh-CN") return state;
118
- return (
119
- {
120
- not_started: "未开始",
121
- planned: "未开始",
122
- in_progress: "进行中",
123
- review: "审查中",
124
- blocked: "已阻塞",
125
- done: "已完成",
126
- }[state] || state
127
- );
128
- }
129
-
130
- function normalizeTaskBudgetInput(budget) {
131
- const normalized = String(budget || "standard").trim().toLowerCase().replaceAll("_", "-");
132
- if (allowedTaskBudgets.has(normalized)) return normalized;
133
- throw new Error(`Invalid task budget: ${budget}. Expected one of: simple, standard, complex`);
134
- }
135
-
136
- function normalizeTaskPresetInput(preset, { targetInput = "" } = {}) {
137
- const normalized = String(preset || "none").trim().toLowerCase().replaceAll("_", "-");
138
- if (!normalized || normalized === "none") return "none";
139
- return readPresetPackage(normalized, { targetInput }).id;
140
- }
141
-
142
- function updateProgressState(content, state, locale) {
143
- const label = stateLabel(state, locale);
144
- if (/^##\s*状态[::][^\n]*/im.test(content)) {
145
- return content.replace(/^##\s*状态[::][^\n]*/im, `## 状态:${label}`);
146
- }
147
- if (/^##\s*(?:Current Status|Status)\s*\n+\s*[^\n]+/im.test(content)) {
148
- return content.replace(/^##\s*(Current Status|Status)\s*\n+\s*[^\n]+/im, `## $1\n\n${label}`);
149
- }
150
- return `${content.trimEnd()}\n\n## Status\n\n${label}\n`;
151
- }
152
-
153
- function ensureDatePrefix(slug) {
154
- if (datePrefix.test(slug)) return slug;
155
- return `${localDate()}-${slug}`;
156
- }
157
-
158
- function bareSlug(datedId) {
159
- if (datePrefix.test(datedId)) return datedId.replace(datePrefix, "");
160
- return datedId;
161
- }
162
-
163
- function automaticTaskSlug(seed) {
164
- return normalizeTaskId(seed || "task").slice(0, 48).replace(/-+$/g, "") || "task";
165
- }
166
-
167
- function randomTaskSuffix() {
168
- return crypto.randomBytes(4).toString("hex");
169
- }
170
-
171
- function resolveTaskIdentity({ target, taskId, title, presetPackage, moduleKey, automaticTaskId }) {
172
- if (!automaticTaskId) {
173
- const rawNormalized = normalizeTaskId(taskId || (presetPackage?.task?.defaultTaskId || ""));
174
- const normalizedTaskId = ensureDatePrefix(rawNormalized);
175
- if (!normalizedTaskId) throw new Error("Missing task id");
176
- return { normalizedTaskId, semanticSlug: bareSlug(normalizedTaskId) };
177
- }
178
-
179
- const semanticSlug = automaticTaskSlug(title || presetPackage?.task?.defaultTaskId || "task");
180
- for (let attempt = 0; attempt < 8; attempt += 1) {
181
- const normalizedTaskId = `${localDate()}-${semanticSlug}-${randomTaskSuffix()}`;
182
- if (!fs.existsSync(taskRoot(target, normalizedTaskId, { moduleKey }))) return { normalizedTaskId, semanticSlug };
183
- }
184
- throw new Error(`Unable to allocate automatic task id for: ${semanticSlug}`);
185
- }
186
-
187
- export function createTask(targetInput, taskId, { title = "", locale = "en-US", dryRun = false, moduleKey = "", budget = "standard", longRunning = false, preset = "", fromSession = "", presetArgs = [], automaticTaskId = false } = {}) {
188
- const requestedPreset = preset || (moduleKey ? "module" : "");
189
- const normalizedPreset = normalizeTaskPresetInput(requestedPreset, { targetInput });
190
- const presetPackage = normalizedPreset === "none" ? null : readPresetPackage(normalizedPreset, { targetInput });
191
- const presetInputs = presetPackage ? resolvePresetInputs(presetPackage, { cliArgs: presetArgs, fromSession, targetInput }) : null;
192
- const target = normalizeTarget(presetInputs?.targetInput || targetInput);
193
- if (presetInputs?.targetInput && targetInput && targetInput !== "." && path.resolve(targetInput) !== path.resolve(presetInputs.targetInput)) {
194
- throw new Error(`--from-session target mismatch: session target is ${presetInputs.targetInput}`);
195
- }
196
- const normalizedBudget = normalizeTaskBudgetInput(budget);
197
- if (presetPackage && !presetPackage.compatibleBudgets.includes(normalizedBudget)) throw new Error(`${normalizedPreset} preset requires --budget ${presetPackage.compatibleBudgets.join("|")}`);
198
- if (presetPackage?.task?.projectLevelOnly === true && moduleKey) throw new Error(`${normalizedPreset} preset is project-level and cannot be combined with --module`);
199
- if (presetPackage?.task?.requiresFromSession === true && !fromSession) throw new Error(`${normalizedPreset} preset requires --from-session`);
200
- const normalizedModuleKey = moduleKey ? normalizeTaskId(moduleKey) : "";
201
- const identity = resolveTaskIdentity({ target, taskId, title, presetPackage, moduleKey: normalizedModuleKey, automaticTaskId });
202
- const normalizedTaskId = identity.normalizedTaskId;
203
- const semanticSlug = identity.semanticSlug;
204
- const normalizedLocale = normalizeLocale(locale || readCapabilityRegistry(target).locale);
205
- const taskTitle = title || (normalizedPreset === "legacy-migration" ? "Harness v1 legacy migration" : semanticSlug);
206
- const directory = taskRoot(target, normalizedTaskId, { moduleKey: normalizedModuleKey });
207
- if (fs.existsSync(directory)) throw new Error(`Task already exists: ${normalizedTaskId}`);
208
- const scaffoldProvenance = buildScaffoldProvenance({
209
- taskId,
210
- normalizedTaskId,
211
- title,
212
- locale: normalizedLocale,
213
- budget: normalizedBudget,
214
- longRunning,
215
- moduleKey: normalizedModuleKey,
216
- preset: normalizedPreset,
217
- fromSession,
218
- targetInput: presetInputs?.targetInput || targetInput,
219
- automaticTaskId,
220
- });
221
- const baseTaskAudit = buildCreationTaskAudit(scaffoldProvenance, { projectRoot: target.projectRoot });
222
- const evaluatedPresetValues = presetPackage ? evaluateTemplateValues(presetPackage, presetInputs.inputs, { taskId: normalizedTaskId, taskTitle, moduleKey: normalizedModuleKey }) : null;
223
- const presetContext = presetPackage
224
- ? buildPresetContext({ ...presetPackage, task: { ...(presetPackage.task || {}), kind: presetPackage.task?.kind || "general" } }, {
225
- target,
226
- taskDir: directory,
227
- taskId: normalizedTaskId,
228
- taskTitle,
229
- resolvedInputs: presetInputs.inputs,
230
- evaluatedValues: evaluatedPresetValues,
231
- })
232
- : null;
233
- const task = {
234
- id: taskIdForDirectory(target, directory),
235
- shortId: normalizedTaskId,
236
- title: taskTitle,
237
- module: normalizedModuleKey || null,
238
- path: `TARGET:${toPosix(path.relative(target.projectRoot, directory))}`,
239
- locale: normalizedLocale,
240
- budget: normalizedBudget,
241
- kind: presetContext?.kind || "general",
242
- preset: normalizedPreset,
243
- presetVersion: presetContext?.presetVersion || "",
244
- presetAudit: presetContext?.audit || null,
245
- migrationTargetLevel: presetContext?.migrationTargetLevel || "",
246
- migrationAchievedLevel: presetContext?.migrationAchievedLevel || "",
247
- evidenceBundle: presetContext?.evidenceBundle || "",
248
- longRunning,
249
- };
250
- const plannedChanges = planCreateTaskChanges({
251
- target,
252
- directory,
253
- normalizedModuleKey,
254
- normalizedLocale,
255
- normalizedBudget,
256
- longRunning,
257
- presetContext,
258
- task,
259
- });
260
- const plannedGovernance = syncTaskGovernance(target, task, { event: "new-task", state: "planned", message: "task registered by CLI", dryRun: true });
261
- const plannedWriteScopes = governanceRelativePaths([...plannedChanges, ...plannedGovernance.changes]);
262
- const changes = [];
263
- const governanceContext = beginGovernanceSync(target, { operation: `new-task ${normalizedTaskId}`, dryRun, allowDirtyWorktree: true, allowedRelativePaths: plannedWriteScopes });
264
- try {
265
- if (normalizedModuleKey) {
266
- const moduleDirectory = path.dirname(directory);
267
- for (const [destination, source] of moduleTemplateFiles({ locale: normalizedLocale })) {
268
- const destinationPath = path.join(moduleDirectory, destination);
269
- if (fs.existsSync(destinationPath)) continue;
270
- changes.push({
271
- destination: toPosix(path.relative(target.projectRoot, destinationPath)),
272
- source,
273
- action: dryRun ? "would-create" : "create",
274
- });
275
- if (presetPackage) assertPresetWriteScope(presetPackage, toPosix(path.relative(target.projectRoot, destinationPath)));
276
- if (dryRun) continue;
277
- fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
278
- fs.writeFileSync(
279
- destinationPath,
280
- renderTaskTemplate(readBundledTemplate(source), {
281
- taskId: normalizedModuleKey,
282
- title: normalizedModuleKey,
283
- locale: normalizedLocale,
284
- budget: normalizedBudget,
285
- moduleKey: normalizedModuleKey,
286
- preset: normalizedPreset,
287
- presetVersion: presetContext?.presetVersion || "",
288
- evidenceBundle: presetContext?.evidenceBundle || "",
289
- longRunning,
290
- scaffoldProvenance,
291
- taskAudit: buildCreationTaskAudit({ ...scaffoldProvenance, templateSource: source }, { projectRoot: target.projectRoot }),
292
- }),
293
- );
294
- }
295
- }
296
- const files = appendLongRunningContractFile(taskFilesForBudget({ budget: normalizedBudget, locale: normalizedLocale }), {
297
- locale: normalizedLocale,
298
- longRunning,
299
- });
300
- for (const [destination, source] of files) {
301
- const destinationPath = path.join(directory, destination);
302
- changes.push({
303
- destination: toPosix(path.relative(target.projectRoot, destinationPath)),
304
- source,
305
- action: dryRun ? "would-create" : "create",
306
- });
307
- if (presetPackage) assertPresetWriteScope(presetPackage, toPosix(path.relative(target.projectRoot, destinationPath)));
308
- if (dryRun) continue;
309
- fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
310
- fs.writeFileSync(
311
- destinationPath,
312
- renderPresetTaskTemplate(destination, renderTaskTemplate(readBundledTemplate(source), {
313
- taskId: normalizedTaskId,
314
- title: taskTitle,
315
- locale: normalizedLocale,
316
- budget: normalizedBudget,
317
- moduleKey: normalizedModuleKey,
318
- preset: normalizedPreset,
319
- presetVersion: presetContext?.presetVersion || "",
320
- evidenceBundle: presetContext?.evidenceBundle || "",
321
- longRunning,
322
- scaffoldProvenance: {
323
- ...scaffoldProvenance,
324
- templateSource: source,
325
- },
326
- taskAudit: destination === "INDEX.md"
327
- ? buildCreationTaskAudit({ ...scaffoldProvenance, templateSource: source }, { projectRoot: target.projectRoot })
328
- : baseTaskAudit,
329
- }), presetContext),
330
- );
331
- }
332
- if (presetContext) {
333
- for (const evidence of presetContext.evidenceFiles) {
334
- const destinationPath = path.join(target.projectRoot, evidence.relativePath);
335
- changes.push({
336
- destination: toPosix(evidence.relativePath),
337
- source: evidence.source,
338
- action: dryRun ? "would-create" : "create",
339
- });
340
- assertPresetWriteScope(presetPackage, toPosix(evidence.relativePath));
341
- if (dryRun) continue;
342
- fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
343
- fs.writeFileSync(destinationPath, evidence.content);
344
- }
345
- for (const resource of presetContext.resourceFiles || []) {
346
- const destinationPath = path.join(target.projectRoot, resource.relativePath);
347
- changes.push({
348
- destination: toPosix(resource.relativePath),
349
- source: resource.source,
350
- action: dryRun ? "would-create" : "create",
351
- });
352
- assertPresetWriteScope(presetPackage, toPosix(resource.relativePath));
353
- if (dryRun) continue;
354
- fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
355
- fs.writeFileSync(destinationPath, resource.content);
356
- }
357
- for (const [kind, rows] of Object.entries(presetContext.resourceIndexRows || {})) {
358
- if (!rows.length) continue;
359
- const destination = kind === "references" ? "references/INDEX.md" : "artifacts/INDEX.md";
360
- const destinationPath = path.join(directory, destination);
361
- const relativePath = toPosix(path.relative(target.projectRoot, destinationPath));
362
- changes.push({
363
- destination: relativePath,
364
- source: `preset-${kind}-index`,
365
- action: dryRun ? "would-update" : "update",
366
- });
367
- assertPresetWriteScope(presetPackage, relativePath);
368
- if (dryRun) continue;
369
- fs.mkdirSync(path.dirname(destinationPath), { recursive: true });
370
- const existing = fs.existsSync(destinationPath) ? fs.readFileSync(destinationPath, "utf8") : "";
371
- fs.writeFileSync(destinationPath, renderPresetResourceIndex(existing, kind, rows));
372
- }
373
- }
374
- const governance = syncTaskGovernance(target, task, { event: "new-task", state: "planned", message: "task registered by CLI", dryRun });
375
- changes.push(...governance.changes);
376
- const commandWriteScopes = governanceRelativePaths(changes);
377
- if (presetContext) {
378
- refreshPresetCommandAudit(target, presetContext, { commandWriteScopes, dryRun });
379
- task.presetAudit = presetContext.audit;
380
- }
381
- const commit = commitGovernanceSync(governanceContext, commandWriteScopes, {
382
- message: `chore(harness): register task ${task.id}`,
383
- });
384
- return {
385
- dryRun,
386
- task,
387
- changes,
388
- governance: { ...governance, commit },
389
- };
390
- } finally {
391
- releaseGovernanceSync(governanceContext);
392
- }
393
- }
394
-
395
- export function updateTaskLifecycle(targetInput, taskId, { event = "task-log", state = "", message = "", evidence = "" } = {}) {
396
- const target = normalizeTarget(targetInput);
397
- const taskDir = resolveTaskDirectory(target, taskId);
398
- const progressPath = path.join(taskDir, "progress.md");
399
- const registry = readCapabilityRegistry(target);
400
- const normalizedState = state ? String(state).toLowerCase().replaceAll("-", "_") : "";
401
- if (normalizedState && !allowedTaskStates.has(normalizedState)) throw new Error(`Invalid task state: ${state}`);
402
- const currentTask = findTaskByDirectory(target, taskDir);
403
- const canonicalTaskId = taskIdForDirectory(target, taskDir);
404
- const budget = parseTaskBudget(readFileSafe(path.join(taskDir, "task_plan.md")));
405
- validateLifecycleTransition({
406
- event,
407
- currentState: currentTask?.state || "unknown",
408
- budget,
409
- reviewContent: readFileSafe(path.join(taskDir, "review.md")),
410
- indexContent: readFileSafe(path.join(taskDir, "INDEX.md")),
411
- reviewTaskKey: canonicalTaskId,
412
- projectRoot: target.projectRoot,
413
- taskDir,
414
- });
415
- if (event === "task-review") validateReviewEntryGate(taskDir, budget);
416
- const governanceContext = beginGovernanceSync(target, { operation: `${event} ${canonicalTaskId}` });
417
- try {
418
- let content = readFileSafe(progressPath);
419
- if (normalizedState) content = updateProgressState(content, normalizedState, registry.locale);
420
- content = appendProgressLog(content, { event, message, evidence });
421
- fs.writeFileSync(progressPath, content.endsWith("\n") ? content : `${content}\n`);
422
- const allowedPaths = [toPosix(path.relative(target.projectRoot, progressPath))];
423
- const advancedPhasePath = advanceLifecyclePhase(target, taskDir, event);
424
- if (advancedPhasePath) allowedPaths.push(advancedPhasePath);
425
- if (event === "task-review") {
426
- const reviewPath = path.join(taskDir, "review.md");
427
- const reviewContent = readFileSafe(reviewPath);
428
- fs.writeFileSync(
429
- reviewPath,
430
- replaceAgentReviewSubmission(
431
- reviewContent,
432
- renderAgentReviewSubmission({
433
- target,
434
- taskDir,
435
- canonicalTaskId,
436
- message,
437
- evidence,
438
- }),
439
- ),
440
- );
441
- allowedPaths.push(toPosix(path.relative(target.projectRoot, reviewPath)));
442
- const lessonDecisionPath = autoRecordNoLessonCandidateDecision(target, taskDir);
443
- if (lessonDecisionPath) allowedPaths.push(lessonDecisionPath);
444
- }
445
- const task =
446
- findTaskByDirectory(target, taskDir) ||
447
- {
448
- id: canonicalTaskId,
449
- shortId: path.basename(taskDir),
450
- title: canonicalTaskId,
451
- path: `TARGET:${toPosix(path.relative(target.projectRoot, taskDir))}`,
452
- state: normalizedState || currentTask?.state || "unknown",
453
- };
454
- const governanceState = normalizedState || task.state || currentTask?.state || "planned";
455
- const governance = syncTaskGovernance(target, task, { event, state: governanceState, message, dryRun: false });
456
- const commit = commitGovernanceSync(governanceContext, [...allowedPaths, ...governanceRelativePaths(governance.changes)], {
457
- message: `chore(harness): advance task ${canonicalTaskId} to ${governanceState}`,
458
- });
459
- return {
460
- event,
461
- task,
462
- governance: { ...governance, commit },
463
- };
464
- } finally {
465
- releaseGovernanceSync(governanceContext);
466
- }
467
- }
468
-
469
- export function confirmTaskReview(targetInput, taskId, { reviewer = "Human Reviewer", message = "", confirmText = "", evidence = "" } = {}) {
470
- const target = normalizeTarget(targetInput);
471
- const taskDir = resolveTaskDirectory(target, taskId);
472
- return confirmTaskReviewWithContext({ target, taskDir, findTaskByDirectory }, { reviewer, message, confirmText, evidence });
473
- }
474
- export function updateTaskPhase(targetInput, taskId, phaseId, { state = "", completion = "", evidenceStatus = "" } = {}) {
475
- const target = normalizeTarget(targetInput);
476
- const taskDir = resolveTaskDirectory(target, taskId);
477
- const visualMapPath = path.join(taskDir, visualMapFile);
478
- const legacyPath = path.join(taskDir, legacyVisualRoadmapFile);
479
- if (!fs.existsSync(visualMapPath)) {
480
- if (fs.existsSync(legacyPath)) throw new Error(`Task has legacy visual_roadmap.md only; rewrite it to visual_map.md before task-phase: ${taskId}`);
481
- throw new Error(`Task visual map not found: ${taskId}`);
482
- }
483
- let content = readFileSafe(visualMapPath);
484
- const normalizedState = state ? String(state).toLowerCase().replaceAll("-", "_") : "";
485
- if (normalizedState && !allowedPhaseStates.has(normalizedState)) throw new Error(`Invalid phase state: ${state}`);
486
- const normalizedEvidence = evidenceStatus ? String(evidenceStatus).toLowerCase() : "";
487
- if (normalizedEvidence && !allowedEvidenceStatus.has(normalizedEvidence)) throw new Error(`Invalid evidence status: ${evidenceStatus}`);
488
- const nextCompletion = completion === "" ? "" : Number.parseInt(String(completion), 10);
489
- if (nextCompletion !== "" && (!Number.isInteger(nextCompletion) || nextCompletion < 0 || nextCompletion > 100)) {
490
- throw new Error(`Invalid completion: ${completion}`);
491
- }
492
- const phaseUpdate = updateMarkdownTableRow(content, /^Phase ID$/i, (header, row) => {
493
- const idIndex = getColumn(header, "Phase ID");
494
- if ((row[idIndex] || "") !== phaseId) return null;
495
- const next = [...row];
496
- const stateIndex = getColumn(header, "State");
497
- const completionIndex = getColumn(header, "Completion");
498
- const evidenceIndex = getColumn(header, "Evidence Status");
499
- if (normalizedState && stateIndex >= 0) next[stateIndex] = normalizedState;
500
- if (nextCompletion !== "" && completionIndex >= 0) next[completionIndex] = String(nextCompletion);
501
- if (normalizedEvidence && evidenceIndex >= 0) next[evidenceIndex] = normalizedEvidence;
502
- return next;
503
- });
504
- if (!phaseUpdate.matched) throw new Error(`Phase not found: ${phaseId}`);
505
- const governanceContext = beginGovernanceSync(target, { operation: `task-phase ${taskId} ${phaseId}` });
506
- try {
507
- content = phaseUpdate.content;
508
- fs.writeFileSync(visualMapPath, content);
509
- const commit = commitGovernanceSync(governanceContext, [toPosix(path.relative(target.projectRoot, visualMapPath))], {
510
- message: `chore(harness): update task phase ${taskId} ${phaseId}`,
511
- });
512
- return { event: "task-phase", task: findTaskByDirectory(target, taskDir), phaseId, governance: { commit } };
513
- } finally {
514
- releaseGovernanceSync(governanceContext);
515
- }
516
- }
517
-
518
- export function updateModuleStep(targetInput, moduleKey, stepId, { state = "" } = {}) {
519
- const target = normalizeTarget(targetInput);
520
- const normalizedModuleKey = normalizeTaskId(moduleKey);
521
- const normalizedState = String(state || "done").toLowerCase().replaceAll("_", "-");
522
- if (!["planned", "in-progress", "done", "blocked", "superseded"].includes(normalizedState)) throw new Error(`Invalid module step state: ${state}`);
523
- const modulePlanPath = path.join(target.docsRoot, "09-PLANNING/MODULES", normalizedModuleKey, "module_plan.md");
524
- if (!fs.existsSync(modulePlanPath)) throw new Error(`Module plan not found: ${normalizedModuleKey}`);
525
- let content = readFileSafe(modulePlanPath);
526
- const stepUpdate = updateMarkdownTableRow(content, /^(Step ID|步骤 ID)$/i, (header, row) => {
527
- const idIndex = firstColumn(header, ["Step ID", "步骤 ID"]);
528
- if ((row[idIndex] || "") !== stepId) return null;
529
- const next = [...row];
530
- const statusIndex = firstColumn(header, ["Status", "状态"]);
531
- if (statusIndex >= 0) next[statusIndex] = normalizedState;
532
- return next;
533
- });
534
- if (!stepUpdate.matched) throw new Error(`Module step not found: ${stepId}`);
535
- const governanceContext = beginGovernanceSync(target, { operation: `module-step ${normalizedModuleKey} ${stepId}` });
536
- try {
537
- content = stepUpdate.content;
538
- fs.writeFileSync(modulePlanPath, content);
539
-
540
- const registryPath = path.join(target.docsRoot, "09-PLANNING/Module-Registry.md");
541
- if (fs.existsSync(registryPath)) {
542
- let registry = readFileSafe(registryPath);
543
- const registryUpdate = updateMarkdownTableRow(registry, /^(ID|模块 Key)$/i, (header, row) => {
544
- const moduleIndex = firstColumn(header, ["Module", "模块", "模块 Key"]);
545
- const taskPlanIndex = getColumn(header, "Task Plan");
546
- const matchesModule = normalizeTaskId(row[moduleIndex] || "") === normalizedModuleKey;
547
- const matchesPlan = taskPlanIndex >= 0 && String(row[taskPlanIndex] || "").includes(`/MODULES/${normalizedModuleKey}/`);
548
- if (!matchesModule && !matchesPlan) return null;
549
- const next = [...row];
550
- const statusIndex = firstColumn(header, ["Status", "状态"]);
551
- const updatedIndex = firstColumn(header, ["Updated", "更新时间"]);
552
- const currentStepIndex = firstColumn(header, ["Current Step", "当前步骤"]);
553
- const chineseRegistry = header.some((cell) => /模块 Key|模块名称|状态|更新时间/.test(cell));
554
- if (statusIndex >= 0) {
555
- next[statusIndex] = normalizedState === "done"
556
- ? chineseRegistry ? "completed" : "merged"
557
- : normalizedState === "in-progress" ? chineseRegistry ? "in-progress" : "active" : normalizedState;
558
- }
559
- if (currentStepIndex >= 0) next[currentStepIndex] = stepId;
560
- if (updatedIndex >= 0) next[updatedIndex] = todayDate();
561
- return next;
562
- });
563
- registry = registryUpdate.content;
564
- fs.writeFileSync(registryPath, registry);
565
- }
566
- const governance = syncModuleStepGovernance(target, { moduleKey: normalizedModuleKey, stepId, state: normalizedState });
567
- const commit = commitGovernanceSync(
568
- governanceContext,
569
- [
570
- toPosix(path.relative(target.projectRoot, modulePlanPath)),
571
- toPosix(path.relative(target.projectRoot, registryPath)),
572
- ...governanceRelativePaths(governance.changes),
573
- ],
574
- { message: `chore(harness): update module ${normalizedModuleKey} step ${stepId}` },
575
- );
576
- return { event: "module-step", moduleKey: normalizedModuleKey, stepId, state: normalizedState, governance: { ...governance, commit } };
577
- } finally {
578
- releaseGovernanceSync(governanceContext);
579
- }
580
- }
581
-
582
- export function listLifecycleTasks(targetInput, { state = "", moduleKey = "", queue = "", preset = "", review = "", lesson = "", search = "", missingMaterials = false } = {}) {
583
- const target = normalizeTarget(targetInput);
584
- let tasks = collectTasks(target);
585
- if (state) tasks = tasks.filter((task) => task.state === String(state).toLowerCase().replaceAll("-", "_"));
586
- if (moduleKey) tasks = tasks.filter((task) => task.module === normalizeTaskId(moduleKey));
587
- if (queue) {
588
- const normalizedQueue = queryToken(queue);
589
- tasks = tasks.filter((task) => (task.taskQueues || []).map(queryToken).includes(normalizedQueue));
590
- }
591
- if (preset) tasks = tasks.filter((task) => queryToken(task.taskPreset || "none") === queryToken(preset));
592
- if (review) tasks = tasks.filter((task) => queryToken(task.reviewStatus || "") === queryToken(review));
593
- if (lesson) {
594
- const needle = queryToken(lesson);
595
- tasks = tasks.filter((task) => [task.lessonCandidateStatus, task.lessonCandidateReviewDecision, task.lessonCandidatePromotionState].some((value) => queryToken(value) === needle));
596
- }
597
- if (missingMaterials) tasks = tasks.filter((task) => !task.materialsReady);
598
- if (search) {
599
- const needle = String(search).toLowerCase();
600
- tasks = tasks.filter((task) => [
601
- task.id,
602
- task.taskKey,
603
- task.shortId,
604
- task.title,
605
- task.currentPath,
606
- task.taskPlanPath,
607
- task.module,
608
- task.inferredModule,
609
- ].some((value) => String(value || "").toLowerCase().includes(needle)));
610
- }
611
- return { tasks };
612
- }
613
-
614
- function queryToken(value) {
615
- return String(value || "").trim().toLowerCase().replaceAll("_", "-");
616
- }