coding-agent-harness 1.0.7 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/CONTRIBUTING.md +9 -5
  3. package/README.md +12 -2
  4. package/README.zh-CN.md +10 -2
  5. package/SKILL.md +14 -3
  6. package/dist/build-dist.mjs +32 -6
  7. package/dist/check-dist-observation.mjs +73 -28
  8. package/dist/check-harness.mjs +0 -1
  9. package/dist/check-import-graph.mjs +44 -27
  10. package/dist/check-lite-forbidden-surfaces.mjs +121 -0
  11. package/dist/check-no-ts-nocheck.mjs +88 -0
  12. package/dist/check-runtime-emit.mjs +10 -3
  13. package/dist/check-type-boundaries.mjs +67 -8
  14. package/dist/commands/dashboard-command.mjs +52 -14
  15. package/dist/commands/migration-command.mjs +18 -8
  16. package/dist/commands/module-command.mjs +142 -0
  17. package/dist/commands/preset-command.mjs +65 -4
  18. package/dist/commands/registry.mjs +483 -0
  19. package/dist/commands/task-command.mjs +111 -53
  20. package/dist/harness.mjs +6 -303
  21. package/dist/lib/capability-registry.mjs +229 -53
  22. package/dist/lib/check-module-parallel.mjs +1 -6
  23. package/dist/lib/check-profiles.mjs +39 -46
  24. package/dist/lib/check-task-contracts.mjs +6 -4
  25. package/dist/lib/command-registry.mjs +248 -0
  26. package/dist/lib/core-shared.mjs +78 -3
  27. package/dist/lib/dashboard-data.mjs +203 -22
  28. package/dist/lib/dashboard-workbench.mjs +245 -21
  29. package/dist/lib/dashboard-writer.mjs +4 -1
  30. package/dist/lib/git-status-summary.mjs +0 -1
  31. package/dist/lib/governance-index-generator.mjs +7 -5
  32. package/dist/lib/governance-sync.mjs +46 -121
  33. package/dist/lib/governance-table-boundary.mjs +1 -14
  34. package/dist/lib/harness-core.mjs +5 -1
  35. package/dist/lib/harness-paths.mjs +115 -1
  36. package/dist/lib/impact-classifier.mjs +420 -0
  37. package/dist/lib/lesson-maintenance.mjs +1 -2
  38. package/dist/lib/markdown-utils.mjs +50 -1
  39. package/dist/lib/migration-planner.mjs +31 -16
  40. package/dist/lib/migration-support.mjs +5 -4
  41. package/dist/lib/module-registry.mjs +296 -0
  42. package/dist/lib/preset-audit-contracts.mjs +24 -1
  43. package/dist/lib/preset-engine.mjs +68 -29
  44. package/dist/lib/preset-registry.mjs +374 -72
  45. package/dist/lib/preset-runner.mjs +560 -0
  46. package/dist/lib/review-confirm-git-gate.mjs +73 -19
  47. package/dist/lib/status-builder.mjs +23 -8
  48. package/dist/lib/structure-migration.mjs +6 -4
  49. package/dist/lib/subagent-authorization-audit.mjs +8 -2
  50. package/dist/lib/task-archive-eligibility.mjs +65 -0
  51. package/dist/lib/task-audit-metadata.mjs +25 -11
  52. package/dist/lib/task-audit-migration.mjs +21 -14
  53. package/dist/lib/task-discovery-contract.mjs +32 -0
  54. package/dist/lib/task-index.mjs +4 -2
  55. package/dist/lib/task-lesson-candidates.mjs +1 -2
  56. package/dist/lib/task-lesson-sedimentation.mjs +310 -9
  57. package/dist/lib/task-lifecycle/create-task-helpers.mjs +6 -3
  58. package/dist/lib/task-lifecycle/phase-sync.mjs +0 -1
  59. package/dist/lib/task-lifecycle/preset-interop.mjs +16 -0
  60. package/dist/lib/task-lifecycle/review-confirm.mjs +34 -2
  61. package/dist/lib/task-lifecycle/review-gates.mjs +12 -5
  62. package/dist/lib/task-lifecycle/review-submission.mjs +1 -2
  63. package/dist/lib/task-lifecycle/scaffold-provenance.mjs +0 -1
  64. package/dist/lib/task-lifecycle/template-files.mjs +2 -5
  65. package/dist/lib/task-lifecycle.mjs +117 -159
  66. package/dist/lib/task-metadata.mjs +10 -5
  67. package/dist/lib/task-preset-contract-drift.mjs +45 -0
  68. package/dist/lib/task-repository.mjs +192 -0
  69. package/dist/lib/task-review-model.mjs +38 -17
  70. package/dist/lib/task-scanner.mjs +75 -23
  71. package/dist/lib/task-template-materials.mjs +131 -0
  72. package/dist/lib/task-tombstone-commands.mjs +187 -18
  73. package/dist/lib/types/check-profiles.js +1 -0
  74. package/dist/lib/types/impact.js +1 -0
  75. package/dist/lib/types/preset.js +1 -0
  76. package/dist/lib/types/task-lifecycle.js +1 -0
  77. package/dist/lib/types/task-scanner.js +1 -0
  78. package/dist/postinstall.mjs +2 -2
  79. package/dist/run-built-tests.mjs +10 -3
  80. package/docs-release/README.md +2 -1
  81. package/docs-release/architecture/document-contract-kernel/README.md +150 -0
  82. package/docs-release/architecture/document-contract-kernel/products/full-skill-overlay.md +29 -0
  83. package/docs-release/architecture/document-contract-kernel/products/lite-forbidden-surfaces.txt +26 -0
  84. package/docs-release/architecture/document-contract-kernel/products/lite-skill-overlay.md +37 -0
  85. package/docs-release/architecture/overview.md +2 -2
  86. package/docs-release/architecture/overview.zh-CN.md +2 -2
  87. package/docs-release/architecture/system-explainer/01-system-overview.md +11 -7
  88. package/docs-release/architecture/system-explainer/02-module-dependency.md +4 -4
  89. package/docs-release/architecture/system-explainer/03-task-lifecycle.md +17 -12
  90. package/docs-release/architecture/system-explainer/05-data-flow.md +6 -6
  91. package/docs-release/architecture/system-explainer/06-preset-and-migration.md +2 -2
  92. package/docs-release/architecture/system-explainer/README.md +1 -1
  93. package/docs-release/architecture/system-explainer/en-US/01-system-overview.md +12 -8
  94. package/docs-release/architecture/system-explainer/en-US/02-module-dependency.md +5 -5
  95. package/docs-release/architecture/system-explainer/en-US/03-task-lifecycle.md +19 -11
  96. package/docs-release/architecture/system-explainer/en-US/05-data-flow.md +5 -5
  97. package/docs-release/architecture/system-explainer/en-US/06-preset-and-migration.md +2 -2
  98. package/docs-release/architecture/system-explainer/en-US/README.md +1 -1
  99. package/docs-release/guides/agent-installation.en-US.md +4 -6
  100. package/docs-release/guides/agent-installation.md +11 -8
  101. package/docs-release/guides/contributing.md +10 -3
  102. package/docs-release/guides/contributing.zh-CN.md +10 -3
  103. package/docs-release/guides/legacy-migration-agent-prompt.md +1 -1
  104. package/docs-release/guides/legacy-migration-agent-prompt.zh-CN.md +1 -1
  105. package/docs-release/guides/migration-playbook.en-US.md +9 -6
  106. package/docs-release/guides/migration-playbook.md +9 -6
  107. package/docs-release/guides/preset-development.md +68 -2
  108. package/docs-release/guides/task-state-machine.en-US.md +8 -8
  109. package/docs-release/guides/task-state-machine.md +7 -7
  110. package/docs-release/guides/typescript-runtime-migration-closeout.md +17 -13
  111. package/package.json +19 -11
  112. package/postinstall.mjs +37 -0
  113. package/presets/legacy-migration/preset.yaml +5 -5
  114. package/presets/legacy-migration/templates/execution_strategy.append.md +1 -1
  115. package/presets/lesson-sedimentation/preset.yaml +3 -3
  116. package/presets/module/preset.yaml +2 -2
  117. package/presets/module/templates/execution_strategy.append.md +1 -1
  118. package/presets/module/templates/task_plan.append.md +3 -3
  119. package/presets/release-closeout/checks/check-release-package.mjs +29 -0
  120. package/presets/release-closeout/preset.yaml +100 -0
  121. package/presets/release-closeout/scripts/generate-release-package.mjs +572 -0
  122. package/presets/release-closeout/templates/execution_strategy.append.md +7 -0
  123. package/presets/release-closeout/templates/findings.seed.md +5 -0
  124. package/presets/release-closeout/templates/review.seed.md +3 -0
  125. package/presets/release-closeout/templates/task_plan.append.md +24 -0
  126. package/presets/standard-task/preset.yaml +2 -2
  127. package/references/agents-md-pattern.md +23 -17
  128. package/references/lessons-governance.md +2 -2
  129. package/references/module-parallel-standard.md +3 -6
  130. package/references/pull-request-standard.md +2 -2
  131. package/references/ssot-governance.md +2 -2
  132. package/references/taskr-gap-analysis.md +3 -3
  133. package/run-dist.mjs +34 -0
  134. package/skills/preset-creator/SKILL.md +40 -8
  135. package/skills/preset-creator/references/complex-task-skeleton/brief.md +32 -8
  136. package/skills/preset-creator/references/preset-package-skeleton.md +15 -5
  137. package/skills/preset-creator/references/structure-aware-paths.md +112 -0
  138. package/templates/AGENTS.md.template +28 -26
  139. package/templates/architecture/README.md +2 -2
  140. package/templates/architecture/service-catalog.md +2 -2
  141. package/templates/architecture/services/service-template.md +1 -1
  142. package/templates/dashboard/assets/app-src/00-state.js +5 -1
  143. package/templates/dashboard/assets/app-src/10-router.js +7 -0
  144. package/templates/dashboard/assets/app-src/20-overview.js +8 -8
  145. package/templates/dashboard/assets/app-src/30-tasks.js +132 -40
  146. package/templates/dashboard/assets/app-src/32-task-swimlane.js +314 -0
  147. package/templates/dashboard/assets/app-src/35-task-detail.js +35 -5
  148. package/templates/dashboard/assets/app-src/40-modules.js +257 -41
  149. package/templates/dashboard/assets/app-src/45-review.js +127 -1
  150. package/templates/dashboard/assets/app-src/90-bindings.js +185 -2
  151. package/templates/dashboard/assets/app.css +928 -53
  152. package/templates/dashboard/assets/app.css.manifest.json +2 -0
  153. package/templates/dashboard/assets/app.js +1071 -98
  154. package/templates/dashboard/assets/app.manifest.json +1 -0
  155. package/templates/dashboard/assets/css-src/00-foundation.css +12 -6
  156. package/templates/dashboard/assets/css-src/10-panels-flow.css +2 -2
  157. package/templates/dashboard/assets/css-src/30-task-index.css +21 -13
  158. package/templates/dashboard/assets/css-src/31-archive.css +94 -0
  159. package/templates/dashboard/assets/css-src/32-task-swimlane.css +487 -0
  160. package/templates/dashboard/assets/css-src/35-review-workspace.css +78 -0
  161. package/templates/dashboard/assets/css-src/40-detail-modules-migration.css +191 -14
  162. package/templates/dashboard/assets/css-src/50-responsive-overrides.css +23 -0
  163. package/templates/dashboard/assets/i18n.js +166 -2
  164. package/templates/development/README.md +9 -9
  165. package/templates/development/cross-repo-debugging.md +3 -3
  166. package/templates/development/external-context/service-template.md +1 -1
  167. package/templates/development/external-source-packs/README.md +2 -2
  168. package/templates/integrations/README.md +4 -4
  169. package/templates/integrations/api-contract.md +1 -1
  170. package/templates/integrations/event-contract.md +1 -1
  171. package/templates/integrations/third-party/vendor-template.md +1 -1
  172. package/templates/integrations/webhook-contract.md +1 -1
  173. package/templates/ledger/Harness-Ledger.md +1 -1
  174. package/templates/modules/module_brief.md +50 -0
  175. package/templates/modules/module_plan.md +49 -0
  176. package/templates/modules/registry_view.md +9 -0
  177. package/templates/modules/session_prompt_pack.md +55 -0
  178. package/templates/planning/brief.md +32 -8
  179. package/templates/planning/module_brief.md +28 -3
  180. package/templates/planning/module_plan.md +26 -11
  181. package/templates/planning/module_session_prompt.md +11 -2
  182. package/templates/planning/optional/slices/_slice-template/brief.md +28 -0
  183. package/templates/planning/review.md +1 -1
  184. package/templates/planning/visual_map.md +1 -1
  185. package/templates/reference/docs-library-standard.md +7 -7
  186. package/templates/reference/execution-workflow-standard.md +13 -0
  187. package/templates/reference/external-source-intake-standard.md +10 -10
  188. package/templates/reference/pull-request-standard.md +2 -2
  189. package/templates/reference/repo-governance-standard.md +1 -1
  190. package/templates/reference/review-routing-standard.md +4 -0
  191. package/templates/ssot/Module-Registry.md +4 -38
  192. package/templates/walkthrough/walkthrough-template.md +1 -1
  193. package/templates-zh-CN/AGENTS.md.template +27 -25
  194. package/templates-zh-CN/CLAUDE.md.template +1 -1
  195. package/templates-zh-CN/architecture/README.md +2 -2
  196. package/templates-zh-CN/architecture/service-catalog.md +2 -2
  197. package/templates-zh-CN/architecture/services/service-template.md +1 -1
  198. package/templates-zh-CN/development/README.md +9 -9
  199. package/templates-zh-CN/development/cross-repo-debugging.md +3 -3
  200. package/templates-zh-CN/development/external-context/service-template.md +1 -1
  201. package/templates-zh-CN/development/external-source-packs/README.md +2 -2
  202. package/templates-zh-CN/integrations/README.md +4 -4
  203. package/templates-zh-CN/integrations/api-contract.md +1 -1
  204. package/templates-zh-CN/integrations/event-contract.md +1 -1
  205. package/templates-zh-CN/integrations/third-party/vendor-template.md +1 -1
  206. package/templates-zh-CN/integrations/webhook-contract.md +1 -1
  207. package/templates-zh-CN/ledger/Harness-Ledger.md +1 -1
  208. package/templates-zh-CN/lessons/lesson-arch-process-change.md +1 -1
  209. package/templates-zh-CN/lessons/lesson-new-doc.md +3 -3
  210. package/templates-zh-CN/lessons/lesson-ref-change.md +4 -4
  211. package/templates-zh-CN/modules/module_brief.md +47 -0
  212. package/templates-zh-CN/modules/module_plan.md +48 -0
  213. package/templates-zh-CN/modules/registry_view.md +9 -0
  214. package/templates-zh-CN/modules/session_prompt_pack.md +50 -0
  215. package/templates-zh-CN/planning/INDEX.md +1 -0
  216. package/templates-zh-CN/planning/brief.md +26 -7
  217. package/templates-zh-CN/planning/module_brief.md +24 -2
  218. package/templates-zh-CN/planning/module_plan.md +35 -29
  219. package/templates-zh-CN/planning/module_session_prompt.md +15 -11
  220. package/templates-zh-CN/planning/optional/slices/_slice-template/brief.md +28 -11
  221. package/templates-zh-CN/planning/review.md +1 -1
  222. package/templates-zh-CN/reference/adversarial-review-standard.md +1 -1
  223. package/templates-zh-CN/reference/delivery-operating-model-standard.md +3 -3
  224. package/templates-zh-CN/reference/docs-library-standard.md +27 -27
  225. package/templates-zh-CN/reference/execution-workflow-standard.md +12 -2
  226. package/templates-zh-CN/reference/external-source-intake-standard.md +10 -10
  227. package/templates-zh-CN/reference/harness-ledger-standard.md +3 -3
  228. package/templates-zh-CN/reference/pull-request-standard.md +1 -1
  229. package/templates-zh-CN/reference/regression-ssot-governance.md +2 -2
  230. package/templates-zh-CN/reference/repo-governance-standard.md +1 -1
  231. package/templates-zh-CN/reference/review-routing-standard.md +3 -0
  232. package/templates-zh-CN/reference/walkthrough-standard.md +2 -2
  233. package/templates-zh-CN/reference/worktree-standard.md +1 -1
  234. package/templates-zh-CN/regression/Cadence-Ledger.md +2 -2
  235. package/templates-zh-CN/ssot/Delivery-SSoT.md +2 -2
  236. package/templates-zh-CN/ssot/Module-Registry.md +5 -44
  237. package/templates-zh-CN/ssot/Regression-SSoT.md +2 -2
  238. package/templates-zh-CN/walkthrough/walkthrough-template.md +4 -4
@@ -0,0 +1,131 @@
1
+ import path from "node:path";
2
+ import { lessonCandidatesFile, readFileSafe, toPosix, visualMapFile, } from "./core-shared.mjs";
3
+ export function collectUneditedTemplateMaterialIssues(target, taskDir, materials) {
4
+ const issues = [];
5
+ const files = [
6
+ { label: "brief.md", sourcePath: toPosix(path.relative(target.projectRoot, path.join(taskDir, "brief.md"))), content: materials.briefContent },
7
+ { label: "task_plan.md", sourcePath: toPosix(path.relative(target.projectRoot, path.join(taskDir, "task_plan.md"))), content: materials.taskPlanContent },
8
+ { label: "execution_strategy.md", sourcePath: toPosix(path.relative(target.projectRoot, path.join(taskDir, "execution_strategy.md"))), content: materials.executionStrategyContent },
9
+ { label: visualMapFile, sourcePath: toPosix(path.relative(target.projectRoot, path.join(taskDir, visualMapFile))), content: materials.visualMapContent },
10
+ { label: "progress.md", sourcePath: toPosix(path.relative(target.projectRoot, path.join(taskDir, "progress.md"))), content: materials.progressContent },
11
+ { label: "findings.md", sourcePath: toPosix(path.relative(target.projectRoot, path.join(taskDir, "findings.md"))), content: materials.findingsContent },
12
+ { label: "review.md", sourcePath: toPosix(path.relative(target.projectRoot, path.join(taskDir, "review.md"))), content: materials.reviewContent },
13
+ { label: lessonCandidatesFile, sourcePath: toPosix(path.relative(target.projectRoot, path.join(taskDir, lessonCandidatesFile))), content: materials.lessonCandidatesContent },
14
+ ];
15
+ if (materials.walkthroughPath) {
16
+ files.push({
17
+ label: path.basename(materials.walkthroughPath),
18
+ sourcePath: materials.walkthroughPath,
19
+ content: readFileSafe(path.join(target.projectRoot, materials.walkthroughPath)),
20
+ });
21
+ }
22
+ for (const file of files) {
23
+ const markers = uneditedCoreTemplateSlots(file.label, file.content);
24
+ if (markers.length === 0)
25
+ continue;
26
+ issues.push({
27
+ code: "unedited-template-material",
28
+ severity: "P2",
29
+ queue: "missing-materials",
30
+ sourcePath: `TARGET:${file.sourcePath}`,
31
+ sourceLine: 0,
32
+ owner: "agent",
33
+ message: `[unedited-template-material] Reviewable task material still contains default core content in ${file.label}: ${markers.slice(0, 3).join(", ")}.`,
34
+ allowedWritePaths: [`${toPosix(path.relative(target.projectRoot, taskDir))}/**`],
35
+ forbiddenActions: ["human-confirm", "edit-unrelated-task", "fabricate-evidence"],
36
+ validationCommands: ["node dist/harness.mjs check --profile target-project <target>"],
37
+ confidence: "high",
38
+ repairable: true,
39
+ enforceFailure: materials.humanReviewConfirmed,
40
+ });
41
+ }
42
+ return issues;
43
+ }
44
+ function uneditedCoreTemplateSlots(label, content) {
45
+ const text = String(content || "");
46
+ const markers = [];
47
+ if (label === "brief.md") {
48
+ if (sectionHasDefault(text, ["Outcome Statement", "一句话结果"], [
49
+ "One sentence stating the concrete result this task must produce.",
50
+ "用一句话说明这个任务完成后会产生什么具体结果。",
51
+ "说明这个任务完成后,用户或项目能直接看到的结果。",
52
+ ]))
53
+ markers.push("brief-outcome");
54
+ }
55
+ else if (label === "task_plan.md") {
56
+ if (sectionHasDefault(text, ["Goal", "目标"], [
57
+ "[State the outcome this task must deliver in one sentence.]",
58
+ "[用一句话说明本任务完成后应达到的状态。]",
59
+ ]))
60
+ markers.push("task-plan-goal");
61
+ }
62
+ else if (label === "execution_strategy.md") {
63
+ if (sectionHasDefault(text, ["Strategy Summary"], ["[Describe the execution approach, including why this operating model fits the risk and scope.]"]) ||
64
+ /\|\s*L0\s*\|\s*\[静态检查\s*\/\s*小范围自检\]\s*\|\s*`?progress\.md`?\s*\|\s*\[通过标准\]\s*\|/i.test(text)) {
65
+ markers.push("execution-strategy-core-plan");
66
+ }
67
+ }
68
+ else if (label === visualMapFile) {
69
+ if (hasDefaultExecutionPhase(text))
70
+ markers.push("visual-map-execution-phase");
71
+ }
72
+ else if (label === "progress.md") {
73
+ if (/^\|\s*YYYY-MM-DD HH:MM\s*\|\s*coordinator\s*\|\s*\[action taken\]\s*\|/im.test(text) ||
74
+ /^###\s*\[YYYY-MM-DD HH:MM\]\s*-\s*\[阶段名称\]\s*$/im.test(text) ||
75
+ /^-\s*做了什么:\[具体操作\]\s*$/im.test(text)) {
76
+ markers.push("progress-log-entry");
77
+ }
78
+ }
79
+ else if (label === "review.md") {
80
+ if (/\|\s*Evidence Summary\s*\|\s*\[(?:tests, diff, runtime, and review packet evidence|测试、diff、运行和审查材料证据)\]\s*\|/i.test(text) ||
81
+ /\|\s*E-001\s*\|[^|\n]*\|[^|\n]*\|\s*\[(?:what was checked and what it showed|检查了什么,结论是什么)\]\s*\|/i.test(text)) {
82
+ markers.push("review-evidence");
83
+ }
84
+ }
85
+ else if (label === lessonCandidatesFile) {
86
+ if (/\|\s*Task-level status\s*\|\s*no-candidate-accepted\s*\|/i.test(text) &&
87
+ /^(?:Not decided yet\. Fill this only when review accepts that the task produced no reusable lesson candidate\.|尚未判定。只有人工审查接受本任务没有可复用候选时,才填写这里。)\s*$/im.test(text)) {
88
+ markers.push("lesson-no-candidate-reason");
89
+ }
90
+ }
91
+ else if (label === "walkthrough.md") {
92
+ if (sectionHasDefault(text, ["Summary", "摘要"], ["Pending closeout.", "待收口。"]))
93
+ markers.push("walkthrough-summary");
94
+ }
95
+ return markers;
96
+ }
97
+ function sectionHasDefault(text, headings, defaults) {
98
+ const body = sectionBody(text, headings);
99
+ if (!body)
100
+ return false;
101
+ return defaults.some((value) => body === value || body.split(/\r?\n/).map((line) => line.trim()).includes(value));
102
+ }
103
+ function sectionBody(text, headings) {
104
+ const lines = text.split(/\r?\n/);
105
+ for (let index = 0; index < lines.length; index += 1) {
106
+ const match = lines[index].match(/^(#{2,6})\s+(.+?)\s*$/);
107
+ if (!match)
108
+ continue;
109
+ if (!headings.includes(match[2].trim()))
110
+ continue;
111
+ const level = match[1].length;
112
+ const body = [];
113
+ for (let bodyIndex = index + 1; bodyIndex < lines.length; bodyIndex += 1) {
114
+ const nextHeading = lines[bodyIndex].match(/^(#{2,6})\s+/);
115
+ if (nextHeading && nextHeading[1].length <= level)
116
+ break;
117
+ if (lines[bodyIndex].trim())
118
+ body.push(lines[bodyIndex].trim());
119
+ }
120
+ return body.join("\n").trim();
121
+ }
122
+ return "";
123
+ }
124
+ function hasDefaultExecutionPhase(text) {
125
+ const rows = text.split(/\r?\n/).filter((line) => /^\|\s*EXEC-01\s*\|/.test(line));
126
+ return rows.some((row) => {
127
+ const cells = row.split("|").map((cell) => cell.trim()).filter(Boolean);
128
+ return cells.some((cell) => cell === "Scoped implementation, document update, and verification evidence" || cell === "有边界的实现、文档切片和验证证据") &&
129
+ cells.some((cell) => cell === "diff, commands, worker handoff, or artifact path" || cell === "diff、commands、worker handoff 或 artifact path");
130
+ });
131
+ }
@@ -1,22 +1,27 @@
1
- // @ts-nocheck
2
1
  import fs from "node:fs";
3
2
  import path from "node:path";
4
3
  import { normalizeTarget, nowTimestamp, readFileSafe, toPosix, datePrefix, } from "./core-shared.mjs";
4
+ import { removeHeadingSectionOutsideFences } from "./markdown-utils.mjs";
5
5
  import { collectTasks } from "./task-scanner.mjs";
6
+ import { resolveTaskDirectory } from "./task-lifecycle.mjs";
7
+ import { taskIdFromDirectory } from "./harness-paths.mjs";
8
+ import { assessArchiveEligibility, normalizeArchiveActor } from "./task-archive-eligibility.mjs";
9
+ import { normalizeReviewBoolean } from "./task-review-model.mjs";
6
10
  import { beginGovernanceSync, commitGovernanceSync, releaseGovernanceSync, } from "./governance-sync.mjs";
7
- export function supersedeTask(targetInput, oldRef, { by = "", reason = "" } = {}) {
11
+ export function supersedeTask(targetInput, oldRef, { by = "", reason = "", deletedBy = "", confirm = "", allowOpenFindings = false } = {}) {
8
12
  if (!by)
9
13
  throw new Error("task-supersede requires --by <new-task-id>");
10
14
  const target = normalizeTarget(targetInput);
11
15
  const oldTask = resolveTask(target, oldRef);
12
16
  const newTask = resolveTask(target, by);
17
+ assertSoftDeleteEligible(oldTask, { reason, deletedBy, confirm, allowOpenFindings, action: "task-supersede" });
13
18
  const governanceContext = beginGovernanceSync(target, { operation: `task-supersede ${oldTask.id}` });
14
19
  try {
15
20
  writeTombstone(target, oldTask, {
16
21
  State: "superseded",
17
22
  "Superseded By": newTask.id,
18
23
  Reason: reason || "superseded",
19
- Operator: "coordinator",
24
+ Operator: normalizeTombstoneActor(deletedBy) || "coordinator",
20
25
  Timestamp: nowTimestamp(),
21
26
  "Reopen Eligible": "yes",
22
27
  "Archive Eligible": "no",
@@ -32,15 +37,43 @@ export function supersedeTask(targetInput, oldRef, { by = "", reason = "" } = {}
32
37
  releaseGovernanceSync(governanceContext);
33
38
  }
34
39
  }
35
- export function softDeleteTask(targetInput, taskRef, { reason = "" } = {}) {
40
+ export function softDeleteTask(targetInput, taskRef, options = {}) {
41
+ return deleteTask(targetInput, taskRef, { ...options, hard: false });
42
+ }
43
+ export function deleteTask(targetInput, taskRef, { hard = false, reason = "", deletedBy = "", confirm = "", allowOpenFindings = false } = {}) {
36
44
  const target = normalizeTarget(targetInput);
37
45
  const task = resolveTask(target, taskRef);
38
- return writeDeletionState(target, task, "soft-deleted", reason || "soft-delete", "task-delete --soft");
46
+ if (!hard) {
47
+ assertSoftDeleteEligible(task, { reason, deletedBy, confirm, allowOpenFindings, action: "task-delete" });
48
+ return writeDeletionState(target, task, "soft-deleted", reason || "soft-delete", "task-delete --soft", {
49
+ Operator: normalizeTombstoneActor(deletedBy) || "coordinator",
50
+ });
51
+ }
52
+ assertHardDeleteEligible(target, task, { reason, deletedBy, confirm });
53
+ const taskDir = path.join(target.projectRoot, task.path.replace(/^TARGET:/, ""));
54
+ const allowedPaths = collectTaskDirectoryFiles(taskDir).map((file) => toPosix(path.relative(target.projectRoot, file)));
55
+ const governanceContext = beginGovernanceSync(target, { operation: `task-delete --hard ${task.id}` });
56
+ try {
57
+ fs.rmSync(taskDir, { recursive: true, force: true });
58
+ const commit = commitGovernanceSync(governanceContext, allowedPaths, {
59
+ message: `chore(harness): hard delete task ${task.id}`,
60
+ });
61
+ return { taskId: task.id, deletionState: "hard-deleted", reason, governance: { commit } };
62
+ }
63
+ finally {
64
+ releaseGovernanceSync(governanceContext);
65
+ }
39
66
  }
40
- export function archiveTask(targetInput, taskRef, { reason = "" } = {}) {
67
+ export function archiveTask(targetInput, taskRef, { reason = "", archivedBy = "", archiveFields = {} } = {}) {
41
68
  const target = normalizeTarget(targetInput);
42
69
  const task = resolveTask(target, taskRef);
43
- return writeDeletionState(target, task, "archived", reason || "archive", "task-archive");
70
+ const archiveAudit = assertArchiveEligible(task, { archivedBy });
71
+ const normalizedArchiveFields = normalizeArchiveFields(archiveFields);
72
+ assertNoReservedArchiveFields(normalizedArchiveFields);
73
+ return writeDeletionState(target, task, "archived", reason || "archive", "task-archive", {
74
+ ...normalizedArchiveFields,
75
+ ...archiveAudit,
76
+ });
44
77
  }
45
78
  export function reopenTask(targetInput, taskRef, { reason = "" } = {}) {
46
79
  const target = normalizeTarget(targetInput);
@@ -49,7 +82,7 @@ export function reopenTask(targetInput, taskRef, { reason = "" } = {}) {
49
82
  try {
50
83
  const taskPlanPath = path.join(target.projectRoot, task.taskPlanPath.replace(/^TARGET:/, ""));
51
84
  const content = readFileSafe(taskPlanPath);
52
- const next = content.replace(/\n##\s*(?:Task Tombstone|任务墓碑)\s*$[\s\S]*?(?=^##\s+|(?![\s\S]))/im, "");
85
+ const next = removeHeadingSectionOutsideFences(content, /^##\s*(?:Task Tombstone|任务墓碑)\s*$/i);
53
86
  fs.writeFileSync(taskPlanPath, next.endsWith("\n") ? next : `${next}\n`);
54
87
  appendProgress(target, task, "task-reopen", reason || "reopened");
55
88
  const commit = commitGovernanceSync(governanceContext, taskPaths(target, task), {
@@ -61,7 +94,8 @@ export function reopenTask(targetInput, taskRef, { reason = "" } = {}) {
61
94
  releaseGovernanceSync(governanceContext);
62
95
  }
63
96
  }
64
- function writeDeletionState(target, task, deletionState, reason, action) {
97
+ function writeDeletionState(target, task, deletionState, reason, action, archiveFields = {}) {
98
+ const normalizedArchiveFields = normalizeArchiveFields(archiveFields);
65
99
  const governanceContext = beginGovernanceSync(target, { operation: `${action} ${task.id}` });
66
100
  try {
67
101
  writeTombstone(target, task, {
@@ -71,6 +105,7 @@ function writeDeletionState(target, task, deletionState, reason, action) {
71
105
  Timestamp: nowTimestamp(),
72
106
  "Reopen Eligible": "yes",
73
107
  "Archive Eligible": deletionState === "archived" ? "yes" : "no",
108
+ ...normalizedArchiveFields,
74
109
  });
75
110
  appendProgress(target, task, action, reason);
76
111
  const commit = commitGovernanceSync(governanceContext, taskPaths(target, task), {
@@ -90,22 +125,121 @@ function contextFor(_target, context) {
90
125
  }
91
126
  function resolveTask(target, ref) {
92
127
  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}`);
128
+ const resolvedTarget = target;
129
+ const taskDir = resolveTaskDirectory(resolvedTarget, normalized);
130
+ const taskId = taskIdFromDirectory(resolvedTarget.harness, taskDir);
131
+ const task = collectTasks(target).find((candidate) => candidate.id === taskId);
132
+ if (task)
133
+ return task;
101
134
  throw new Error(`Task not found: ${ref}`);
102
135
  }
136
+ function assertArchiveEligible(task, { archivedBy = "" } = {}) {
137
+ const result = assessArchiveEligibility(task, { archivedBy, now: nowTimestamp() });
138
+ if (!result.eligible)
139
+ throw new Error(result.reason);
140
+ return result.auditFields;
141
+ }
103
142
  function writeTombstone(target, task, fields) {
104
143
  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, "");
144
+ const content = removeHeadingSectionOutsideFences(readFileSafe(taskPlanPath), /^##\s*(?:Task Tombstone|任务墓碑)\s*$/i);
106
145
  const block = ["", "## Task Tombstone", "", "| Field | Value |", "| --- | --- |", ...Object.entries(fields).map(([key, value]) => `| ${key} | ${escapeCell(value)} |`), ""].join("\n");
107
146
  fs.writeFileSync(taskPlanPath, `${content.trimEnd()}\n${block}`);
108
147
  }
148
+ function assertSoftDeleteEligible(task, { reason = "", deletedBy = "", confirm = "", allowOpenFindings = false, action = "task-delete" } = {}) {
149
+ const risky = isRiskyMutationTask(task);
150
+ const hasOpenFindings = !isDraftState(task.state) && hasOpenBlockingFindings(task);
151
+ if (!risky && !hasOpenFindings)
152
+ return;
153
+ const missing = [];
154
+ if (!String(reason || "").trim())
155
+ missing.push("--reason");
156
+ if (!normalizeTombstoneActor(deletedBy))
157
+ missing.push("--deleted-by");
158
+ if (String(confirm || "").trim() !== task.id)
159
+ missing.push(`--confirm ${task.id}`);
160
+ if (hasOpenFindings && !allowOpenFindings)
161
+ missing.push("--allow-open-findings");
162
+ if (missing.length) {
163
+ throw new Error(`${action} would hide a risky task; required: ${missing.join(", ")}`);
164
+ }
165
+ }
166
+ export function assertHardDeleteEligible(target, task, { reason = "", deletedBy = "", confirm = "" } = {}) {
167
+ const missing = [];
168
+ if (!String(reason || "").trim())
169
+ missing.push("--reason");
170
+ if (!normalizeTombstoneActor(deletedBy))
171
+ missing.push("--deleted-by");
172
+ if (String(confirm || "").trim() !== task.id)
173
+ missing.push(`--confirm ${task.id}`);
174
+ if (missing.length)
175
+ throw new Error(`task-delete --hard requires accountable safe draft confirmation: ${missing.join(", ")}`);
176
+ const blockers = [];
177
+ if (!isDraftState(task.state))
178
+ blockers.push(`state:${task.state}`);
179
+ if (task.deletionState !== "active")
180
+ blockers.push(`deletionState:${task.deletionState}`);
181
+ if (isRiskyMutationTask(task))
182
+ blockers.push("task has lifecycle, review, evidence, or closeout history");
183
+ if (!isDraftState(task.state) && hasOpenBlockingFindings(task))
184
+ blockers.push("task has open blocking findings");
185
+ const taskDir = path.join(target.projectRoot, task.path.replace(/^TARGET:/, ""));
186
+ const disallowedFiles = collectTaskDirectoryFiles(taskDir).filter((file) => !isSafeDraftFile(taskDir, file));
187
+ if (disallowedFiles.length)
188
+ blockers.push(`non-scaffold files: ${disallowedFiles.map((file) => toPosix(path.relative(taskDir, file))).join(", ")}`);
189
+ if (blockers.length)
190
+ throw new Error(`task-delete --hard only supports safe draft tasks; ${blockers.join("; ")}`);
191
+ }
192
+ function isRiskyMutationTask(task) {
193
+ return !isDraftState(task.state) ||
194
+ Boolean(task.reviewSubmitted) ||
195
+ task.reviewStatus === "confirmed" ||
196
+ Boolean(task.reviewConfirmation?.confirmed) ||
197
+ task.materialsReady === true && !isDraftState(task.state) ||
198
+ (task.evidence || []).length > 0 ||
199
+ (task.taskQueues || []).some((queue) => ["blocked", "review", "confirmed", "finalized", "lessons"].includes(queue));
200
+ }
201
+ function isDraftState(state) {
202
+ const normalized = String(state || "").trim().toLowerCase().replaceAll("-", "_").replace(/\s+/g, "_");
203
+ return ["planned", "not_started"].includes(normalized);
204
+ }
205
+ function hasOpenBlockingFindings(task) {
206
+ return (task.risks || []).some((risk) => normalizeReviewBoolean(risk.open) !== "no" && (normalizeReviewBoolean(risk.blocksRelease) === "yes" || ["P0", "P1", "P2"].includes(String(risk.severity))));
207
+ }
208
+ function normalizeTombstoneActor(value) {
209
+ return normalizeArchiveActor(value);
210
+ }
211
+ function collectTaskDirectoryFiles(taskDir) {
212
+ if (!fs.existsSync(taskDir))
213
+ return [];
214
+ const files = [];
215
+ const visit = (dir) => {
216
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
217
+ const full = path.join(dir, entry.name);
218
+ if (entry.isDirectory())
219
+ visit(full);
220
+ else if (entry.isFile())
221
+ files.push(full);
222
+ }
223
+ };
224
+ visit(taskDir);
225
+ return files.sort();
226
+ }
227
+ function isSafeDraftFile(taskDir, file) {
228
+ const relative = toPosix(path.relative(taskDir, file));
229
+ return [
230
+ "INDEX.md",
231
+ "brief.md",
232
+ "execution_strategy.md",
233
+ "findings.md",
234
+ "lesson_candidates.md",
235
+ "progress.md",
236
+ "review.md",
237
+ "task_plan.md",
238
+ "visual_map.md",
239
+ "walkthrough.md",
240
+ "long-running-task-contract.md",
241
+ ].includes(relative) || /^artifacts\/INDEX\.md$/.test(relative) || /^references\/INDEX\.md$/.test(relative);
242
+ }
109
243
  function appendSupersedes(target, task, oldId) {
110
244
  const taskPlanPath = path.join(target.projectRoot, task.taskPlanPath.replace(/^TARGET:/, ""));
111
245
  const content = readFileSafe(taskPlanPath);
@@ -123,3 +257,38 @@ function appendProgress(target, task, action, reason) {
123
257
  function escapeCell(value) {
124
258
  return String(value || "").replace(/\r?\n/g, " ").replaceAll("|", "\\|").trim();
125
259
  }
260
+ function normalizeArchiveFields(fields) {
261
+ const entries = Object.entries(fields || {});
262
+ const normalized = {};
263
+ const seen = new Set();
264
+ for (const [rawKey, rawValue] of entries) {
265
+ const key = String(rawKey || "").trim();
266
+ if (!key || /[\r\n|]/.test(key))
267
+ throw new Error(`Invalid archive field key: ${key || "<empty>"}`);
268
+ const normalizedKey = key.toLowerCase();
269
+ if (seen.has(normalizedKey))
270
+ throw new Error(`Duplicate archive field key: ${key}`);
271
+ seen.add(normalizedKey);
272
+ normalized[key] = String(rawValue || "").replace(/\r?\n/g, " ").trim();
273
+ }
274
+ return normalized;
275
+ }
276
+ function assertNoReservedArchiveFields(fields) {
277
+ const reserved = new Set([
278
+ "state",
279
+ "reason",
280
+ "operator",
281
+ "timestamp",
282
+ "reopen eligible",
283
+ "archive eligible",
284
+ "archived by",
285
+ "archived at",
286
+ "review confirmed by",
287
+ "review confirmed at",
288
+ "review confirmation id",
289
+ "review commit sha",
290
+ ]);
291
+ const blocked = Object.keys(fields).filter((key) => reserved.has(key.toLowerCase()));
292
+ if (blocked.length)
293
+ throw new Error(`Reserved archive field cannot be overridden: ${blocked.join(", ")}`);
294
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- // @ts-nocheck
3
2
  import { seedBundledPresets } from "./lib/harness-core.mjs";
4
3
  if (process.env.CODING_AGENT_HARNESS_SKIP_POSTINSTALL === "1")
5
4
  process.exit(0);
@@ -10,5 +9,6 @@ try {
10
9
  console.log(`coding-agent-harness postinstall: ${summary} at ${result.target}`);
11
10
  }
12
11
  catch (error) {
13
- console.warn(`coding-agent-harness postinstall: preset seed skipped (${error.message})`);
12
+ const message = error instanceof Error ? error.message : String(error);
13
+ console.warn(`coding-agent-harness postinstall: preset seed skipped (${message})`);
14
14
  }
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- // @ts-nocheck
3
2
  import fs from "node:fs";
4
3
  import path from "node:path";
5
4
  import { spawnSync } from "node:child_process";
@@ -9,7 +8,9 @@ const outDir = path.join(repoRoot, "tmp", `test-runner-emit-${process.pid}`);
9
8
  const typescriptVersion = "5.9.3";
10
9
  const options = parseArgs(process.argv.slice(2));
11
10
  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"], {
11
+ const npmArgs = ["exec", "--yes", "--package", `typescript@${typescriptVersion}`, "--", "tsc", "-p", "tsconfig.tests.json", "--outDir", outDir, "--noCheck"];
12
+ const npmCommand = resolveNpmCommand(npmArgs);
13
+ const emit = spawnSync(npmCommand.command, npmCommand.args, {
13
14
  cwd: repoRoot,
14
15
  encoding: "utf8",
15
16
  stdio: "inherit",
@@ -36,7 +37,7 @@ const result = spawnSync(process.execPath, [runner], {
36
37
  if (result.status !== 0)
37
38
  process.exit(result.status || 1);
38
39
  function parseArgs(argv) {
39
- const parsed = { test: undefined };
40
+ const parsed = {};
40
41
  for (let index = 0; index < argv.length; index += 1) {
41
42
  const arg = argv[index];
42
43
  if (arg === "--test") {
@@ -55,6 +56,12 @@ function requireValue(argv, index, option) {
55
56
  throw new Error(`${option} requires a value`);
56
57
  return value;
57
58
  }
59
+ function resolveNpmCommand(npmArgs) {
60
+ const npmExecPath = process.env.npm_execpath;
61
+ if (npmExecPath)
62
+ return { command: process.execPath, args: [npmExecPath, ...npmArgs] };
63
+ return { command: process.platform === "win32" ? "npm.cmd" : "npm", args: npmArgs };
64
+ }
58
65
  function linkPackageResources() {
59
66
  for (const entry of [
60
67
  "package.json",
@@ -56,6 +56,7 @@ Not every document is written for the same reader.
56
56
  ### Architecture / 架构
57
57
 
58
58
  - `architecture/overview.md` / `architecture/overview.zh-CN.md` — public architecture overview, including the CLI, dashboard, task lifecycle, migration rails, review gate, and release package surface. 公开架构总览,覆盖 CLI、Dashboard、任务生命周期、迁移轨道、审查门禁和发布包表面。
59
+ - `architecture/document-contract-kernel/README.md` — shared Document Contract Kernel for Full/Lite Skill semantics, compatibility matrix, change classification, Lite forbidden surfaces, and Lite to Full upgrade mapping.
59
60
 
60
61
  ### Methodology / 方法论
61
62
 
@@ -71,7 +72,7 @@ Not every document is written for the same reader.
71
72
  - `guides/migration-playbook.md` / `guides/migration-playbook.en-US.md` — smooth migration guide for existing legacy harness projects. 旧 Harness 项目的平滑迁移指南。
72
73
  - `guides/legacy-migration-agent-prompt.md` / `guides/legacy-migration-agent-prompt.zh-CN.md` — prompt contract for agents running baseline or full legacy migration. 给迁移 Agent 使用的执行合同。
73
74
  - `guides/full-legacy-migration-subagent-strategy.md` / `guides/full-legacy-migration-subagent-strategy.zh-CN.md` — full readable cutover strategy with subagent roles, adversarial review, and dashboard/CLI proof gates. 完整可读迁移的 subagent 分工、对抗审查和 Dashboard/CLI 证据门禁。
74
- - `guides/typescript-runtime-migration-closeout.md` — public closeout for the TypeScript runtime source-twin migration and the remaining `.mjs` shim policy. TypeScript runtime source twin 迁移收口和剩余 `.mjs` shim 策略。
75
+ - `guides/typescript-runtime-migration-closeout.md` — public closeout for the TypeScript runtime source-twin migration and the documented preset/dashboard JavaScript exceptions. TypeScript runtime source twin 迁移收口和已记录的 preset/dashboard JavaScript 例外。
75
76
 
76
77
  ## Repository Operating Models / 仓库运行模式
77
78
 
@@ -0,0 +1,150 @@
1
+ # Document Contract Kernel
2
+
3
+ The Document Contract Kernel is the product and Skill contract shared by the
4
+ Full Coding Agent Harness Skill and the future Lite Skill. It defines the stable
5
+ document semantics that both product surfaces must preserve without turning Lite
6
+ into a runtime mode or forcing Full to lower its automation and audit bar.
7
+
8
+ This contract is public source material. Full and Lite Skill text should be
9
+ generated from this shared source plus product overlays. Until a generator is
10
+ introduced, changes to the concepts or compatibility matrix must update this
11
+ file and the product overlays in the same change.
12
+
13
+ ## Kernel Naming Boundary
14
+
15
+ | Name | Layer | Responsibility | Must not do |
16
+ | --- | --- | --- | --- |
17
+ | Document Contract Kernel | Product / Skill contract | Shared document semantics, Lite/Full compatibility matrix, change classification, and upgrade mapping | Implement runtime behavior, read or write files, or define git, markdown, or path utilities |
18
+ | Domain Kernel | Runtime domain | Task identity, policy, state transitions, module ownership, and preset model | Describe Skill installation copy, package manager commands, UI text, or git transport |
19
+ | Infrastructure Kernel | Runtime infrastructure | Filesystem, path, template, markdown, git, date, id, and locale helpers | Own business semantics or product distribution semantics |
20
+
21
+ Always write the full name "Document Contract Kernel" in source and docs. Do not
22
+ shorten it to a generic `kernel/` label; that collides with later runtime
23
+ refactoring work.
24
+
25
+ ## Shared Concepts
26
+
27
+ | Concept | Kernel semantic | Lite expression | Full expression |
28
+ | --- | --- | --- | --- |
29
+ | Agent entry | The first project protocol an agent reads before acting | `AGENTS.md` | `AGENTS.md` plus optional `CLAUDE.md` shim |
30
+ | Project context | Durable project facts, separate from chat memory | `context/architecture`, `context/development`, `context/integrations` | Same directories, with optional source packs and generated indexes |
31
+ | Task package | The smallest reviewable unit of work for one objective | `tasks/<id>/brief.md`, `task_plan.md`, `progress.md`, `walkthrough.md` | `planning/tasks/<id>/` or module task package |
32
+ | Brief | Purpose, scope, and first-read entry for the task | Required | Required |
33
+ | Task plan | Goal, scope, steps, acceptance criteria, and verification intent | Required | Required |
34
+ | Progress evidence | Execution log with evidence references | Required, hand-maintained | Required, may be updated by lifecycle command |
35
+ | Review | Independent review or human confirmation material | Optional | Required for standard and complex work |
36
+ | Visual map | Phase, state, and structure aid | Optional for simple work | Required in CLI-created task packages |
37
+ | Walkthrough | Closeout summary, validation, residual risks, and lesson decision | Required | Required |
38
+ | Regression | The project's important validation surfaces | Simple checklist | Regression SSoT, cadence, and checks |
39
+ | Lessons | Reusable learning from the work | Optional `lessons.md` | Lesson candidates plus promoted lesson details |
40
+
41
+ ## Compatibility Matrix
42
+
43
+ Compatibility matrix version: `document-contract-kernel-v0.5`.
44
+
45
+ | Surface | Lite | Full | Kernel classification |
46
+ | --- | --- | --- | --- |
47
+ | `AGENTS.md` | Required | Required | Shared |
48
+ | `CLAUDE.md` | Optional shim | Optional shim | Shared optional |
49
+ | `context/` | Required | Required | Shared |
50
+ | `brief.md` | Required | Required | Shared |
51
+ | `task_plan.md` | Required | Required | Shared |
52
+ | `progress.md` | Required | Required | Shared |
53
+ | `walkthrough.md` | Required | Required | Shared |
54
+ | `review.md` | Optional | Required for standard and complex work | Shared with profile differences |
55
+ | `visual_map.md` | Optional for simple work | Required in CLI-created task packages | Shared with profile differences |
56
+ | `regression.md` | Simple checklist | Regression SSoT and cadence | Shared with profile differences |
57
+ | `lessons.md` | Optional simple table | Lesson candidates and promoted details | Shared with profile differences |
58
+ | `harness.yaml` | Forbidden | Required | Full-only |
59
+ | Generated ledger | Forbidden | Supported | Full-only |
60
+ | Dashboard / Workbench | Forbidden | Supported | Full-only |
61
+ | Preset | Forbidden | Supported | Full-only |
62
+ | Module Registry | Forbidden | Supported | Full-only |
63
+ | Lifecycle CLI commands | Forbidden | Supported | Full-only |
64
+ | `npm install` / `npx coding-agent-harness` | Forbidden | Allowed with user consent | Full-only |
65
+ | Node.js version requirement | Forbidden | Required by the source package and runtime | Full-only |
66
+
67
+ ## Change Classification
68
+
69
+ Every PR or task that edits Skill, template, or public product docs must be
70
+ classified before implementation.
71
+
72
+ | Change type | Definition | Must sync | Must not do |
73
+ | --- | --- | --- | --- |
74
+ | Kernel change | Changes the base semantics of AGENTS, context, task package, progress, walkthrough, review, regression, or lessons | Lite overlay, Full overlay, and this compatibility matrix | Update only the Full Skill |
75
+ | Full-only change | CLI, Dashboard, Preset, module registry, generated ledger, transaction, migration, or runtime automation | Full Skill, runtime docs, and Full overlay | Leak into Lite source |
76
+ | Lite-only change | Document-only scaffold, lower cognitive load wording, and hand-maintained task material | Lite overlay | Weaken Full audit or automation requirements |
77
+ | Migration bridge | Maps Lite project material into a Full Harness project | Full migration docs and Lite upgrade note | Promise that Lite supports Full surfaces in place |
78
+
79
+ PR checklist:
80
+
81
+ - [ ] Does this change alter the Document Contract Kernel?
82
+ - [ ] If yes, did Lite and Full overlays both change?
83
+ - [ ] If yes, did the compatibility matrix version or content change?
84
+ - [ ] If Full-only, does the Lite forbidden-surface check still pass?
85
+ - [ ] If Lite-only, did Full retain its review, regression, and automation requirements?
86
+
87
+ ## Lite Forbidden Surfaces
88
+
89
+ Lite is a document-only Skill surface. It must not contain product or runtime
90
+ surfaces that imply package installation, CLI operation, dashboard operation,
91
+ preset execution, generated governance, module registry ownership, lifecycle
92
+ commands, or a Node.js runtime requirement.
93
+
94
+ The canonical blocked pattern list is
95
+ `docs-release/architecture/document-contract-kernel/products/lite-forbidden-surfaces.txt`.
96
+ Run the guard with:
97
+
98
+ ```bash
99
+ node scripts/check-lite-forbidden-surfaces.mts
100
+ ```
101
+
102
+ The guard scans Lite product source files only. It intentionally does not scan
103
+ this contract document or the forbidden pattern list, because those files must
104
+ name Full-only surfaces to define the boundary.
105
+
106
+ ## Lite to Full Upgrade Path
107
+
108
+ A Lite project can be used as migration input for Full Harness adoption. Lite
109
+ does not promise that Full runtime adapters can read or update its files in
110
+ place.
111
+
112
+ 1. Read Lite `AGENTS.md`, `context/`, `tasks/`, `regression.md`, and `lessons.md`.
113
+ 2. Create the Full harness manifest and v2 directory structure.
114
+ 3. Map `tasks/<id>/` into Full `planning/tasks/<id>/`.
115
+ 4. If `review.md` or `visual_map.md` is missing, create adoption-needed follow-up
116
+ work or fill it within an approved migration budget.
117
+ 5. Project Lite `regression.md` into Regression SSoT.
118
+ 6. Project Lite `lessons.md` into lesson candidates, not promoted lesson details.
119
+ 7. Run Full migration and check evidence before claiming Full adoption.
120
+
121
+ ## Source, Overlay, Generator Path
122
+
123
+ The maintainable target is shared source plus product overlays:
124
+
125
+ ```text
126
+ docs-release/architecture/document-contract-kernel/
127
+ README.md
128
+ products/
129
+ lite-skill-overlay.md
130
+ full-skill-overlay.md
131
+ lite-forbidden-surfaces.txt
132
+ ```
133
+
134
+ Future generator shape:
135
+
136
+ 1. Render this shared Document Contract Kernel source.
137
+ 2. Apply the Full or Lite product overlay.
138
+ 3. Stamp the compatibility matrix version into the generated Skill.
139
+ 4. Run Lite forbidden-surface checks after Lite render.
140
+ 5. Run Full concept coverage checks after Full render.
141
+
142
+ Generated targets should be:
143
+
144
+ ```text
145
+ SKILL.md
146
+ skills/coding-agent-harness-lite/SKILL.md
147
+ ```
148
+
149
+ The Lite target is not created in Phase 0.5. This phase establishes the public
150
+ contract and drift guard that a later Lite implementation must use.