coding-agent-harness 1.0.5 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,439 @@
1
+ // @ts-nocheck
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { visualMapFile, legacyVisualRoadmapFile, lessonCandidatesFile, longRunningTaskContractFile, toPosix, readFileSafe, readJsonSafe, walkFiles, isArchivedHarnessPath, titleFromMarkdown, } from "./core-shared.mjs";
5
+ import { tableAfterHeading, firstColumn, splitList, splitDependencies, } from "./markdown-utils.mjs";
6
+ import { normalizePhaseActor, normalizePhaseKind, phaseCompletionAverage, } from "./phase-kind.mjs";
7
+ import { legacyAuditIssues, parseTaskAuditMetadata, scaffoldProvenanceSummaryFromTaskAudit, taskAuditMaterialIssues, } from "./task-audit-metadata.mjs";
8
+ import { parseTaskBudget, parseTaskContractInfo, parseTaskMetadata, parseTaskStateInfo, } from "./task-metadata.mjs";
9
+ import { isLessonCandidateDecisionComplete, parseLessonCandidateStatus, validateLessonCandidateDetailArtifacts, } from "./task-lesson-candidates.mjs";
10
+ import { assessMaterialsReadiness, collectReviewRisks, collectStateConflicts, deriveLifecycleState, deriveReviewQueueState, deriveTaskQueues, isBlockingReviewRisk, parseAgentReviewSubmission, parseReviewConfirmation, parseTaskIdentity, parseTaskTombstone, requiresReviewMaterials, taskReviewStatus, taskScannerVersion, } from "./task-review-model.mjs";
11
+ import { resolveHarnessPaths, safeAdoptionCapability, taskIdFromDirectory, taskLocalWalkthrough, } from "./harness-paths.mjs";
12
+ export { parseTaskBudget, parseTaskContractInfo, parseTaskMetadata, parseTaskState, parseTaskStateInfo, } from "./task-metadata.mjs";
13
+ export { collectReviewRisks, deriveLifecycleState, deriveReviewQueueState, isBlockingReviewRisk, parseAgentReviewSubmission, parseReviewConfirmation, parseTaskIdentity, parseTaskTombstone, requiresReviewMaterials, taskReviewStatus, taskScannerVersion, } from "./task-review-model.mjs";
14
+ export { parseTaskAuditMetadata, } from "./task-audit-metadata.mjs";
15
+ export { allowedLessonCandidateRowStatuses, allowedLessonCandidateTaskStatuses, isLessonCandidateDecisionComplete, parseLessonCandidateStatus, reviewCompleteLessonCandidateStatuses, } from "./task-lesson-candidates.mjs";
16
+ export function parsePhases(taskPlanContent) {
17
+ const { header, rows } = tableAfterHeading(taskPlanContent, /^Phase ID$/i);
18
+ if (rows.length === 0)
19
+ return [];
20
+ const indexes = {
21
+ id: firstColumn(header, ["Phase ID", "阶段 ID"]),
22
+ kind: firstColumn(header, ["Kind", "阶段类型", "类型"]),
23
+ dependsOn: firstColumn(header, ["Depends On", "依赖"]),
24
+ state: firstColumn(header, ["State", "状态"]),
25
+ completion: firstColumn(header, ["Completion", "完成度"]),
26
+ output: firstColumn(header, ["Output", "产出"]),
27
+ requiredEvidence: firstColumn(header, ["Required Evidence", "必要证据"]),
28
+ exitCommand: firstColumn(header, ["Exit Command", "出口命令", "退出命令"]),
29
+ actor: firstColumn(header, ["Actor", "执行者", "角色"]),
30
+ evidenceStatus: firstColumn(header, ["Evidence Status", "证据状态"]),
31
+ blockingRisk: firstColumn(header, ["Blocking Risk", "阻塞风险"]),
32
+ owner: firstColumn(header, ["Owner / Handoff", "负责人 / 交接"]),
33
+ };
34
+ return rows.map((row) => ({
35
+ id: row[indexes.id] || "",
36
+ kind: normalizePhaseKind(row[indexes.kind]),
37
+ dependsOn: splitDependencies(row[indexes.dependsOn] || ""),
38
+ state: row[indexes.state] || "planned",
39
+ completion: Number.parseInt(String(row[indexes.completion] || "0").replace("%", ""), 10) || 0,
40
+ output: row[indexes.output] || "",
41
+ requiredEvidence: splitList(row[indexes.requiredEvidence] || ""),
42
+ exitCommand: row[indexes.exitCommand] || "",
43
+ actor: normalizePhaseActor(row[indexes.actor]),
44
+ evidenceStatus: row[indexes.evidenceStatus] || "missing",
45
+ blockingRisk: row[indexes.blockingRisk] || "",
46
+ owner: row[indexes.owner] || "",
47
+ }));
48
+ }
49
+ export function readTaskContractFile(taskDir, fileName, legacyContent = "") {
50
+ const filePath = path.join(taskDir, fileName);
51
+ const content = readFileSafe(filePath);
52
+ if (content.trim())
53
+ return { path: filePath, content, source: "standalone" };
54
+ return { path: filePath, content: legacyContent, source: legacyContent.trim() ? "legacy" : "missing" };
55
+ }
56
+ export function readVisualMapContractFile(taskDir, legacyContent = "") {
57
+ const canonicalPath = path.join(taskDir, visualMapFile);
58
+ const canonical = readFileSafe(canonicalPath);
59
+ if (canonical.trim())
60
+ return { path: canonicalPath, content: canonical, source: "canonical", status: "present" };
61
+ const legacyPath = path.join(taskDir, legacyVisualRoadmapFile);
62
+ const legacy = readFileSafe(legacyPath);
63
+ if (legacy.trim())
64
+ return { path: legacyPath, content: legacy, source: "legacy", status: "legacy-only" };
65
+ return {
66
+ path: canonicalPath,
67
+ content: legacyContent,
68
+ source: legacyContent.trim() ? "legacy" : "missing",
69
+ status: legacyContent.trim() ? "legacy-only" : "missing",
70
+ };
71
+ }
72
+ export function isActiveTaskState(state) {
73
+ return ["active", "planned", "not_started", "in_progress", "review", "blocked", "reopened", "current-evidence"].includes(state);
74
+ }
75
+ export function listTaskPlanPaths(target) {
76
+ const harnessPaths = target.harness || resolveHarnessPaths(target);
77
+ const taskRoots = harnessPaths.taskRoots;
78
+ return taskRoots
79
+ .flatMap(walkFiles)
80
+ .filter((file) => file.endsWith("task_plan.md"))
81
+ .filter((file) => !file.includes(`${path.sep}_task-template${path.sep}`))
82
+ .filter((file) => !file.includes(`${path.sep}_optional-structures${path.sep}`))
83
+ .filter((file) => !isArchivedHarnessPath(file));
84
+ }
85
+ export function taskIdForDirectory(target, taskDir) {
86
+ return taskIdFromDirectory(target.harness || resolveHarnessPaths(target), taskDir);
87
+ }
88
+ export function inferTaskClassification({ id, title, relative, explicitModule, legacyCandidate = false }) {
89
+ if (explicitModule) {
90
+ return {
91
+ module: explicitModule,
92
+ source: "explicit",
93
+ bucket: "module",
94
+ };
95
+ }
96
+ const text = `${id} ${title} ${relative}`.toLowerCase();
97
+ const rules = [
98
+ ["dashboard", /dashboard|visibility|cockpit|console|ui|frontend|view|页面|看板|驾驶舱/],
99
+ ["migration", new RegExp(`migration|migrate|adoption|legacy|${escapeRegExp(safeAdoptionCapability)}|迁移|历史|兼容`)],
100
+ ["task-lifecycle", /task|phase|lifecycle|planning|计划|任务|阶段/],
101
+ ["review-quality", /review|finding|evidence|qa|test|regression|审查|证据|回归|测试/],
102
+ ["release-docs", /docs-release|readme|guide|install|playbook|文档|安装|指南/],
103
+ ["repo-governance", /git|ci|source-package|private|boundary|repo|branch|pr|仓库|边界/],
104
+ ["automation-cli", /cli|command|script|harness\.mjs|自动化|命令/],
105
+ ];
106
+ const match = rules.find(([, pattern]) => pattern.test(text));
107
+ return {
108
+ module: match ? match[0] : legacyCandidate ? "legacy-unclassified" : "unclassified",
109
+ source: match ? "inferred" : "fallback",
110
+ bucket: legacyCandidate ? "legacy" : "current",
111
+ };
112
+ }
113
+ function escapeRegExp(value) {
114
+ return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
115
+ }
116
+ export function assessBriefQuality(content, { source = "missing" } = {}) {
117
+ const text = String(content || "").trim();
118
+ const issues = [];
119
+ if (source !== "standalone")
120
+ issues.push("missing-standalone-brief");
121
+ if (text.length < 120)
122
+ issues.push("too-short");
123
+ if (!/^##\s+/m.test(text))
124
+ issues.push("missing-sections");
125
+ if (/\[(?:outcome|scope|risk|evidence|next|目标|范围|风险|证据|下一步)[^\]]*\]/i.test(text))
126
+ issues.push("unfilled-placeholder");
127
+ return { status: issues.length ? "fail" : "pass", issues };
128
+ }
129
+ export function explicitVisualMapStatus(briefContent) {
130
+ const match = String(briefContent || "").match(/^Visual Map Status:\s*(present|not-needed|missing|legacy-only)\s*$/im);
131
+ return match ? match[1] : "";
132
+ }
133
+ export function taskMigrationClassification(state, visualMapStatus) {
134
+ if (state === "unknown")
135
+ return "unknown-needs-human";
136
+ if (isActiveTaskState(state))
137
+ return "active";
138
+ if (visualMapStatus === "present" || visualMapStatus === "legacy-only")
139
+ return "historical-with-diagram";
140
+ return "historical-no-map-needed";
141
+ }
142
+ export function requiresCanonicalVisualMap(task) {
143
+ return ["active", "reopened", "current-evidence", "historical-with-diagram"].includes(task.migrationClassification);
144
+ }
145
+ export function taskCutoverCounters(tasks) {
146
+ const legacyVisualOnlyCount = tasks.filter((task) => task.visualMapStatus === "legacy-only").length;
147
+ const unknownClassificationCount = tasks.filter((task) => task.migrationClassification === "unknown-needs-human").length;
148
+ const weakBriefCount = tasks.filter((task) => task.briefQuality?.status !== "pass").length;
149
+ const visualMapRequiredCount = tasks.filter(requiresCanonicalVisualMap).length;
150
+ const missingCanonicalVisualMapCount = tasks.filter((task) => requiresCanonicalVisualMap(task) && task.visualMapSource !== "canonical").length;
151
+ return {
152
+ legacyVisualOnlyCount,
153
+ unknownClassificationCount,
154
+ weakBriefCount,
155
+ visualMapRequiredCount,
156
+ missingCanonicalVisualMapCount,
157
+ };
158
+ }
159
+ export function collectTasks(target, { requireGeneratedScaffoldProvenance = false, taskPlanPaths, closeoutContent } = {}) {
160
+ const harnessPaths = target.harness || resolveHarnessPaths(target);
161
+ const paths = taskPlanPaths || listTaskPlanPaths(target);
162
+ const closeout = closeoutContent ?? (harnessPaths.version === 2 ? "" : readFileSafe(harnessPaths.legacy.closeoutPath));
163
+ return paths.map((taskPlanPath) => {
164
+ const taskDir = path.dirname(taskPlanPath);
165
+ const taskPlan = readFileSafe(taskPlanPath);
166
+ const brief = readTaskContractFile(taskDir, "brief.md", "");
167
+ const executionStrategyPath = path.join(taskDir, "execution_strategy.md");
168
+ const indexPath = path.join(taskDir, "INDEX.md");
169
+ const progressPath = path.join(taskDir, "progress.md");
170
+ const reviewPath = path.join(taskDir, "review.md");
171
+ const findingsPath = path.join(taskDir, "findings.md");
172
+ const lessonCandidatesPath = path.join(taskDir, lessonCandidatesFile);
173
+ const longRunningContractPath = path.join(taskDir, longRunningTaskContractFile);
174
+ const visualMap = readVisualMapContractFile(taskDir, taskPlan);
175
+ const progress = readFileSafe(progressPath);
176
+ const review = readFileSafe(reviewPath);
177
+ const indexContent = readFileSafe(indexPath);
178
+ const parsedLessonCandidates = parseLessonCandidateStatus(readFileSafe(lessonCandidatesPath));
179
+ const lessonDetailIssues = validateLessonCandidateDetailArtifacts(target, taskDir, parsedLessonCandidates);
180
+ const lessonCandidates = lessonDetailIssues.length
181
+ ? { ...parsedLessonCandidates, issues: [...parsedLessonCandidates.issues, ...lessonDetailIssues] }
182
+ : parsedLessonCandidates;
183
+ const phases = parsePhases(visualMap.content);
184
+ const completion = phaseCompletionAverage(phases);
185
+ const relative = toPosix(path.relative(target.projectRoot, taskDir));
186
+ const id = taskIdForDirectory(target, taskDir);
187
+ const identity = parseTaskIdentity(taskPlan, id);
188
+ const tombstone = parseTaskTombstone(taskPlan);
189
+ const title = titleFromMarkdown(brief.content || taskPlan, path.basename(taskDir));
190
+ const stateInfo = parseTaskStateInfo(progress);
191
+ const budget = parseTaskBudget(taskPlan);
192
+ const metadata = parseTaskMetadata(taskPlan);
193
+ const taskContract = parseTaskContractInfo(taskPlan);
194
+ const taskAudit = parseTaskAuditMetadata(indexContent, {
195
+ required: requireGeneratedScaffoldProvenance && taskContract.generated,
196
+ });
197
+ const scaffoldProvenance = { summary: scaffoldProvenanceSummaryFromTaskAudit(taskAudit) };
198
+ const explicitModule = id.startsWith("MODULES/") ? id.split("/")[1] : null;
199
+ const legacyCandidate = brief.source !== "standalone" || visualMap.status === "legacy-only" || !fs.existsSync(executionStrategyPath);
200
+ const classification = inferTaskClassification({ id, title, relative, explicitModule, legacyCandidate });
201
+ const briefVisualStatus = explicitVisualMapStatus(brief.content);
202
+ const visualMapStatus = briefVisualStatus === "not-needed" && visualMap.status === "missing" ? "not-needed" : visualMap.status;
203
+ const risks = collectReviewRisks(review);
204
+ const reviewSubmission = parseAgentReviewSubmission(review, { taskKey: identity.taskKey });
205
+ const reviewConfirmation = parseReviewConfirmation(review, {
206
+ taskKey: identity.taskKey,
207
+ taskAudit,
208
+ projectRoot: target.projectRoot,
209
+ taskDir,
210
+ indexPath,
211
+ reviewPath,
212
+ progressPath,
213
+ });
214
+ const reviewStatus = taskReviewStatus({ reviewContent: review, risks, confirmation: reviewConfirmation, submission: reviewSubmission });
215
+ const closeoutInfo = taskCloseoutInfo(target, taskPlanPath, closeout);
216
+ const effectiveCloseoutStatus = budget === "simple" && stateInfo.state === "done" && completion === 100
217
+ ? "closed"
218
+ : closeoutInfo.status;
219
+ const lifecycleState = deriveLifecycleState({ state: stateInfo.state, reviewStatus, closeoutStatus: effectiveCloseoutStatus, budget });
220
+ const materialReadiness = assessMaterialsReadiness({
221
+ budget,
222
+ taskDir,
223
+ taskPlan,
224
+ brief,
225
+ visualMap,
226
+ reviewSubmission,
227
+ lessonCandidates,
228
+ phases,
229
+ longRunningContractPath,
230
+ reviewSurfaceRequired: requiresReviewMaterials({
231
+ state: stateInfo.state,
232
+ lifecycleState,
233
+ closeoutStatus: effectiveCloseoutStatus,
234
+ }),
235
+ });
236
+ const materialIssues = [
237
+ ...materialReadiness.issues,
238
+ ...taskAuditMaterialIssues(target, taskDir, taskAudit),
239
+ ...legacyAuditIssues(target, taskDir, { briefContent: brief.content, reviewContent: review }),
240
+ ];
241
+ const stateConflicts = collectStateConflicts({ state: stateInfo.state, reviewStatus, closeoutStatus: effectiveCloseoutStatus, lifecycleState, budget });
242
+ const reviewQueueState = deriveReviewQueueState({
243
+ state: stateInfo.state,
244
+ lifecycleState,
245
+ reviewStatus,
246
+ closeoutStatus: effectiveCloseoutStatus,
247
+ budget,
248
+ walkthroughPath: closeoutInfo.walkthroughPath,
249
+ lessonCandidateDecisionComplete: isLessonCandidateDecisionComplete(lessonCandidates),
250
+ materialsReady: materialIssues.length === 0,
251
+ deletionState: tombstone.deletionState,
252
+ });
253
+ const queueModel = deriveTaskQueues({
254
+ id,
255
+ title,
256
+ state: stateInfo.state,
257
+ budget,
258
+ reviewStatus,
259
+ reviewSubmission,
260
+ reviewConfirmation,
261
+ reviewQueueState,
262
+ materialIssues,
263
+ risks,
264
+ stateConflicts,
265
+ lessonCandidates,
266
+ closeoutStatus: effectiveCloseoutStatus,
267
+ tombstone,
268
+ taskDir,
269
+ target,
270
+ });
271
+ return {
272
+ id,
273
+ taskKey: identity.taskKey,
274
+ currentPath: `TARGET:${relative}`,
275
+ originalPath: `TARGET:${relative}`,
276
+ aliases: [],
277
+ identitySource: identity.identitySource,
278
+ shortId: path.basename(taskDir),
279
+ title,
280
+ path: `TARGET:${relative}`,
281
+ taskPlanPath: `TARGET:${toPosix(path.relative(target.projectRoot, taskPlanPath))}`,
282
+ executionStrategyPath: `TARGET:${toPosix(path.relative(target.projectRoot, executionStrategyPath))}`,
283
+ progressPath: `TARGET:${toPosix(path.relative(target.projectRoot, progressPath))}`,
284
+ reviewPath: `TARGET:${toPosix(path.relative(target.projectRoot, reviewPath))}`,
285
+ findingsPath: `TARGET:${toPosix(path.relative(target.projectRoot, findingsPath))}`,
286
+ module: explicitModule,
287
+ inferredModule: classification.module,
288
+ classificationSource: classification.source,
289
+ classificationBucket: classification.bucket,
290
+ briefSource: brief.source,
291
+ briefPath: `TARGET:${toPosix(path.relative(target.projectRoot, brief.path))}`,
292
+ visualMapSource: visualMap.source,
293
+ visualMapStatus,
294
+ visualMapPath: `TARGET:${toPosix(path.relative(target.projectRoot, visualMap.path))}`,
295
+ legacyVisualRoadmapPresent: fs.existsSync(path.join(taskDir, legacyVisualRoadmapFile)),
296
+ briefQuality: assessBriefQuality(brief.content, { source: brief.source }),
297
+ migrationClassification: taskMigrationClassification(stateInfo.state, visualMapStatus),
298
+ roadmapSource: visualMap.source,
299
+ state: stateInfo.state,
300
+ budget,
301
+ taskContractVersion: taskContract.version,
302
+ taskContractGenerated: taskContract.generated,
303
+ stateSource: stateInfo.source,
304
+ stateRaw: stateInfo.raw,
305
+ taskKind: metadata.kind,
306
+ taskPreset: metadata.preset,
307
+ presetVersion: metadata.presetVersion,
308
+ migrationTargetLevel: metadata.migrationTargetLevel,
309
+ migrationAchievedLevel: metadata.migrationAchievedLevel,
310
+ evidenceBundle: formatEvidenceBundle(metadata.evidenceBundle),
311
+ migrationSnapshot: collectMigrationSnapshot(target, metadata),
312
+ scaffoldProvenance: scaffoldProvenance.summary,
313
+ taskAudit: taskAudit.summary,
314
+ lifecycleState,
315
+ reviewStatus,
316
+ reviewSubmitted: Boolean(reviewSubmission?.submitted),
317
+ reviewSubmission,
318
+ reviewQueueState,
319
+ reviewConfirmation,
320
+ materialsReady: materialIssues.length === 0,
321
+ materialIssues,
322
+ taskQueues: queueModel.taskQueues,
323
+ queueReasons: queueModel.queueReasons,
324
+ repairPrompt: queueModel.repairPrompt,
325
+ closeoutStatus: effectiveCloseoutStatus,
326
+ walkthroughPath: closeoutInfo.walkthroughPath ? `TARGET:${closeoutInfo.walkthroughPath}` : "",
327
+ lessonCandidatePath: fs.existsSync(lessonCandidatesPath)
328
+ ? `TARGET:${toPosix(path.relative(target.projectRoot, lessonCandidatesPath))}`
329
+ : "",
330
+ lessonCandidateStatus: lessonCandidates.status,
331
+ lessonCandidateReviewDecision: lessonCandidates.reviewDecision,
332
+ lessonCandidatePromotionState: lessonCandidates.promotionState,
333
+ lessonCandidateCloseoutToken: lessonCandidates.closeoutToken,
334
+ lessonCandidateRowCount: lessonCandidates.rows.length,
335
+ lessonCandidateRows: lessonCandidates.rows,
336
+ lessonCandidateOpenCount: lessonCandidates.openCount,
337
+ lessonCandidateIssues: lessonCandidates.issues,
338
+ lessonCandidateDecisionComplete: isLessonCandidateDecisionComplete(lessonCandidates),
339
+ longRunningContractPath: fs.existsSync(longRunningContractPath)
340
+ ? `TARGET:${toPosix(path.relative(target.projectRoot, longRunningContractPath))}`
341
+ : "",
342
+ longRunningContractStatus: fs.existsSync(longRunningContractPath) ? "present" : "missing",
343
+ deletionState: tombstone.deletionState,
344
+ supersededBy: tombstone.supersededBy,
345
+ supersedes: tombstone.supersedes,
346
+ deleteReason: tombstone.deleteReason,
347
+ hiddenByDefault: tombstone.hiddenByDefault,
348
+ reopenEligible: tombstone.reopenEligible,
349
+ archiveEligible: tombstone.archiveEligible,
350
+ tombstoneSourcePath: tombstone.tombstoneSourcePath
351
+ ? `TARGET:${toPosix(path.relative(target.projectRoot, path.join(taskDir, "task_plan.md")))}#Task Tombstone`
352
+ : "",
353
+ stateConflicts,
354
+ completion,
355
+ phases,
356
+ risks,
357
+ evidence: collectEvidence(progress),
358
+ handoffs: collectHandoffs(progress, title),
359
+ dependencies: [],
360
+ };
361
+ });
362
+ }
363
+ function collectMigrationSnapshot(target, metadata) {
364
+ if (metadata.preset !== "legacy-migration")
365
+ return null;
366
+ const evidenceBundle = String(metadata.evidenceBundle || "").replace(/^TARGET:/, "").replace(/^\/+/, "");
367
+ const bundlePath = evidenceBundle ? path.join(target.projectRoot, evidenceBundle) : "";
368
+ const sessionPath = bundlePath ? path.join(bundlePath, "session.json") : "";
369
+ const session = sessionPath && fs.existsSync(sessionPath) ? readJsonSafe(sessionPath, null) : null;
370
+ const summary = session?.plan?.summary || {};
371
+ return {
372
+ targetLevel: metadata.migrationTargetLevel || "",
373
+ achievedLevel: metadata.migrationAchievedLevel || "",
374
+ evidenceBundle: evidenceBundle ? `TARGET:${evidenceBundle}` : "",
375
+ evidencePresent: Boolean(bundlePath && fs.existsSync(bundlePath)),
376
+ sessionPresent: Boolean(session),
377
+ sessionResult: session?.result || "",
378
+ normalStatus: session?.checks?.normal?.status || "",
379
+ strictStatus: session?.checks?.strict?.status || "",
380
+ strictDeferred: Boolean(session?.strictDeferred),
381
+ warnings: Number(summary.warnings || 0),
382
+ taskActions: Number(summary.taskActions || 0),
383
+ reviewSchemaGaps: Number(summary.reviewSchemaGaps || 0),
384
+ legacyReferenceGaps: Number(summary.legacyReferenceGaps || 0),
385
+ legacyResiduals: Number(summary.legacyResiduals || 0),
386
+ fullCutoverEligible: summary.fullCutoverEligible === true,
387
+ };
388
+ }
389
+ function formatEvidenceBundle(value) {
390
+ const normalized = String(value || "").replace(/^TARGET:/, "").replace(/^\/+/, "");
391
+ return normalized ? `TARGET:${normalized}` : "";
392
+ }
393
+ function taskCloseoutInfo(target, taskPlanPath, closeout) {
394
+ const localWalkthrough = taskLocalWalkthrough(target.harness || resolveHarnessPaths(target), path.dirname(taskPlanPath));
395
+ if (localWalkthrough) {
396
+ const content = readFileSafe(path.join(target.projectRoot, localWalkthrough));
397
+ const status = /^Closeout Status\s*:\s*(closed|complete|completed|done|已关闭|已完成)\s*$/im.test(content)
398
+ ? "closed"
399
+ : "pending";
400
+ return { status, walkthroughPath: localWalkthrough };
401
+ }
402
+ if (!closeout.trim())
403
+ return { status: "missing", walkthroughPath: "" };
404
+ const docsRelative = `docs/${toPosix(path.relative(target.docsRoot, taskPlanPath))}`;
405
+ const projectRelative = toPosix(path.relative(target.projectRoot, taskPlanPath));
406
+ const line = closeout
407
+ .split(/\r?\n/)
408
+ .find((entry) => entry.includes(docsRelative) || entry.includes(projectRelative));
409
+ if (!line)
410
+ return { status: "missing", walkthroughPath: "" };
411
+ const walkthroughPath = extractWalkthroughPath(target, line);
412
+ const status = /\b(closed|complete|completed|done|skipped-with-reason|skipped|已关闭|已完成|跳过)\b/i.test(line) ? "closed" : "pending";
413
+ return { status, walkthroughPath };
414
+ }
415
+ function extractWalkthroughPath(target, closeoutLine) {
416
+ const matches = [...String(closeoutLine || "").matchAll(/`?((?:docs\/)?10-WALKTHROUGH\/[^`|\s]+\.md)`?/g)];
417
+ const match = matches.find((entry) => !entry[1].endsWith("Closeout-SSoT.md") && !entry[1].includes("/_"));
418
+ if (!match)
419
+ return "";
420
+ const projectRelative = match[1].startsWith("docs/") ? match[1] : `docs/${match[1]}`;
421
+ if (!fs.existsSync(path.join(target.projectRoot, projectRelative)))
422
+ return "";
423
+ return projectRelative;
424
+ }
425
+ function collectHandoffs(progressContent, taskId) {
426
+ if (!/Coordinator Handoff/i.test(progressContent) || !/pending-coordinator-pass/i.test(progressContent))
427
+ return [];
428
+ return [{ id: `H-${taskId}`, from: "worker", to: "coordinator", state: "pending", summary: "Coordinator handoff pending" }];
429
+ }
430
+ function collectEvidence(progressContent) {
431
+ const matches = [...progressContent.matchAll(/\b(command|diff|fixture|screenshot|review|report):((?:PUBLIC|PRIVATE|TARGET|EXTERNAL|URL):[^:\s|]+):([^\n|]+)/g)];
432
+ return matches.map((match, index) => ({
433
+ id: `E-${String(index + 1).padStart(3, "0")}`,
434
+ type: match[1],
435
+ path: match[2],
436
+ status: "present",
437
+ summary: match[3].trim(),
438
+ }));
439
+ }
@@ -0,0 +1,125 @@
1
+ // @ts-nocheck
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { normalizeTarget, nowTimestamp, readFileSafe, toPosix, datePrefix, } from "./core-shared.mjs";
5
+ import { collectTasks } from "./task-scanner.mjs";
6
+ import { beginGovernanceSync, commitGovernanceSync, releaseGovernanceSync, } from "./governance-sync.mjs";
7
+ export function supersedeTask(targetInput, oldRef, { by = "", reason = "" } = {}) {
8
+ if (!by)
9
+ throw new Error("task-supersede requires --by <new-task-id>");
10
+ const target = normalizeTarget(targetInput);
11
+ const oldTask = resolveTask(target, oldRef);
12
+ const newTask = resolveTask(target, by);
13
+ const governanceContext = beginGovernanceSync(target, { operation: `task-supersede ${oldTask.id}` });
14
+ try {
15
+ writeTombstone(target, oldTask, {
16
+ State: "superseded",
17
+ "Superseded By": newTask.id,
18
+ Reason: reason || "superseded",
19
+ Operator: "coordinator",
20
+ Timestamp: nowTimestamp(),
21
+ "Reopen Eligible": "yes",
22
+ "Archive Eligible": "no",
23
+ });
24
+ appendProgress(target, oldTask, `task-supersede: superseded by ${newTask.id}`, reason || "superseded");
25
+ appendSupersedes(target, newTask, oldTask.id);
26
+ const commit = commitGovernanceSync(contextFor(target, governanceContext), taskPaths(target, oldTask, newTask), {
27
+ message: `chore(harness): supersede task ${oldTask.id}`,
28
+ });
29
+ return { taskId: oldTask.id, supersededBy: newTask.id, reason: reason || "superseded", governance: { commit } };
30
+ }
31
+ finally {
32
+ releaseGovernanceSync(governanceContext);
33
+ }
34
+ }
35
+ export function softDeleteTask(targetInput, taskRef, { reason = "" } = {}) {
36
+ const target = normalizeTarget(targetInput);
37
+ const task = resolveTask(target, taskRef);
38
+ return writeDeletionState(target, task, "soft-deleted", reason || "soft-delete", "task-delete --soft");
39
+ }
40
+ export function archiveTask(targetInput, taskRef, { reason = "" } = {}) {
41
+ const target = normalizeTarget(targetInput);
42
+ const task = resolveTask(target, taskRef);
43
+ return writeDeletionState(target, task, "archived", reason || "archive", "task-archive");
44
+ }
45
+ export function reopenTask(targetInput, taskRef, { reason = "" } = {}) {
46
+ const target = normalizeTarget(targetInput);
47
+ const task = resolveTask(target, taskRef);
48
+ const governanceContext = beginGovernanceSync(target, { operation: `task-reopen ${task.id}` });
49
+ try {
50
+ const taskPlanPath = path.join(target.projectRoot, task.taskPlanPath.replace(/^TARGET:/, ""));
51
+ const content = readFileSafe(taskPlanPath);
52
+ const next = content.replace(/\n##\s*(?:Task Tombstone|任务墓碑)\s*$[\s\S]*?(?=^##\s+|(?![\s\S]))/im, "");
53
+ fs.writeFileSync(taskPlanPath, next.endsWith("\n") ? next : `${next}\n`);
54
+ appendProgress(target, task, "task-reopen", reason || "reopened");
55
+ const commit = commitGovernanceSync(governanceContext, taskPaths(target, task), {
56
+ message: `chore(harness): reopen task ${task.id}`,
57
+ });
58
+ return { taskId: task.id, deletionState: "active", reason: reason || "reopened", governance: { commit } };
59
+ }
60
+ finally {
61
+ releaseGovernanceSync(governanceContext);
62
+ }
63
+ }
64
+ function writeDeletionState(target, task, deletionState, reason, action) {
65
+ const governanceContext = beginGovernanceSync(target, { operation: `${action} ${task.id}` });
66
+ try {
67
+ writeTombstone(target, task, {
68
+ State: deletionState,
69
+ Reason: reason,
70
+ Operator: "coordinator",
71
+ Timestamp: nowTimestamp(),
72
+ "Reopen Eligible": "yes",
73
+ "Archive Eligible": deletionState === "archived" ? "yes" : "no",
74
+ });
75
+ appendProgress(target, task, action, reason);
76
+ const commit = commitGovernanceSync(governanceContext, taskPaths(target, task), {
77
+ message: `chore(harness): ${action.replace(/\s+/g, " ")} ${task.id}`,
78
+ });
79
+ return { taskId: task.id, deletionState, reason, governance: { commit } };
80
+ }
81
+ finally {
82
+ releaseGovernanceSync(governanceContext);
83
+ }
84
+ }
85
+ function taskPaths(target, ...tasks) {
86
+ return [...new Set(tasks.flatMap((task) => [task.taskPlanPath, task.progressPath]).filter(Boolean).map((item) => toPosix(item.replace(/^TARGET:/, ""))))];
87
+ }
88
+ function contextFor(_target, context) {
89
+ return context;
90
+ }
91
+ function resolveTask(target, ref) {
92
+ const normalized = String(ref || "").trim();
93
+ const matches = collectTasks(target).filter((task) => {
94
+ const bare = datePrefix.test(task.shortId) ? task.shortId.replace(datePrefix, "") : task.shortId;
95
+ return task.id === normalized || task.shortId === normalized || task.id.endsWith(`/${normalized}`) || bare === normalized;
96
+ });
97
+ if (matches.length === 1)
98
+ return matches[0];
99
+ if (matches.length > 1)
100
+ throw new Error(`Ambiguous task reference: ${ref}`);
101
+ throw new Error(`Task not found: ${ref}`);
102
+ }
103
+ function writeTombstone(target, task, fields) {
104
+ const taskPlanPath = path.join(target.projectRoot, task.taskPlanPath.replace(/^TARGET:/, ""));
105
+ const content = readFileSafe(taskPlanPath).replace(/\n##\s*(?:Task Tombstone|任务墓碑)\s*$[\s\S]*?(?=^##\s+|(?![\s\S]))/im, "");
106
+ const block = ["", "## Task Tombstone", "", "| Field | Value |", "| --- | --- |", ...Object.entries(fields).map(([key, value]) => `| ${key} | ${escapeCell(value)} |`), ""].join("\n");
107
+ fs.writeFileSync(taskPlanPath, `${content.trimEnd()}\n${block}`);
108
+ }
109
+ function appendSupersedes(target, task, oldId) {
110
+ const taskPlanPath = path.join(target.projectRoot, task.taskPlanPath.replace(/^TARGET:/, ""));
111
+ const content = readFileSafe(taskPlanPath);
112
+ if (/^Supersedes\s*[::]/im.test(content)) {
113
+ fs.writeFileSync(taskPlanPath, content.replace(/^Supersedes\s*[::]\s*(.*)$/im, (_m, current) => `Supersedes: ${[current, oldId].filter(Boolean).join(", ")}`));
114
+ return;
115
+ }
116
+ fs.writeFileSync(taskPlanPath, `${content.trimEnd()}\nSupersedes: ${oldId}\n`);
117
+ }
118
+ function appendProgress(target, task, action, reason) {
119
+ const progressPath = path.join(target.projectRoot, task.progressPath.replace(/^TARGET:/, ""));
120
+ const relative = toPosix(path.relative(target.projectRoot, progressPath));
121
+ fs.appendFileSync(progressPath, `\n\n## Tombstone Log\n\n- ${nowTimestamp()} ${action}: ${escapeCell(reason)} (${relative})\n`);
122
+ }
123
+ function escapeCell(value) {
124
+ return String(value || "").replace(/\r?\n/g, " ").replaceAll("|", "\\|").trim();
125
+ }
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ // @ts-nocheck
3
+ import { seedBundledPresets } from "./lib/harness-core.mjs";
4
+ if (process.env.CODING_AGENT_HARNESS_SKIP_POSTINSTALL === "1")
5
+ process.exit(0);
6
+ try {
7
+ const result = seedBundledPresets({ scope: "user" });
8
+ const changed = result.created + result.overwritten;
9
+ const summary = changed > 0 ? `${changed} bundled presets installed` : `${result.skipped} bundled presets already present`;
10
+ console.log(`coding-agent-harness postinstall: ${summary} at ${result.target}`);
11
+ }
12
+ catch (error) {
13
+ console.warn(`coding-agent-harness postinstall: preset seed skipped (${error.message})`);
14
+ }
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+ // @ts-nocheck
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { spawnSync } from "node:child_process";
6
+ import { fileURLToPath } from "node:url";
7
+ const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
8
+ const outDir = path.join(repoRoot, "tmp", `test-runner-emit-${process.pid}`);
9
+ const typescriptVersion = "5.9.3";
10
+ const options = parseArgs(process.argv.slice(2));
11
+ fs.rmSync(outDir, { recursive: true, force: true });
12
+ const emit = spawnSync("npm", ["exec", "--yes", "--package", `typescript@${typescriptVersion}`, "--", "tsc", "-p", "tsconfig.tests.json", "--outDir", outDir, "--noCheck"], {
13
+ cwd: repoRoot,
14
+ encoding: "utf8",
15
+ stdio: "inherit",
16
+ });
17
+ if (emit.status !== 0)
18
+ process.exit(emit.status || 1);
19
+ linkPackageResources();
20
+ const runner = path.join(outDir, options.test || "tests/run-all.mjs");
21
+ if (!fs.existsSync(runner)) {
22
+ console.error(`Built test runner not found: ${runner}`);
23
+ process.exit(1);
24
+ }
25
+ const result = spawnSync(process.execPath, [runner], {
26
+ cwd: repoRoot,
27
+ encoding: "utf8",
28
+ stdio: "inherit",
29
+ env: {
30
+ ...process.env,
31
+ HARNESS_TEST_REPO_ROOT: repoRoot,
32
+ HARNESS_TEST_RUNNER_MODE: "built",
33
+ HARNESS_TEST_RUNNER_OUT_DIR: outDir,
34
+ },
35
+ });
36
+ if (result.status !== 0)
37
+ process.exit(result.status || 1);
38
+ function parseArgs(argv) {
39
+ const parsed = { test: undefined };
40
+ for (let index = 0; index < argv.length; index += 1) {
41
+ const arg = argv[index];
42
+ if (arg === "--test") {
43
+ parsed.test = requireValue(argv, index, arg);
44
+ index += 1;
45
+ }
46
+ else {
47
+ throw new Error(`Unknown run-built-tests option: ${arg}`);
48
+ }
49
+ }
50
+ return parsed;
51
+ }
52
+ function requireValue(argv, index, option) {
53
+ const value = argv[index + 1];
54
+ if (!value)
55
+ throw new Error(`${option} requires a value`);
56
+ return value;
57
+ }
58
+ function linkPackageResources() {
59
+ for (const entry of [
60
+ "package.json",
61
+ "README.md",
62
+ "README.en-US.md",
63
+ "README.zh-CN.md",
64
+ "CONTRIBUTING.md",
65
+ "CHANGELOG.md",
66
+ "SKILL.md",
67
+ "LICENSE",
68
+ "LICENSE-EXCEPTION.md",
69
+ "presets",
70
+ "templates",
71
+ "templates-zh-CN",
72
+ "docs-release",
73
+ "examples",
74
+ "references",
75
+ "skills",
76
+ ]) {
77
+ const source = path.join(repoRoot, entry);
78
+ const target = path.join(outDir, entry);
79
+ if (!fs.existsSync(source) || fs.existsSync(target))
80
+ continue;
81
+ fs.mkdirSync(path.dirname(target), { recursive: true });
82
+ fs.symlinkSync(source, target);
83
+ }
84
+ }