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
@@ -0,0 +1,239 @@
1
+ // @ts-nocheck
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { tableAfterHeading, firstColumn, } from "./markdown-utils.mjs";
5
+ import { toPosix } from "./core-shared.mjs";
6
+ export const allowedLessonCandidateTaskStatuses = new Set([
7
+ "missing",
8
+ "pending-review",
9
+ "no-candidate-accepted",
10
+ "needs-promotion",
11
+ "promoted",
12
+ "rejected",
13
+ ]);
14
+ export const allowedLessonCandidateRowStatuses = new Set([
15
+ "ready-for-review",
16
+ "needs-promotion",
17
+ "promoted",
18
+ "rejected",
19
+ ]);
20
+ export const reviewCompleteLessonCandidateStatuses = new Set([
21
+ "no-candidate-accepted",
22
+ "needs-promotion",
23
+ "promoted",
24
+ "rejected",
25
+ ]);
26
+ export function parseLessonCandidateStatus(content) {
27
+ const text = String(content || "");
28
+ if (!text.trim()) {
29
+ return emptyLessonCandidateStatus("missing", ["missing-candidate-file"]);
30
+ }
31
+ const fields = lessonCandidateFields(text);
32
+ const declaredStatus = normalizeLessonCandidateStatus(fields.get("task-level status") || "pending-review");
33
+ const reviewDecision = normalizeCandidateField(fields.get("review decision") || "pending-human-review");
34
+ const promotionState = normalizeCandidateField(fields.get("promotion state") || "not-promoted");
35
+ const closeoutToken = String(fields.get("closeout token") || "pending").trim();
36
+ const candidateTable = lessonCandidateRows(text);
37
+ const rows = candidateTable.rows;
38
+ const issues = [];
39
+ if (!allowedLessonCandidateTaskStatuses.has(declaredStatus)) {
40
+ issues.push(`invalid-task-status:${declaredStatus}`);
41
+ }
42
+ for (const row of rows) {
43
+ if (!allowedLessonCandidateRowStatuses.has(row.status))
44
+ issues.push(`invalid-row-status:${row.id || "missing-id"}:${row.status}`);
45
+ }
46
+ const promotionRows = rows.filter((row) => row.status === "needs-promotion");
47
+ if (promotionRows.length > 0) {
48
+ for (const column of candidateTable.missingColumns)
49
+ issues.push(`missing-column:${column}`);
50
+ for (const row of promotionRows) {
51
+ for (const [field, label] of [
52
+ ["scope", "Scope"],
53
+ ["detailArtifact", "Detail Artifact"],
54
+ ["boundaryReason", "Boundary Reason"],
55
+ ["whyItMightMatter", "Why It Might Matter"],
56
+ ["promotionTarget", "Promotion Target"],
57
+ ["conflictCheck", "Conflict Check"],
58
+ ["requiredStandardUpdate", "Required Standard Update"],
59
+ ["followUpTask", "Follow-up Task"],
60
+ ]) {
61
+ if (!String(row[field] || "").trim())
62
+ issues.push(`missing-row-field:${row.id || "missing-id"}:${label}`);
63
+ }
64
+ }
65
+ }
66
+ const aggregateStatus = aggregateLessonCandidateStatus(rows, declaredStatus);
67
+ if (declaredStatus !== aggregateStatus && declaredStatus !== "missing") {
68
+ issues.push(`status-aggregate-mismatch:${declaredStatus}->${aggregateStatus}`);
69
+ }
70
+ if (aggregateStatus === "no-candidate-accepted" && !noCandidateReason(text)) {
71
+ issues.push("missing-no-candidate-reason");
72
+ }
73
+ return {
74
+ status: aggregateStatus,
75
+ declaredStatus,
76
+ schemaVersion: fields.get("schema version") || "",
77
+ reviewDecision,
78
+ promotionState,
79
+ closeoutToken,
80
+ rows,
81
+ openCount: rows.filter((row) => ["ready-for-review", "needs-promotion"].includes(row.status)).length,
82
+ issues,
83
+ };
84
+ }
85
+ export function isLessonCandidateDecisionComplete(candidateStatus) {
86
+ if (!candidateStatus || candidateStatus.issues?.length)
87
+ return false;
88
+ return reviewCompleteLessonCandidateStatuses.has(candidateStatus.status);
89
+ }
90
+ export function validateLessonCandidateDetailArtifacts(target, taskDir, candidateStatus) {
91
+ const issues = [];
92
+ const rows = Array.isArray(candidateStatus?.rows) ? candidateStatus.rows : [];
93
+ for (const row of rows.filter((candidate) => candidate.status === "needs-promotion")) {
94
+ const rawPath = String(row.detailArtifact || "").trim();
95
+ if (!rawPath)
96
+ continue;
97
+ const resolved = resolveTaskLocalLessonArtifact(target, taskDir, rawPath);
98
+ if (!resolved || !isInsideDirectory(taskDir, resolved)) {
99
+ issues.push(`invalid-detail-artifact-path:${row.id || "missing-id"}:${rawPath}`);
100
+ continue;
101
+ }
102
+ if (!fs.existsSync(resolved) || !fs.statSync(resolved).isFile()) {
103
+ issues.push(`missing-detail-artifact:${row.id || "missing-id"}:${rawPath}`);
104
+ }
105
+ }
106
+ return issues;
107
+ }
108
+ function emptyLessonCandidateStatus(status, issues = []) {
109
+ return {
110
+ status,
111
+ declaredStatus: status,
112
+ schemaVersion: "",
113
+ reviewDecision: "",
114
+ promotionState: "",
115
+ closeoutToken: "",
116
+ rows: [],
117
+ openCount: 0,
118
+ issues,
119
+ };
120
+ }
121
+ function lessonCandidateFields(content) {
122
+ const { header, rows } = tableAfterHeading(content, /^Field$/i);
123
+ const fieldIndex = firstColumn(header, ["Field", "字段"]);
124
+ const valueIndex = firstColumn(header, ["Value", "值"]);
125
+ const fields = new Map();
126
+ if (fieldIndex < 0 || valueIndex < 0)
127
+ return fields;
128
+ for (const row of rows) {
129
+ const key = String(row[fieldIndex] || "").trim().toLowerCase();
130
+ if (key)
131
+ fields.set(key, String(row[valueIndex] || "").trim());
132
+ }
133
+ return fields;
134
+ }
135
+ function resolveTaskLocalLessonArtifact(target, taskDir, artifactPath) {
136
+ const raw = String(artifactPath || "").trim();
137
+ if (!raw)
138
+ return "";
139
+ if (/^(?:https?:|file:|[A-Za-z]:\\|\/)/.test(raw))
140
+ return "";
141
+ const relative = raw.startsWith("TARGET:")
142
+ ? raw.replace(/^TARGET:/, "").replace(/^\/+/, "")
143
+ : toPosix(path.join(toPosix(path.relative(target.projectRoot, taskDir)), raw));
144
+ return path.resolve(target.projectRoot, relative);
145
+ }
146
+ function isInsideDirectory(parent, child) {
147
+ const relative = path.relative(parent, child);
148
+ return Boolean(relative) && !relative.startsWith("..") && !path.isAbsolute(relative);
149
+ }
150
+ function lessonCandidateRows(content) {
151
+ const { header, rows } = tableAfterHeading(content, /^ID$/i);
152
+ const idIndex = firstColumn(header, ["ID", "候选 ID"]);
153
+ const statusIndex = firstColumn(header, ["Row Status", "行状态", "Status", "状态"]);
154
+ const titleIndex = firstColumn(header, ["Title", "标题"]);
155
+ const decisionIndex = firstColumn(header, ["Review Decision", "审查决定"]);
156
+ const targetIndex = firstColumn(header, ["Promotion Target", "沉淀目标"]);
157
+ const scopeIndex = firstColumn(header, ["Scope", "范围"]);
158
+ const boundaryIndex = firstColumn(header, ["Boundary Reason", "边界原因"]);
159
+ const moduleKeyIndex = firstColumn(header, ["Module Key", "模块 Key", "模块"]);
160
+ const detailArtifactIndex = firstColumn(header, ["Detail Artifact", "Lesson Detail", "详情产物", "详情文件"]);
161
+ const whyIndex = firstColumn(header, ["Why It Might Matter", "价值说明", "为什么重要"]);
162
+ const conflictIndex = firstColumn(header, ["Conflict Check", "冲突检查"]);
163
+ const requiredUpdateIndex = firstColumn(header, ["Required Standard Update", "必需标准更新"]);
164
+ const followUpIndex = firstColumn(header, ["Follow-up Task", "Followup Task", "后续任务"]);
165
+ if (idIndex < 0 || statusIndex < 0)
166
+ return { rows: [], missingColumns: [] };
167
+ const requiredColumnSpecs = [
168
+ ["Scope", scopeIndex],
169
+ ["Detail Artifact", detailArtifactIndex],
170
+ ["Boundary Reason", boundaryIndex],
171
+ ["Why It Might Matter", whyIndex],
172
+ ["Promotion Target", targetIndex],
173
+ ["Conflict Check", conflictIndex],
174
+ ["Required Standard Update", requiredUpdateIndex],
175
+ ["Follow-up Task", followUpIndex],
176
+ ];
177
+ const missingColumns = requiredColumnSpecs.filter(([, index]) => index < 0).map(([label]) => label);
178
+ return {
179
+ missingColumns,
180
+ rows: rows
181
+ .filter((row) => /^LC-[A-Za-z0-9-]+$/i.test(row[idIndex] || ""))
182
+ .map((row) => ({
183
+ id: row[idIndex] || "",
184
+ status: normalizeLessonCandidateStatus(row[statusIndex] || ""),
185
+ title: row[titleIndex] || "",
186
+ scope: scopeIndex >= 0 ? row[scopeIndex] || "" : "",
187
+ moduleKey: moduleKeyIndex >= 0 ? row[moduleKeyIndex] || "" : "",
188
+ detailArtifact: detailArtifactIndex >= 0 ? row[detailArtifactIndex] || "" : "",
189
+ boundaryReason: boundaryIndex >= 0 ? row[boundaryIndex] || "" : "",
190
+ whyItMightMatter: whyIndex >= 0 ? row[whyIndex] || "" : "",
191
+ reviewDecision: row[decisionIndex] || "",
192
+ promotionTarget: row[targetIndex] || "",
193
+ conflictCheck: conflictIndex >= 0 ? row[conflictIndex] || "" : "",
194
+ requiredStandardUpdate: requiredUpdateIndex >= 0 ? row[requiredUpdateIndex] || "" : "",
195
+ followUpTask: followUpIndex >= 0 ? row[followUpIndex] || "" : "",
196
+ originalRow: row.map((cell) => String(cell || "").trim()).join(" | "),
197
+ })),
198
+ };
199
+ }
200
+ function normalizeLessonCandidateStatus(value) {
201
+ return String(value || "")
202
+ .replace(/`/g, "")
203
+ .trim()
204
+ .toLowerCase()
205
+ .replaceAll("_", "-")
206
+ .replace(/\s+/g, "-");
207
+ }
208
+ function normalizeCandidateField(value) {
209
+ return String(value || "").replace(/`/g, "").trim().toLowerCase().replaceAll("_", "-").replace(/\s+/g, "-");
210
+ }
211
+ function aggregateLessonCandidateStatus(rows, declaredStatus) {
212
+ if (rows.length === 0)
213
+ return declaredStatus === "no-candidate-accepted" ? "no-candidate-accepted" : declaredStatus;
214
+ const statuses = rows.map((row) => row.status);
215
+ if (statuses.includes("ready-for-review"))
216
+ return "pending-review";
217
+ if (statuses.includes("needs-promotion"))
218
+ return "needs-promotion";
219
+ if (statuses.every((status) => status === "promoted"))
220
+ return "promoted";
221
+ if (statuses.every((status) => status === "rejected"))
222
+ return "rejected";
223
+ if (statuses.every((status) => ["promoted", "rejected"].includes(status)))
224
+ return "promoted";
225
+ return declaredStatus;
226
+ }
227
+ function noCandidateReason(content) {
228
+ const lines = String(content || "").split(/\r?\n/);
229
+ const start = lines.findIndex((line) => /^##\s*No-Candidate Reason\s*$/i.test(line.trim()));
230
+ if (start < 0)
231
+ return "";
232
+ const body = [];
233
+ for (const line of lines.slice(start + 1)) {
234
+ if (/^##\s+/.test(line))
235
+ break;
236
+ body.push(line);
237
+ }
238
+ return body.join("\n").replace(/`/g, "").trim();
239
+ }
@@ -0,0 +1,300 @@
1
+ // @ts-nocheck
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { lessonCandidatesFile, normalizeTarget, readFileSafe, toPosix, normalizeTaskId, localDate, } from "./core-shared.mjs";
5
+ import { parseLessonCandidateStatus } from "./task-lesson-candidates.mjs";
6
+ import { readCapabilityRegistry } from "./capability-registry.mjs";
7
+ import { createTask, resolveTaskDirectory } from "./task-lifecycle.mjs";
8
+ import { readPresetPackage, buildPresetAudit, renderPresetTemplate } from "./preset-registry.mjs";
9
+ import { firstColumn, updateMarkdownTableRow } from "./markdown-utils.mjs";
10
+ import { taskIdForDirectory } from "./task-scanner.mjs";
11
+ import { beginGovernanceSync, commitGovernanceSync, governanceRelativePaths, releaseGovernanceSync, } from "./governance-sync.mjs";
12
+ const presetId = "lesson-sedimentation";
13
+ export class LessonSedimentationError extends Error {
14
+ constructor(message, { code = "lesson-sedimentation-failed", status = 400, details = {}, recovery = [] } = {}) {
15
+ super(message);
16
+ this.name = "LessonSedimentationError";
17
+ this.code = code;
18
+ this.status = status;
19
+ this.details = details;
20
+ this.recovery = recovery;
21
+ }
22
+ }
23
+ export function createLessonSedimentationTask(targetInput, taskRef, candidateId, { dryRun = false, title = "" } = {}) {
24
+ const target = normalizeTarget(targetInput);
25
+ const sourceTaskDir = resolveTaskDirectory(target, taskRef);
26
+ const sourceTaskId = taskIdForDirectory(target, sourceTaskDir);
27
+ const sourceShortId = path.basename(sourceTaskDir);
28
+ const candidatePath = path.join(sourceTaskDir, lessonCandidatesFile);
29
+ const content = readFileSafe(candidatePath);
30
+ const candidateStatus = parseLessonCandidateStatus(content);
31
+ const candidate = candidateStatus.rows.find((row) => row.id === candidateId);
32
+ if (!candidate) {
33
+ throw new LessonSedimentationError(`Lesson candidate not found: ${candidateId}`, {
34
+ code: "lesson-candidate-not-found",
35
+ status: 404,
36
+ details: { candidateId, sourceTask: sourceTaskId },
37
+ recovery: [
38
+ "Open the source task lesson_candidates.md and confirm the candidate ID.",
39
+ "Refresh the Dashboard snapshot if the candidate was just added.",
40
+ ],
41
+ });
42
+ }
43
+ if (!["needs-promotion", "ready-for-review"].includes(candidate.status)) {
44
+ throw new LessonSedimentationError(`Lesson candidate must be ready-for-review or needs-promotion; current status is ${candidate.status}`, {
45
+ code: "lesson-candidate-not-actionable",
46
+ status: 409,
47
+ details: { candidateId, status: candidate.status, sourceTask: sourceTaskId },
48
+ recovery: [
49
+ "Set the candidate status to ready-for-review or needs-promotion after human review.",
50
+ "Use Copy lesson prompt if you only need the prompt without creating a task.",
51
+ ],
52
+ });
53
+ }
54
+ if (candidate.followUpTask && !/^pending$/i.test(candidate.followUpTask)) {
55
+ throw new LessonSedimentationError(`Lesson candidate already has follow-up task: ${candidate.followUpTask}`, {
56
+ code: "lesson-follow-up-exists",
57
+ status: 409,
58
+ details: { candidateId, followUpTask: candidate.followUpTask, sourceTask: sourceTaskId },
59
+ recovery: [
60
+ "Open the existing follow-up task instead of creating a duplicate.",
61
+ "If the existing task is wrong, edit the Follow-up Task cell back to pending after review.",
62
+ ],
63
+ });
64
+ }
65
+ const preset = readPresetPackage(presetId);
66
+ const slug = normalizeTaskId(`lesson-${sourceShortId.replace(/^\d{4}-\d{2}-\d{2}-/, "")}-${candidate.id}`);
67
+ const taskTitle = title || `Lesson sedimentation for ${candidate.id}`;
68
+ const locale = readCapabilityRegistry(target).locale;
69
+ let taskResult;
70
+ try {
71
+ taskResult = createTask(target.projectRoot, slug, {
72
+ title: taskTitle,
73
+ locale,
74
+ budget: "standard",
75
+ longRunning: true,
76
+ dryRun,
77
+ });
78
+ }
79
+ catch (error) {
80
+ if (/Task already exists:/i.test(error.message)) {
81
+ const existingTask = `TASKS/${localDate()}-${slug}`;
82
+ throw new LessonSedimentationError(error.message, {
83
+ code: "lesson-follow-up-directory-exists",
84
+ status: 409,
85
+ details: { candidateId, existingTask, sourceTask: sourceTaskId },
86
+ recovery: [
87
+ "Open the existing task directory and confirm whether it is the intended follow-up.",
88
+ "If it is valid, update the source candidate Follow-up Task cell to that task id.",
89
+ ],
90
+ });
91
+ }
92
+ throw error;
93
+ }
94
+ const followUpTaskId = taskResult.task.id;
95
+ const followUpDir = path.join(target.projectRoot, taskResult.task.path.replace(/^TARGET:/, ""));
96
+ const audit = buildPresetAudit(preset, {
97
+ taskId: followUpTaskId,
98
+ targetRoot: target.projectRoot,
99
+ entrypoint: "newTask",
100
+ writeScopes: [`${toPosix(path.relative(target.projectRoot, target.harness.tasksRoot))}/**`],
101
+ });
102
+ const prompt = renderLessonSedimentationPrompt(preset, {
103
+ target,
104
+ sourceTaskDir,
105
+ sourceTaskId,
106
+ sourceShortId,
107
+ candidate,
108
+ followUpTaskId,
109
+ });
110
+ const contextPacket = renderContextPacket({
111
+ target,
112
+ sourceTaskDir,
113
+ sourceTaskId,
114
+ candidate,
115
+ followUpTaskId,
116
+ audit,
117
+ });
118
+ const changes = [...taskResult.changes];
119
+ if (!dryRun) {
120
+ const governanceContext = beginGovernanceSync(target, { operation: `lesson-sediment ${sourceTaskId} ${candidate.id}` });
121
+ try {
122
+ appendToFollowUpTask({ followUpDir, sourceTaskId, candidate, prompt, contextPacket, audit });
123
+ updateSourceFollowUpTask(candidatePath, candidate.id, followUpTaskId);
124
+ changes.push({
125
+ destination: toPosix(path.relative(target.projectRoot, path.join(followUpDir, "task_plan.md"))),
126
+ source: "lesson-sedimentation",
127
+ action: "append-preset-context",
128
+ }, {
129
+ destination: toPosix(path.relative(target.projectRoot, path.join(followUpDir, "progress.md"))),
130
+ source: "lesson-sedimentation",
131
+ action: "append-preset-progress",
132
+ }, {
133
+ destination: toPosix(path.relative(target.projectRoot, path.join(followUpDir, "artifacts/lesson-sedimentation-prompt.md"))),
134
+ source: "lesson-sedimentation",
135
+ action: "create-prompt-artifact",
136
+ }, {
137
+ destination: toPosix(path.relative(target.projectRoot, path.join(followUpDir, "artifacts/preset-audit.json"))),
138
+ source: "lesson-sedimentation",
139
+ action: "create-preset-audit",
140
+ }, {
141
+ destination: toPosix(path.relative(target.projectRoot, candidatePath)),
142
+ source: lessonCandidatesFile,
143
+ action: "update-follow-up-task",
144
+ });
145
+ taskResult.governance = {
146
+ ...(taskResult.governance || {}),
147
+ lessonSedimentationCommit: commitGovernanceSync(governanceContext, governanceRelativePaths(changes), {
148
+ message: `chore(harness): record lesson sedimentation ${candidate.id}`,
149
+ }),
150
+ };
151
+ }
152
+ finally {
153
+ releaseGovernanceSync(governanceContext);
154
+ }
155
+ }
156
+ return {
157
+ dryRun,
158
+ event: "lesson-sedimentation-task",
159
+ preset: presetId,
160
+ sourceTask: sourceTaskId,
161
+ candidate,
162
+ followUpTask: {
163
+ id: followUpTaskId,
164
+ path: taskResult.task.path,
165
+ title: taskTitle,
166
+ },
167
+ prompt,
168
+ changes,
169
+ governance: taskResult.governance || null,
170
+ };
171
+ }
172
+ function renderLessonSedimentationPrompt(preset, values) {
173
+ const detailArtifact = resolveDetailArtifact(values.target, values.sourceTaskDir, values.candidate);
174
+ const prompt = renderPresetTemplate(preset, preset.entrypoints.newTask?.templates?.prompt, {
175
+ sourceTaskId: values.sourceTaskId,
176
+ sourceShortId: values.sourceShortId,
177
+ candidateId: values.candidate.id,
178
+ candidateTitle: values.candidate.title,
179
+ candidateScope: values.candidate.scope,
180
+ candidateModuleKey: values.candidate.moduleKey || "n/a",
181
+ detailArtifact: detailArtifact.prefixedPath || "not provided",
182
+ boundaryReason: values.candidate.boundaryReason,
183
+ whyItMightMatter: values.candidate.whyItMightMatter,
184
+ promotionTarget: values.candidate.promotionTarget,
185
+ conflictCheck: values.candidate.conflictCheck,
186
+ requiredStandardUpdate: values.candidate.requiredStandardUpdate,
187
+ followUpTaskId: values.followUpTaskId,
188
+ });
189
+ return prompt.trim();
190
+ }
191
+ function renderContextPacket({ target, sourceTaskDir, sourceTaskId, candidate, followUpTaskId, audit }) {
192
+ const sourceLessonPath = `TARGET:${toPosix(path.relative(target.projectRoot, path.join(sourceTaskDir, lessonCandidatesFile)))}`;
193
+ const detailArtifact = resolveDetailArtifact(target, sourceTaskDir, candidate);
194
+ const sourceReview = summarizeMarkdown(readFileSafe(path.join(sourceTaskDir, "review.md")));
195
+ const sourceFindings = summarizeMarkdown(readFileSafe(path.join(sourceTaskDir, "findings.md")));
196
+ const sourceProgress = summarizeMarkdown(readFileSafe(path.join(sourceTaskDir, "progress.md")));
197
+ const sourceLessonDetail = summarizeMarkdown(detailArtifact.path ? readFileSafe(detailArtifact.path) : "");
198
+ return [
199
+ "## Lesson Sedimentation Context Packet",
200
+ "",
201
+ "| Field | Value |",
202
+ "| --- | --- |",
203
+ `| Preset | ${presetId} |`,
204
+ `| Follow-up Task | ${followUpTaskId} |`,
205
+ `| Source Task | ${sourceTaskId} |`,
206
+ `| Source Lesson Candidates | ${sourceLessonPath} |`,
207
+ `| Source Lesson Detail | ${detailArtifact.prefixedPath || "not provided"} |`,
208
+ `| Candidate ID | ${candidate.id} |`,
209
+ `| Candidate Title | ${markdownCell(candidate.title)} |`,
210
+ `| Original Candidate Row | ${markdownCell(candidate.originalRow || "")} |`,
211
+ `| Scope | ${markdownCell(candidate.scope || "unspecified")} |`,
212
+ `| Module Key | ${markdownCell(candidate.moduleKey || "n/a")} |`,
213
+ `| Detail Summary | ${markdownCell(sourceLessonDetail || "not recorded")} |`,
214
+ `| Boundary Reason | ${markdownCell(candidate.boundaryReason || "unspecified")} |`,
215
+ `| Why It Might Matter | ${markdownCell(candidate.whyItMightMatter || "unspecified")} |`,
216
+ `| Promotion Target | ${markdownCell(candidate.promotionTarget || "unspecified")} |`,
217
+ `| Conflict Check | ${markdownCell(candidate.conflictCheck || "pending")} |`,
218
+ `| Required Standard Update | ${markdownCell(candidate.requiredStandardUpdate || "pending")} |`,
219
+ `| Review Summary | ${markdownCell(sourceReview)} |`,
220
+ `| Findings Summary | ${markdownCell(sourceFindings)} |`,
221
+ `| Evidence Summary | ${markdownCell(sourceProgress)} |`,
222
+ `| Preset Manifest | ${audit.manifestPath} |`,
223
+ "",
224
+ ].join("\n");
225
+ }
226
+ function resolveDetailArtifact(target, sourceTaskDir, candidate) {
227
+ const raw = String(candidate.detailArtifact || "").trim();
228
+ if (!raw || /^(?:n\/a|none|pending)$/i.test(raw))
229
+ return { path: "", prefixedPath: "" };
230
+ const absolute = raw.startsWith("TARGET:")
231
+ ? path.resolve(target.projectRoot, raw.replace(/^TARGET:/, "").replace(/^\/+/, ""))
232
+ : path.resolve(sourceTaskDir, raw.replace(/^\.?\//, ""));
233
+ if (!absolute.startsWith(path.resolve(sourceTaskDir) + path.sep))
234
+ return { path: "", prefixedPath: "" };
235
+ return {
236
+ path: absolute,
237
+ prefixedPath: `TARGET:${toPosix(path.relative(target.projectRoot, absolute))}`,
238
+ };
239
+ }
240
+ function appendToFollowUpTask({ followUpDir, sourceTaskId, candidate, prompt, contextPacket, audit }) {
241
+ const taskPlanPath = path.join(followUpDir, "task_plan.md");
242
+ const progressPath = path.join(followUpDir, "progress.md");
243
+ const artifactsDir = path.join(followUpDir, "artifacts");
244
+ fs.mkdirSync(artifactsDir, { recursive: true });
245
+ fs.writeFileSync(path.join(artifactsDir, "lesson-sedimentation-prompt.md"), `${prompt}\n`);
246
+ fs.writeFileSync(path.join(artifactsDir, "preset-audit.json"), `${JSON.stringify(audit, null, 2)}\n`);
247
+ const taskPlanAppend = [
248
+ "",
249
+ "## Lesson Sedimentation Preset",
250
+ "",
251
+ "Task Preset: lesson-sedimentation",
252
+ "Preset Version: 1",
253
+ "Task Kind: lesson-sedimentation",
254
+ `Source Task: ${sourceTaskId}`,
255
+ `Source Candidate: ${candidate.id}`,
256
+ `Promotion Target: ${candidate.promotionTarget || "pending"}`,
257
+ "",
258
+ contextPacket.trimEnd(),
259
+ "",
260
+ "## Execution Prompt",
261
+ "",
262
+ "The copyable prompt for a new agent session is stored in `artifacts/lesson-sedimentation-prompt.md`.",
263
+ "",
264
+ ].join("\n");
265
+ fs.appendFileSync(taskPlanPath, taskPlanAppend);
266
+ fs.appendFileSync(progressPath, [
267
+ "",
268
+ "### Lesson sedimentation task created",
269
+ "",
270
+ `- Source task: ${sourceTaskId}`,
271
+ `- Source candidate: ${candidate.id}`,
272
+ "- Next: paste the execution prompt into a fresh agent session and require diff-first review before applying changes.",
273
+ "",
274
+ ].join("\n"));
275
+ }
276
+ function updateSourceFollowUpTask(candidatePath, candidateId, followUpTaskId) {
277
+ const content = readFileSafe(candidatePath);
278
+ const update = updateMarkdownTableRow(content, /^ID$/i, (header, row) => {
279
+ const idIndex = firstColumn(header, ["ID", "候选 ID"]);
280
+ const followUpIndex = firstColumn(header, ["Follow-up Task", "Followup Task", "后续任务"]);
281
+ if (idIndex < 0 || followUpIndex < 0 || row[idIndex] !== candidateId)
282
+ return null;
283
+ const next = [...row];
284
+ next[followUpIndex] = followUpTaskId;
285
+ return next;
286
+ });
287
+ if (!update.matched)
288
+ throw new Error(`Could not update Follow-up Task column for ${candidateId}`);
289
+ fs.writeFileSync(candidatePath, update.content.endsWith("\n") ? update.content : `${update.content}\n`);
290
+ }
291
+ function markdownCell(value) {
292
+ return String(value || "").replace(/\r?\n/g, " ").replaceAll("|", "\\|").trim();
293
+ }
294
+ function summarizeMarkdown(content) {
295
+ const lines = String(content || "")
296
+ .split(/\r?\n/)
297
+ .map((line) => line.replace(/^#+\s*/, "").trim())
298
+ .filter((line) => line && !/^\|?\s*-{3,}/.test(line));
299
+ return lines.slice(0, 4).join(" / ") || "not recorded";
300
+ }
@@ -0,0 +1,84 @@
1
+ // @ts-nocheck
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { toPosix } from "../core-shared.mjs";
5
+ import { appendLongRunningContractFile, moduleTemplateFiles, taskFilesForBudget, } from "./template-files.mjs";
6
+ import { discoverImplicitHarnessTarget } from "../harness-paths.mjs";
7
+ export function planCreateTaskChanges({ target, directory, normalizedModuleKey, normalizedLocale, normalizedBudget, longRunning, presetContext }) {
8
+ const changes = [];
9
+ if (normalizedModuleKey) {
10
+ const moduleDirectory = path.dirname(directory);
11
+ for (const [destination, source] of moduleTemplateFiles({ locale: normalizedLocale })) {
12
+ const destinationPath = path.join(moduleDirectory, destination);
13
+ if (fs.existsSync(destinationPath))
14
+ continue;
15
+ changes.push({
16
+ destination: toPosix(path.relative(target.projectRoot, destinationPath)),
17
+ source,
18
+ action: "create",
19
+ });
20
+ }
21
+ }
22
+ for (const [destination, source] of appendLongRunningContractFile(taskFilesForBudget({ budget: normalizedBudget, locale: normalizedLocale }), {
23
+ locale: normalizedLocale,
24
+ longRunning,
25
+ })) {
26
+ changes.push({
27
+ destination: toPosix(path.relative(target.projectRoot, path.join(directory, destination))),
28
+ source,
29
+ action: "create",
30
+ });
31
+ }
32
+ if (presetContext) {
33
+ for (const evidence of presetContext.evidenceFiles || []) {
34
+ changes.push({ destination: toPosix(evidence.relativePath), source: evidence.source, action: "create" });
35
+ }
36
+ for (const resource of presetContext.resourceFiles || []) {
37
+ changes.push({ destination: toPosix(resource.relativePath), source: resource.source, action: "create" });
38
+ }
39
+ for (const [kind, rows] of Object.entries(presetContext.resourceIndexRows || {})) {
40
+ if (!rows.length)
41
+ continue;
42
+ const destination = kind === "references" ? "references/INDEX.md" : "artifacts/INDEX.md";
43
+ changes.push({
44
+ destination: toPosix(path.relative(target.projectRoot, path.join(directory, destination))),
45
+ source: `preset-${kind}-index`,
46
+ action: "update",
47
+ });
48
+ }
49
+ }
50
+ return changes;
51
+ }
52
+ export function refreshPresetCommandAudit(target, presetContext, { commandWriteScopes = [], dryRun = false } = {}) {
53
+ const scopes = [...new Set(commandWriteScopes.filter(Boolean))];
54
+ presetContext.audit = {
55
+ ...presetContext.audit,
56
+ presetWriteScopes: presetContext.audit.writeScopes || [],
57
+ commandWriteScopes: scopes,
58
+ };
59
+ for (const evidence of presetContext.evidenceFiles || []) {
60
+ if (evidence.source !== "preset-audit")
61
+ continue;
62
+ evidence.content = `${JSON.stringify(presetContext.audit, null, 2)}\n`;
63
+ if (dryRun)
64
+ continue;
65
+ fs.writeFileSync(path.join(target.projectRoot, evidence.relativePath), evidence.content);
66
+ }
67
+ }
68
+ export function targetInputFromSessionFile(fromSession) {
69
+ if (!fromSession)
70
+ return "";
71
+ try {
72
+ const parsed = JSON.parse(fs.readFileSync(path.resolve(fromSession), "utf8"));
73
+ return parsed.target || "";
74
+ }
75
+ catch {
76
+ return "";
77
+ }
78
+ }
79
+ export function resolveImplicitCreateTarget(targetInput, fromSession) {
80
+ const sessionTarget = targetInputFromSessionFile(fromSession);
81
+ if (targetInput && targetInput !== ".")
82
+ return targetInput;
83
+ return sessionTarget || discoverImplicitHarnessTarget(targetInput || ".") || targetInput || "";
84
+ }