coding-agent-harness 1.0.8 → 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 (232) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/CONTRIBUTING.md +8 -4
  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 +19 -6
  7. package/dist/check-dist-observation.mjs +57 -29
  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 +7 -7
  12. package/dist/check-runtime-emit.mjs +10 -3
  13. package/dist/check-type-boundaries.mjs +51 -9
  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 +51 -12
  18. package/dist/commands/registry.mjs +483 -0
  19. package/dist/commands/task-command.mjs +109 -52
  20. package/dist/harness.mjs +6 -304
  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 +4 -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 +67 -29
  44. package/dist/lib/preset-registry.mjs +361 -71
  45. package/dist/lib/preset-runner.mjs +292 -26
  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 +3 -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 +116 -160
  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 +36 -17
  70. package/dist/lib/task-scanner.mjs +74 -23
  71. package/dist/lib/task-template-materials.mjs +131 -0
  72. package/dist/lib/task-tombstone-commands.mjs +186 -29
  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 +1 -0
  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 +16 -12
  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 +6 -1
  120. package/presets/release-closeout/preset.yaml +9 -9
  121. package/presets/release-closeout/scripts/generate-release-package.mjs +387 -25
  122. package/presets/release-closeout/templates/task_plan.append.md +5 -5
  123. package/presets/standard-task/preset.yaml +2 -2
  124. package/references/agents-md-pattern.md +23 -17
  125. package/references/lessons-governance.md +2 -2
  126. package/references/module-parallel-standard.md +3 -6
  127. package/references/ssot-governance.md +2 -2
  128. package/references/taskr-gap-analysis.md +3 -3
  129. package/run-dist.mjs +34 -0
  130. package/skills/preset-creator/SKILL.md +40 -8
  131. package/skills/preset-creator/references/complex-task-skeleton/brief.md +32 -8
  132. package/skills/preset-creator/references/preset-package-skeleton.md +15 -5
  133. package/skills/preset-creator/references/structure-aware-paths.md +112 -0
  134. package/templates/AGENTS.md.template +28 -26
  135. package/templates/architecture/README.md +2 -2
  136. package/templates/architecture/service-catalog.md +2 -2
  137. package/templates/architecture/services/service-template.md +1 -1
  138. package/templates/dashboard/assets/app-src/00-state.js +5 -1
  139. package/templates/dashboard/assets/app-src/10-router.js +7 -0
  140. package/templates/dashboard/assets/app-src/20-overview.js +8 -8
  141. package/templates/dashboard/assets/app-src/30-tasks.js +132 -40
  142. package/templates/dashboard/assets/app-src/32-task-swimlane.js +314 -0
  143. package/templates/dashboard/assets/app-src/35-task-detail.js +35 -5
  144. package/templates/dashboard/assets/app-src/40-modules.js +257 -41
  145. package/templates/dashboard/assets/app-src/45-review.js +127 -1
  146. package/templates/dashboard/assets/app-src/90-bindings.js +185 -2
  147. package/templates/dashboard/assets/app.css +928 -53
  148. package/templates/dashboard/assets/app.css.manifest.json +2 -0
  149. package/templates/dashboard/assets/app.js +1071 -98
  150. package/templates/dashboard/assets/app.manifest.json +1 -0
  151. package/templates/dashboard/assets/css-src/00-foundation.css +12 -6
  152. package/templates/dashboard/assets/css-src/10-panels-flow.css +2 -2
  153. package/templates/dashboard/assets/css-src/30-task-index.css +21 -13
  154. package/templates/dashboard/assets/css-src/31-archive.css +94 -0
  155. package/templates/dashboard/assets/css-src/32-task-swimlane.css +487 -0
  156. package/templates/dashboard/assets/css-src/35-review-workspace.css +78 -0
  157. package/templates/dashboard/assets/css-src/40-detail-modules-migration.css +191 -14
  158. package/templates/dashboard/assets/css-src/50-responsive-overrides.css +23 -0
  159. package/templates/dashboard/assets/i18n.js +166 -2
  160. package/templates/development/README.md +9 -9
  161. package/templates/development/cross-repo-debugging.md +3 -3
  162. package/templates/development/external-context/service-template.md +1 -1
  163. package/templates/development/external-source-packs/README.md +2 -2
  164. package/templates/integrations/README.md +4 -4
  165. package/templates/integrations/api-contract.md +1 -1
  166. package/templates/integrations/event-contract.md +1 -1
  167. package/templates/integrations/third-party/vendor-template.md +1 -1
  168. package/templates/integrations/webhook-contract.md +1 -1
  169. package/templates/ledger/Harness-Ledger.md +1 -1
  170. package/templates/modules/module_brief.md +50 -0
  171. package/templates/modules/module_plan.md +49 -0
  172. package/templates/modules/registry_view.md +9 -0
  173. package/templates/modules/session_prompt_pack.md +55 -0
  174. package/templates/planning/brief.md +32 -8
  175. package/templates/planning/module_brief.md +28 -3
  176. package/templates/planning/module_plan.md +26 -11
  177. package/templates/planning/module_session_prompt.md +11 -2
  178. package/templates/planning/optional/slices/_slice-template/brief.md +28 -0
  179. package/templates/planning/review.md +1 -1
  180. package/templates/planning/visual_map.md +1 -1
  181. package/templates/reference/docs-library-standard.md +7 -7
  182. package/templates/reference/execution-workflow-standard.md +13 -0
  183. package/templates/reference/external-source-intake-standard.md +10 -10
  184. package/templates/reference/repo-governance-standard.md +1 -1
  185. package/templates/reference/review-routing-standard.md +4 -0
  186. package/templates/ssot/Module-Registry.md +4 -38
  187. package/templates/walkthrough/walkthrough-template.md +1 -1
  188. package/templates-zh-CN/AGENTS.md.template +27 -25
  189. package/templates-zh-CN/CLAUDE.md.template +1 -1
  190. package/templates-zh-CN/architecture/README.md +2 -2
  191. package/templates-zh-CN/architecture/service-catalog.md +2 -2
  192. package/templates-zh-CN/architecture/services/service-template.md +1 -1
  193. package/templates-zh-CN/development/README.md +9 -9
  194. package/templates-zh-CN/development/cross-repo-debugging.md +3 -3
  195. package/templates-zh-CN/development/external-context/service-template.md +1 -1
  196. package/templates-zh-CN/development/external-source-packs/README.md +2 -2
  197. package/templates-zh-CN/integrations/README.md +4 -4
  198. package/templates-zh-CN/integrations/api-contract.md +1 -1
  199. package/templates-zh-CN/integrations/event-contract.md +1 -1
  200. package/templates-zh-CN/integrations/third-party/vendor-template.md +1 -1
  201. package/templates-zh-CN/integrations/webhook-contract.md +1 -1
  202. package/templates-zh-CN/ledger/Harness-Ledger.md +1 -1
  203. package/templates-zh-CN/lessons/lesson-arch-process-change.md +1 -1
  204. package/templates-zh-CN/lessons/lesson-new-doc.md +3 -3
  205. package/templates-zh-CN/lessons/lesson-ref-change.md +4 -4
  206. package/templates-zh-CN/modules/module_brief.md +47 -0
  207. package/templates-zh-CN/modules/module_plan.md +48 -0
  208. package/templates-zh-CN/modules/registry_view.md +9 -0
  209. package/templates-zh-CN/modules/session_prompt_pack.md +50 -0
  210. package/templates-zh-CN/planning/INDEX.md +1 -0
  211. package/templates-zh-CN/planning/brief.md +26 -7
  212. package/templates-zh-CN/planning/module_brief.md +24 -2
  213. package/templates-zh-CN/planning/module_plan.md +35 -29
  214. package/templates-zh-CN/planning/module_session_prompt.md +15 -11
  215. package/templates-zh-CN/planning/optional/slices/_slice-template/brief.md +28 -11
  216. package/templates-zh-CN/planning/review.md +1 -1
  217. package/templates-zh-CN/reference/adversarial-review-standard.md +1 -1
  218. package/templates-zh-CN/reference/delivery-operating-model-standard.md +3 -3
  219. package/templates-zh-CN/reference/docs-library-standard.md +27 -27
  220. package/templates-zh-CN/reference/execution-workflow-standard.md +12 -2
  221. package/templates-zh-CN/reference/external-source-intake-standard.md +10 -10
  222. package/templates-zh-CN/reference/harness-ledger-standard.md +3 -3
  223. package/templates-zh-CN/reference/regression-ssot-governance.md +2 -2
  224. package/templates-zh-CN/reference/repo-governance-standard.md +1 -1
  225. package/templates-zh-CN/reference/review-routing-standard.md +3 -0
  226. package/templates-zh-CN/reference/walkthrough-standard.md +2 -2
  227. package/templates-zh-CN/reference/worktree-standard.md +1 -1
  228. package/templates-zh-CN/regression/Cadence-Ledger.md +2 -2
  229. package/templates-zh-CN/ssot/Delivery-SSoT.md +2 -2
  230. package/templates-zh-CN/ssot/Module-Registry.md +5 -44
  231. package/templates-zh-CN/ssot/Regression-SSoT.md +2 -2
  232. package/templates-zh-CN/walkthrough/walkthrough-template.md +4 -4
@@ -1,4 +1,3 @@
1
- // @ts-nocheck
2
1
  import fs from "node:fs";
3
2
  import path from "node:path";
4
3
  import { lessonCandidatesFile, normalizeTarget, readFileSafe, toPosix, normalizeTaskId, localDate, } from "./core-shared.mjs";
@@ -11,6 +10,10 @@ import { taskIdForDirectory } from "./task-scanner.mjs";
11
10
  import { beginGovernanceSync, commitGovernanceSync, governanceRelativePaths, releaseGovernanceSync, } from "./governance-sync.mjs";
12
11
  const presetId = "lesson-sedimentation";
13
12
  export class LessonSedimentationError extends Error {
13
+ code;
14
+ status;
15
+ details;
16
+ recovery;
14
17
  constructor(message, { code = "lesson-sedimentation-failed", status = 400, details = {}, recovery = [] } = {}) {
15
18
  super(message);
16
19
  this.name = "LessonSedimentationError";
@@ -20,7 +23,7 @@ export class LessonSedimentationError extends Error {
20
23
  this.recovery = recovery;
21
24
  }
22
25
  }
23
- export function createLessonSedimentationTask(targetInput, taskRef, candidateId, { dryRun = false, title = "" } = {}) {
26
+ export function createLessonSedimentationTask(targetInput, taskRef, candidateId, { dryRun = false, title = "", deferCommit = false, allowDirtyRelativePaths = [] } = {}) {
24
27
  const target = normalizeTarget(targetInput);
25
28
  const sourceTaskDir = resolveTaskDirectory(target, taskRef);
26
29
  const sourceTaskId = taskIdForDirectory(target, sourceTaskDir);
@@ -74,12 +77,14 @@ export function createLessonSedimentationTask(targetInput, taskRef, candidateId,
74
77
  budget: "standard",
75
78
  longRunning: true,
76
79
  dryRun,
80
+ deferCommit,
81
+ allowDirtyRelativePaths,
77
82
  });
78
83
  }
79
84
  catch (error) {
80
- if (/Task already exists:/i.test(error.message)) {
85
+ if (/Task already exists:/i.test(errorMessage(error))) {
81
86
  const existingTask = `TASKS/${localDate()}-${slug}`;
82
- throw new LessonSedimentationError(error.message, {
87
+ throw new LessonSedimentationError(errorMessage(error), {
83
88
  code: "lesson-follow-up-directory-exists",
84
89
  status: 409,
85
90
  details: { candidateId, existingTask, sourceTask: sourceTaskId },
@@ -117,7 +122,19 @@ export function createLessonSedimentationTask(targetInput, taskRef, candidateId,
117
122
  });
118
123
  const changes = [...taskResult.changes];
119
124
  if (!dryRun) {
120
- const governanceContext = beginGovernanceSync(target, { operation: `lesson-sediment ${sourceTaskId} ${candidate.id}` });
125
+ const deferredDirtyPaths = deferCommit
126
+ ? [
127
+ ...governanceRelativePaths(asGovernanceChanges(taskResult.changes)),
128
+ toPosix(path.relative(target.projectRoot, candidatePath)),
129
+ ...(allowDirtyRelativePaths || []),
130
+ ]
131
+ : [];
132
+ const governanceContext = beginGovernanceSync(target, {
133
+ operation: `lesson-sediment ${sourceTaskId} ${candidate.id}`,
134
+ allowDirtyWorktree: deferCommit,
135
+ allowedRelativePaths: deferredDirtyPaths,
136
+ allowDirtyWriteScope: deferCommit,
137
+ });
121
138
  try {
122
139
  appendToFollowUpTask({ followUpDir, sourceTaskId, candidate, prompt, contextPacket, audit });
123
140
  updateSourceFollowUpTask(candidatePath, candidate.id, followUpTaskId);
@@ -142,11 +159,14 @@ export function createLessonSedimentationTask(targetInput, taskRef, candidateId,
142
159
  source: lessonCandidatesFile,
143
160
  action: "update-follow-up-task",
144
161
  });
162
+ const commit = deferCommit
163
+ ? { committed: false, reason: "deferred", allowedPaths: governanceRelativePaths(asGovernanceChanges(changes)) }
164
+ : commitGovernanceSync(governanceContext, governanceRelativePaths(asGovernanceChanges(changes)), {
165
+ message: `chore(harness): record lesson sedimentation ${candidate.id}`,
166
+ });
145
167
  taskResult.governance = {
146
168
  ...(taskResult.governance || {}),
147
- lessonSedimentationCommit: commitGovernanceSync(governanceContext, governanceRelativePaths(changes), {
148
- message: `chore(harness): record lesson sedimentation ${candidate.id}`,
149
- }),
169
+ lessonSedimentationCommit: commit,
150
170
  };
151
171
  }
152
172
  finally {
@@ -169,9 +189,173 @@ export function createLessonSedimentationTask(targetInput, taskRef, candidateId,
169
189
  governance: taskResult.governance || null,
170
190
  };
171
191
  }
192
+ export function createAggregateLessonSedimentationTask(targetInput, selections, { dryRun = false, title = "" } = {}) {
193
+ const target = normalizeTarget(targetInput);
194
+ const normalizedSelections = normalizeAggregateSelections(selections);
195
+ if (normalizedSelections.length === 0) {
196
+ throw new LessonSedimentationError("No lesson candidates selected", {
197
+ code: "lesson-aggregate-empty",
198
+ status: 400,
199
+ recovery: ["Select at least one actionable lesson candidate."],
200
+ });
201
+ }
202
+ const entries = normalizedSelections.map((selection) => resolveLessonCandidate(target, selection.taskId, selection.candidateId));
203
+ const sourceShort = entries.length === 1
204
+ ? entries[0].sourceShortId.replace(/^\d{4}-\d{2}-\d{2}-/, "")
205
+ : commonSourceShort(entries) || "selected-lessons";
206
+ const slug = normalizeTaskId(`lesson-${sourceShort}-aggregate-${Date.now().toString(36).slice(-6)}`);
207
+ const taskTitle = title || `Aggregate lesson sedimentation for ${entries.length} candidates`;
208
+ const locale = readCapabilityRegistry(target).locale;
209
+ const preset = readPresetPackage(presetId);
210
+ const taskResult = createTask(target.projectRoot, slug, {
211
+ title: taskTitle,
212
+ locale,
213
+ budget: "standard",
214
+ longRunning: true,
215
+ dryRun,
216
+ deferCommit: true,
217
+ });
218
+ const followUpTaskId = taskResult.task.id;
219
+ const followUpDir = path.join(target.projectRoot, taskResult.task.path.replace(/^TARGET:/, ""));
220
+ const audit = buildPresetAudit(preset, {
221
+ taskId: followUpTaskId,
222
+ targetRoot: target.projectRoot,
223
+ entrypoint: "newTask",
224
+ writeScopes: [`${toPosix(path.relative(target.projectRoot, target.harness.tasksRoot))}/**`],
225
+ });
226
+ const prompt = renderAggregateLessonSedimentationPrompt({ target, entries, followUpTaskId });
227
+ const contextPacket = renderAggregateContextPacket({ target, entries, followUpTaskId, audit });
228
+ const changes = [...taskResult.changes];
229
+ if (!dryRun) {
230
+ const candidatePaths = [...new Set(entries.map((entry) => toPosix(path.relative(target.projectRoot, entry.candidatePath))))];
231
+ const governanceContext = beginGovernanceSync(target, {
232
+ operation: `lesson-sediment-aggregate ${followUpTaskId}`,
233
+ allowDirtyWorktree: true,
234
+ allowedRelativePaths: [...governanceRelativePaths(asGovernanceChanges(taskResult.changes)), ...candidatePaths],
235
+ allowDirtyWriteScope: true,
236
+ });
237
+ try {
238
+ appendToAggregateFollowUpTask({ followUpDir, entries, prompt, contextPacket, audit });
239
+ for (const entry of entries)
240
+ updateSourceFollowUpTask(entry.candidatePath, entry.candidate.id, followUpTaskId);
241
+ changes.push({
242
+ destination: toPosix(path.relative(target.projectRoot, path.join(followUpDir, "task_plan.md"))),
243
+ source: "lesson-sedimentation-aggregate",
244
+ action: "append-aggregate-context",
245
+ }, {
246
+ destination: toPosix(path.relative(target.projectRoot, path.join(followUpDir, "progress.md"))),
247
+ source: "lesson-sedimentation-aggregate",
248
+ action: "append-aggregate-progress",
249
+ }, {
250
+ destination: toPosix(path.relative(target.projectRoot, path.join(followUpDir, "artifacts/lesson-sedimentation-prompt.md"))),
251
+ source: "lesson-sedimentation-aggregate",
252
+ action: "create-aggregate-prompt-artifact",
253
+ }, {
254
+ destination: toPosix(path.relative(target.projectRoot, path.join(followUpDir, "artifacts/preset-audit.json"))),
255
+ source: "lesson-sedimentation-aggregate",
256
+ action: "create-aggregate-preset-audit",
257
+ }, ...candidatePaths.map((destination) => ({
258
+ destination,
259
+ source: lessonCandidatesFile,
260
+ action: "update-follow-up-task",
261
+ })));
262
+ taskResult.governance = {
263
+ ...(taskResult.governance || {}),
264
+ commit: commitGovernanceSync(governanceContext, governanceRelativePaths(asGovernanceChanges(changes)), {
265
+ message: `chore(harness): record aggregate lesson sedimentation ${followUpTaskId}`,
266
+ }),
267
+ };
268
+ }
269
+ finally {
270
+ releaseGovernanceSync(governanceContext);
271
+ }
272
+ }
273
+ return {
274
+ dryRun,
275
+ event: "lesson-sedimentation-aggregate",
276
+ preset: presetId,
277
+ candidates: entries.map((entry) => ({
278
+ taskId: entry.sourceTaskId,
279
+ candidateId: entry.candidate.id,
280
+ title: entry.candidate.title,
281
+ detailArtifact: resolveDetailArtifact(target, entry.sourceTaskDir, entry.candidate).prefixedPath || "",
282
+ })),
283
+ followUpTask: {
284
+ id: followUpTaskId,
285
+ path: taskResult.task.path,
286
+ title: taskTitle,
287
+ },
288
+ prompt,
289
+ changes,
290
+ governance: taskResult.governance || null,
291
+ };
292
+ }
293
+ function normalizeAggregateSelections(selections) {
294
+ const seen = new Set();
295
+ const normalized = [];
296
+ for (const selection of Array.isArray(selections) ? selections : []) {
297
+ const record = asRecord(selection);
298
+ const taskId = String(record.taskId || "").trim();
299
+ const candidateId = String(record.candidateId || "").trim();
300
+ if (!taskId || !candidateId)
301
+ continue;
302
+ const key = `${taskId}\n${candidateId}`;
303
+ if (seen.has(key))
304
+ continue;
305
+ seen.add(key);
306
+ normalized.push({ taskId, candidateId });
307
+ }
308
+ return normalized;
309
+ }
310
+ function resolveLessonCandidate(target, taskRef, candidateId) {
311
+ const sourceTaskDir = resolveTaskDirectory(target, taskRef);
312
+ const sourceTaskId = taskIdForDirectory(target, sourceTaskDir);
313
+ const sourceShortId = path.basename(sourceTaskDir);
314
+ const candidatePath = path.join(sourceTaskDir, lessonCandidatesFile);
315
+ const candidateStatus = parseLessonCandidateStatus(readFileSafe(candidatePath));
316
+ const candidate = candidateStatus.rows.find((row) => row.id === candidateId);
317
+ if (!candidate) {
318
+ throw new LessonSedimentationError(`Lesson candidate not found: ${candidateId}`, {
319
+ code: "lesson-candidate-not-found",
320
+ status: 404,
321
+ details: { candidateId, sourceTask: sourceTaskId },
322
+ recovery: [
323
+ "Open the source task lesson_candidates.md and confirm the candidate ID.",
324
+ "Refresh the Dashboard snapshot if the candidate was just added.",
325
+ ],
326
+ });
327
+ }
328
+ if (!["needs-promotion", "ready-for-review"].includes(candidate.status)) {
329
+ throw new LessonSedimentationError(`Lesson candidate must be ready-for-review or needs-promotion; current status is ${candidate.status}`, {
330
+ code: "lesson-candidate-not-actionable",
331
+ status: 409,
332
+ details: { candidateId, status: candidate.status, sourceTask: sourceTaskId },
333
+ recovery: [
334
+ "Set the candidate status to ready-for-review or needs-promotion after human review.",
335
+ "Use Copy lesson prompt if you only need the prompt without creating a task.",
336
+ ],
337
+ });
338
+ }
339
+ if (candidate.followUpTask && !/^pending$/i.test(candidate.followUpTask)) {
340
+ throw new LessonSedimentationError(`Lesson candidate already has follow-up task: ${candidate.followUpTask}`, {
341
+ code: "lesson-follow-up-exists",
342
+ status: 409,
343
+ details: { candidateId, followUpTask: candidate.followUpTask, sourceTask: sourceTaskId },
344
+ recovery: [
345
+ "Open the existing follow-up task instead of creating a duplicate.",
346
+ "If the existing task is wrong, edit the Follow-up Task cell back to pending after review.",
347
+ ],
348
+ });
349
+ }
350
+ return { sourceTaskDir, sourceTaskId, sourceShortId, candidatePath, candidate };
351
+ }
352
+ function commonSourceShort(entries) {
353
+ const unique = [...new Set(entries.map((entry) => entry.sourceShortId.replace(/^\d{4}-\d{2}-\d{2}-/, "")))];
354
+ return unique.length === 1 ? unique[0] : "";
355
+ }
172
356
  function renderLessonSedimentationPrompt(preset, values) {
173
357
  const detailArtifact = resolveDetailArtifact(values.target, values.sourceTaskDir, values.candidate);
174
- const prompt = renderPresetTemplate(preset, preset.entrypoints.newTask?.templates?.prompt, {
358
+ const prompt = renderPresetTemplate(preset, preset.entrypoints.newTask?.templates?.prompt || "", {
175
359
  sourceTaskId: values.sourceTaskId,
176
360
  sourceShortId: values.sourceShortId,
177
361
  candidateId: values.candidate.id,
@@ -223,6 +407,76 @@ function renderContextPacket({ target, sourceTaskDir, sourceTaskId, candidate, f
223
407
  "",
224
408
  ].join("\n");
225
409
  }
410
+ function renderAggregateLessonSedimentationPrompt({ target, entries, followUpTaskId }) {
411
+ const candidateBlocks = entries.map((entry, index) => {
412
+ const detailArtifact = resolveDetailArtifact(target, entry.sourceTaskDir, entry.candidate);
413
+ return [
414
+ `### ${index + 1}. ${entry.candidate.id} - ${entry.candidate.title || "Untitled lesson candidate"}`,
415
+ "",
416
+ `- Source task: ${entry.sourceTaskId}`,
417
+ `- Candidate status: ${entry.candidate.status}`,
418
+ `- Scope: ${entry.candidate.scope || "unspecified"}`,
419
+ `- Module key: ${entry.candidate.moduleKey || "n/a"}`,
420
+ `- Detail artifact: ${detailArtifact.prefixedPath || "not provided"}`,
421
+ `- Boundary reason: ${entry.candidate.boundaryReason || "unspecified"}`,
422
+ `- Why it might matter: ${entry.candidate.whyItMightMatter || "unspecified"}`,
423
+ `- Promotion target: ${entry.candidate.promotionTarget || "unspecified"}`,
424
+ `- Conflict check: ${entry.candidate.conflictCheck || "pending"}`,
425
+ `- Required standard update: ${entry.candidate.requiredStandardUpdate || "pending"}`,
426
+ "",
427
+ ].join("\n");
428
+ }).join("\n");
429
+ return [
430
+ "You are executing an aggregate lesson sedimentation follow-up task.",
431
+ "",
432
+ `Follow-up task: ${followUpTaskId}`,
433
+ `Candidate count: ${entries.length}`,
434
+ "",
435
+ "Instructions:",
436
+ "1. Read each source task, review, findings, progress, lesson_candidates.md, and task-local detail artifact.",
437
+ "2. Preserve candidate boundaries; do not merge unrelated lessons into one vague generalization.",
438
+ "3. Promote the smallest coherent set of reusable lessons, grouping only when candidates describe the same rule.",
439
+ "4. Check conflicts against existing lessons and standards before proposing changes.",
440
+ "5. Propose the smallest diff first.",
441
+ "6. Do not write a shared Lessons table; use promoted detail docs or focused follow-up edits.",
442
+ "",
443
+ "Selected candidates:",
444
+ "",
445
+ candidateBlocks.trimEnd(),
446
+ "",
447
+ ].join("\n");
448
+ }
449
+ function renderAggregateContextPacket({ target, entries, followUpTaskId, audit }) {
450
+ const rows = entries.map((entry) => {
451
+ const detailArtifact = resolveDetailArtifact(target, entry.sourceTaskDir, entry.candidate);
452
+ const sourceLessonPath = `TARGET:${toPosix(path.relative(target.projectRoot, path.join(entry.sourceTaskDir, lessonCandidatesFile)))}`;
453
+ return [
454
+ entry.candidate.id,
455
+ entry.sourceTaskId,
456
+ entry.candidate.title || "",
457
+ entry.candidate.scope || "unspecified",
458
+ entry.candidate.moduleKey || "n/a",
459
+ detailArtifact.prefixedPath || "not provided",
460
+ sourceLessonPath,
461
+ entry.candidate.promotionTarget || "unspecified",
462
+ ];
463
+ });
464
+ return [
465
+ "## Aggregate Lesson Sedimentation Context Packet",
466
+ "",
467
+ "| Candidate | Source Task | Title | Scope | Module | Detail Artifact | Candidate Table | Promotion Target |",
468
+ "| --- | --- | --- | --- | --- | --- | --- | --- |",
469
+ ...rows.map((row) => `| ${row.map(markdownCell).join(" | ")} |`),
470
+ "",
471
+ "| Field | Value |",
472
+ "| --- | --- |",
473
+ `| Preset | ${presetId} |`,
474
+ `| Follow-up Task | ${followUpTaskId} |`,
475
+ `| Candidate Count | ${entries.length} |`,
476
+ `| Preset Manifest | ${audit.manifestPath} |`,
477
+ "",
478
+ ].join("\n");
479
+ }
226
480
  function resolveDetailArtifact(target, sourceTaskDir, candidate) {
227
481
  const raw = String(candidate.detailArtifact || "").trim();
228
482
  if (!raw || /^(?:n\/a|none|pending)$/i.test(raw))
@@ -273,6 +527,44 @@ function appendToFollowUpTask({ followUpDir, sourceTaskId, candidate, prompt, co
273
527
  "",
274
528
  ].join("\n"));
275
529
  }
530
+ function appendToAggregateFollowUpTask({ followUpDir, entries, prompt, contextPacket, audit }) {
531
+ const taskPlanPath = path.join(followUpDir, "task_plan.md");
532
+ const progressPath = path.join(followUpDir, "progress.md");
533
+ const artifactsDir = path.join(followUpDir, "artifacts");
534
+ fs.mkdirSync(artifactsDir, { recursive: true });
535
+ fs.writeFileSync(path.join(artifactsDir, "lesson-sedimentation-prompt.md"), `${prompt}\n`);
536
+ fs.writeFileSync(path.join(artifactsDir, "preset-audit.json"), `${JSON.stringify(audit, null, 2)}\n`);
537
+ const candidateList = entries.map((entry) => `- ${entry.candidate.id}: ${entry.sourceTaskId} - ${entry.candidate.title || "Untitled lesson candidate"}`).join("\n");
538
+ fs.appendFileSync(taskPlanPath, [
539
+ "",
540
+ "## Aggregate Lesson Sedimentation Preset",
541
+ "",
542
+ "Task Preset: lesson-sedimentation",
543
+ "Preset Version: 1",
544
+ "Task Kind: lesson-sedimentation",
545
+ `Candidate Count: ${entries.length}`,
546
+ "",
547
+ "### Selected Candidates",
548
+ "",
549
+ candidateList,
550
+ "",
551
+ contextPacket.trimEnd(),
552
+ "",
553
+ "## Execution Prompt",
554
+ "",
555
+ "The copyable aggregate prompt for a new agent session is stored in `artifacts/lesson-sedimentation-prompt.md`.",
556
+ "",
557
+ ].join("\n"));
558
+ fs.appendFileSync(progressPath, [
559
+ "",
560
+ "### Aggregate lesson sedimentation task created",
561
+ "",
562
+ `- Candidate count: ${entries.length}`,
563
+ `- Source tasks: ${[...new Set(entries.map((entry) => entry.sourceTaskId))].join(", ")}`,
564
+ "- Next: use the aggregate execution prompt, preserve candidate boundaries, and propose focused reusable lesson diffs.",
565
+ "",
566
+ ].join("\n"));
567
+ }
276
568
  function updateSourceFollowUpTask(candidatePath, candidateId, followUpTaskId) {
277
569
  const content = readFileSafe(candidatePath);
278
570
  const update = updateMarkdownTableRow(content, /^ID$/i, (header, row) => {
@@ -298,3 +590,12 @@ function summarizeMarkdown(content) {
298
590
  .filter((line) => line && !/^\|?\s*-{3,}/.test(line));
299
591
  return lines.slice(0, 4).join(" / ") || "not recorded";
300
592
  }
593
+ function asGovernanceChanges(changes) {
594
+ return changes.map((change) => ({ surface: "task", ...change }));
595
+ }
596
+ function asRecord(value) {
597
+ return value && typeof value === "object" ? value : {};
598
+ }
599
+ function errorMessage(error) {
600
+ return error instanceof Error ? error.message : String(error);
601
+ }
@@ -1,13 +1,16 @@
1
- // @ts-nocheck
2
1
  import fs from "node:fs";
3
2
  import path from "node:path";
4
3
  import { toPosix } from "../core-shared.mjs";
5
4
  import { appendLongRunningContractFile, moduleTemplateFiles, taskFilesForBudget, } from "./template-files.mjs";
6
5
  import { discoverImplicitHarnessTarget } from "../harness-paths.mjs";
7
- export function planCreateTaskChanges({ target, directory, normalizedModuleKey, normalizedLocale, normalizedBudget, longRunning, presetContext }) {
6
+ export function planCreateTaskChanges({ target, directory, normalizedModuleKey, normalizedLocale, normalizedBudget, longRunning, presetContext, }) {
8
7
  const changes = [];
9
8
  if (normalizedModuleKey) {
10
- const moduleDirectory = path.dirname(directory);
9
+ const harness = target.harness;
10
+ const modulesRoot = String(harness?.modulesRoot || target.modulesRoot || "");
11
+ const moduleDirectory = modulesRoot
12
+ ? path.join(modulesRoot, normalizedModuleKey)
13
+ : path.dirname(directory);
11
14
  for (const [destination, source] of moduleTemplateFiles({ locale: normalizedLocale })) {
12
15
  const destinationPath = path.join(moduleDirectory, destination);
13
16
  if (fs.existsSync(destinationPath))
@@ -1,4 +1,3 @@
1
- // @ts-nocheck
2
1
  import fs from "node:fs";
3
2
  import path from "node:path";
4
3
  import { lessonCandidatesFile, readFileSafe, todayDate, toPosix, visualMapFile, } from "../core-shared.mjs";
@@ -0,0 +1,16 @@
1
+ import { assertPresetWriteScope, buildPresetContext, evaluateTemplateValues, resolvePresetInputs, renderPresetTaskTemplate, } from "../preset-engine.mjs";
2
+ export function resolveLifecyclePresetInputs(presetPackage, { cliArgs, fromSession, targetInput }) {
3
+ return Reflect.apply(resolvePresetInputs, undefined, [presetPackage, { cliArgs, fromSession, targetInput }]);
4
+ }
5
+ export function evaluatePresetValues(presetPackage, resolvedInputs, { taskId, taskTitle, moduleKey, target }) {
6
+ return Reflect.apply(evaluateTemplateValues, undefined, [presetPackage, resolvedInputs, { taskId, taskTitle, moduleKey, target }]);
7
+ }
8
+ export function buildLifecyclePresetContext(presetPackage, options) {
9
+ return Reflect.apply(buildPresetContext, undefined, [presetPackage, options]);
10
+ }
11
+ export function renderLifecyclePresetTaskTemplate(destination, content, presetContext) {
12
+ return Reflect.apply(renderPresetTaskTemplate, undefined, [destination, content, presetContext]);
13
+ }
14
+ export function assertLifecyclePresetWriteScope(presetPackage, relativePath, target) {
15
+ Reflect.apply(assertPresetWriteScope, undefined, [presetPackage, relativePath, target]);
16
+ }
@@ -1,4 +1,3 @@
1
- // @ts-nocheck
2
1
  // Dynamic review confirmation flow stays behavior-first until the metadata domain model PR.
3
2
  import fs from "node:fs";
4
3
  import path from "node:path";
@@ -8,7 +7,7 @@ import { collectReviewRisks, isBlockingReviewRisk, isLessonCandidateDecisionComp
8
7
  import { commitReviewConfirmationGate, prepareReviewConfirmGitGate } from "../review-confirm-git-gate.mjs";
9
8
  import { validateHumanReviewConfirmation } from "./review-gates.mjs";
10
9
  import { markdownCell } from "./text-utils.mjs";
11
- export function confirmTaskReview({ target, taskDir, findTaskByDirectory }, { reviewer = "Human Reviewer", message = "", confirmText = "", evidence = "" } = {}) {
10
+ export function confirmTaskReview({ target, taskDir, findTaskByDirectory }, { reviewer = "Human Reviewer", message = "", confirmText = "", evidence = "", deferCommit = false } = {}) {
12
11
  assertTaskDirectoryInsidePlanning(target, taskDir);
13
12
  const canonicalTaskId = taskIdForDirectory(target, taskDir);
14
13
  const shortId = path.basename(taskDir);
@@ -56,6 +55,19 @@ export function confirmTaskReview({ target, taskDir, findTaskByDirectory }, { re
56
55
  "Migration Status": baseAuditFields["Migration Status"] || "native",
57
56
  });
58
57
  fs.writeFileSync(indexPath, ensureTrailingNewline(replaceTaskAuditMetadata(indexContent, buildAuditFields(), { locale: target.locale })));
58
+ if (deferCommit) {
59
+ return {
60
+ event: "review-confirm",
61
+ task: findTaskByDirectory(target, taskDir) || { id: canonicalTaskId, reviewStatus: "confirmed" },
62
+ audit: {
63
+ commitSha: "pending",
64
+ auditCommitSha: "",
65
+ auditStatus: "deferred",
66
+ allowedPaths: gitGate.allowedPaths,
67
+ message: message || `Human review confirmed by ${reviewer}`,
68
+ },
69
+ };
70
+ }
59
71
  const audit = commitReviewConfirmationGate(gitGate, {
60
72
  taskId: canonicalTaskId,
61
73
  reviewPath: indexPath,
@@ -72,6 +84,26 @@ export function confirmTaskReview({ target, taskDir, findTaskByDirectory }, { re
72
84
  audit,
73
85
  };
74
86
  }
87
+ export function finalizeDeferredTaskReviewConfirmation({ target, taskDir, findTaskByDirectory }, { commitSha = "" } = {}) {
88
+ assertTaskDirectoryInsidePlanning(target, taskDir);
89
+ if (!commitSha)
90
+ throw new Error("Missing deferred review confirmation commit SHA");
91
+ const canonicalTaskId = taskIdForDirectory(target, taskDir);
92
+ const indexPath = path.join(taskDir, "INDEX.md");
93
+ const indexContent = readFileSafe(indexPath);
94
+ const existingAudit = taskAuditFieldsFromIndex(indexContent);
95
+ const finalIndex = replaceTaskAuditMetadata(indexContent, {
96
+ ...existingAudit,
97
+ "Review Commit SHA": commitSha,
98
+ "Audit Status": "committed",
99
+ }, { locale: target.locale });
100
+ fs.writeFileSync(indexPath, ensureTrailingNewline(finalIndex));
101
+ return {
102
+ event: "review-confirm-audit",
103
+ task: findTaskByDirectory(target, taskDir) || { id: canonicalTaskId, reviewStatus: "confirmed" },
104
+ indexPath,
105
+ };
106
+ }
75
107
  function assertTaskDirectoryInsidePlanning(target, taskDir) {
76
108
  const realTaskDir = fs.realpathSync(taskDir);
77
109
  const allowedRoots = (target.harness?.taskRoots || [])
@@ -1,11 +1,10 @@
1
- // @ts-nocheck
2
1
  // Dynamic review gate modeling stays behavior-first until the metadata domain model PR.
3
2
  import fs from "node:fs";
4
3
  import path from "node:path";
5
4
  import { lessonCandidatesFile, } from "../core-shared.mjs";
6
- import { collectReviewRisks, isBlockingReviewRisk, parseTaskAuditMetadata, parsePhases, parseReviewConfirmation, readVisualMapContractFile, } from "../task-scanner.mjs";
5
+ import { collectReviewRisks, isLessonCandidateDecisionComplete, isBlockingReviewRisk, parseLessonCandidateStatus, parseTaskAuditMetadata, parsePhases, parseReviewConfirmation, readVisualMapContractFile, } from "../task-scanner.mjs";
7
6
  import { implementationPhases, phaseHasRecordedProgress, } from "../phase-kind.mjs";
8
- export function validateLifecycleTransition({ event, currentState, budget, reviewContent = "", indexContent = "", reviewTaskKey = "", projectRoot = "", taskDir = "" }) {
7
+ export function validateLifecycleTransition({ event, currentState, budget, reviewContent = "", indexContent = "", reviewTaskKey = "", projectRoot = "", taskDir = "", }) {
9
8
  if (event === "task-review" && currentState !== "in_progress") {
10
9
  throw new Error(`task-review requires current state in_progress; current state is ${currentState || "unknown"}`);
11
10
  }
@@ -19,7 +18,15 @@ export function validateLifecycleTransition({ event, currentState, budget, revie
19
18
  throw new Error(`Open blocking review findings must be closed before task-complete: ${ids}`);
20
19
  }
21
20
  if (!parseReviewConfirmation(reviewContent, { taskKey: reviewTaskKey, taskAudit: parseTaskAuditMetadata(indexContent), projectRoot, taskDir, indexPath: path.join(taskDir, "INDEX.md") })?.confirmed) {
22
- throw new Error("Human review must be confirmed before task-complete. Run review-confirm first.");
21
+ throw new Error("Human review must be confirmed before task-complete. Confirm it from the local Dashboard workbench first.");
22
+ }
23
+ const lessonCandidateStatus = parseLessonCandidateStatus(fs.existsSync(path.join(taskDir, lessonCandidatesFile)) ? fs.readFileSync(path.join(taskDir, lessonCandidatesFile), "utf8") : "");
24
+ if (!isLessonCandidateDecisionComplete(lessonCandidateStatus)) {
25
+ throw new Error(`Lesson candidate decision must be complete before task-complete; current status is ${lessonCandidateStatus.status}.`);
26
+ }
27
+ const hasLessonWork = lessonCandidateStatus.status === "needs-promotion" || lessonCandidateStatus.promotionState === "queued" || lessonCandidateStatus.openCount > 0;
28
+ if (hasLessonWork) {
29
+ throw new Error("Lesson candidate promotion or sedimentation work must be resolved before task-complete.");
23
30
  }
24
31
  }
25
32
  }
@@ -44,7 +51,7 @@ export function validateHumanReviewConfirmation({ task, budget }) {
44
51
  if (budget === "simple")
45
52
  return;
46
53
  if (!task?.walkthroughPath) {
47
- throw new Error("Human review confirmation requires task-local walkthrough.md before review-confirm.");
54
+ throw new Error("Human review confirmation requires task-local walkthrough.md before Dashboard confirmation.");
48
55
  }
49
56
  const queueState = task?.reviewQueueState || "not-in-queue";
50
57
  if (queueState !== "ready-to-confirm") {
@@ -1,4 +1,3 @@
1
- // @ts-nocheck
2
1
  // Dynamic review submission rendering stays behavior-first until the metadata domain model PR.
3
2
  import crypto from "node:crypto";
4
3
  import fs from "node:fs";
@@ -6,7 +5,7 @@ import path from "node:path";
6
5
  import { lessonCandidatesFile, longRunningTaskContractFile, nowTimestamp, readFileSafe, toPosix, visualMapFile, } from "../core-shared.mjs";
7
6
  import { collectReviewRisks, isBlockingReviewRisk, taskScannerVersion, } from "../task-review-model.mjs";
8
7
  import { markdownCell } from "./text-utils.mjs";
9
- export function renderAgentReviewSubmission({ target, taskDir, canonicalTaskId, message, evidence }) {
8
+ export function renderAgentReviewSubmission({ target, taskDir, canonicalTaskId, message, evidence, }) {
10
9
  const timestamp = nowTimestamp();
11
10
  const submissionId = `ARS-${timestamp.replace(/[^0-9]/g, "").slice(0, 14)}`;
12
11
  const materialsHash = hashTaskMaterials(taskDir);
@@ -1,4 +1,3 @@
1
- // @ts-nocheck
2
1
  import path from "node:path";
3
2
  import { localizedTemplateSource, todayDate } from "../core-shared.mjs";
4
3
  import { markdownCell } from "./text-utils.mjs";
@@ -30,11 +30,8 @@ export function optionalTaskTemplateFiles({ locale = "en-US" } = {}) {
30
30
  }
31
31
  export function moduleTemplateFiles({ locale = "en-US" } = {}) {
32
32
  return [
33
- ["brief.md", "templates/planning/module_brief.md"],
34
- ["module_plan.md", "templates/planning/module_plan.md"],
35
- ["execution_strategy.md", "templates/planning/execution_strategy.md"],
36
- [visualMapFile, "templates/planning/visual_map.md"],
37
- ["session_prompt.md", "templates/planning/module_session_prompt.md"],
33
+ ["brief.md", "templates/modules/module_brief.md"],
34
+ ["module_plan.md", "templates/modules/module_plan.md"],
38
35
  ].map(([destination, source]) => [destination, localizedTemplateSource(source, locale)]);
39
36
  }
40
37
  export function taskFilesForBudget({ budget, locale }) {