coding-agent-harness 1.0.4 → 1.0.5

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 (100) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE +661 -21
  3. package/LICENSE-EXCEPTION.md +37 -0
  4. package/README.md +33 -1
  5. package/README.zh-CN.md +23 -1
  6. package/SKILL.md +9 -8
  7. package/docs-release/architecture/overview.md +1 -1
  8. package/docs-release/architecture/overview.zh-CN.md +1 -1
  9. package/docs-release/architecture/system-explainer/01-system-overview.md +217 -0
  10. package/docs-release/architecture/system-explainer/02-module-dependency.md +257 -0
  11. package/docs-release/architecture/system-explainer/03-task-lifecycle.md +304 -0
  12. package/docs-release/architecture/system-explainer/04-check-and-governance.md +239 -0
  13. package/docs-release/architecture/system-explainer/05-data-flow.md +276 -0
  14. package/docs-release/architecture/system-explainer/06-preset-and-migration.md +303 -0
  15. package/docs-release/architecture/system-explainer/README.md +67 -0
  16. package/docs-release/architecture/system-explainer/en-US/01-system-overview.md +226 -0
  17. package/docs-release/architecture/system-explainer/en-US/02-module-dependency.md +263 -0
  18. package/docs-release/architecture/system-explainer/en-US/03-task-lifecycle.md +319 -0
  19. package/docs-release/architecture/system-explainer/en-US/04-check-and-governance.md +250 -0
  20. package/docs-release/architecture/system-explainer/en-US/05-data-flow.md +290 -0
  21. package/docs-release/architecture/system-explainer/en-US/06-preset-and-migration.md +323 -0
  22. package/docs-release/architecture/system-explainer/en-US/README.md +70 -0
  23. package/docs-release/guides/agent-installation.en-US.md +8 -7
  24. package/docs-release/guides/agent-installation.md +9 -7
  25. package/docs-release/guides/preset-development.md +26 -2
  26. package/docs-release/guides/task-state-machine.en-US.md +30 -13
  27. package/docs-release/guides/task-state-machine.md +30 -13
  28. package/examples/minimal-project/docs/09-PLANNING/TASKS/demo-task/INDEX.md +60 -0
  29. package/package.json +3 -2
  30. package/references/harness-ledger.md +1 -1
  31. package/scripts/commands/migration-command.mjs +30 -0
  32. package/scripts/commands/task-command.mjs +26 -25
  33. package/scripts/harness.mjs +7 -3
  34. package/scripts/lib/capability-registry.mjs +17 -21
  35. package/scripts/lib/check-module-parallel.mjs +9 -16
  36. package/scripts/lib/check-profiles.mjs +35 -81
  37. package/scripts/lib/check-task-contracts.mjs +13 -5
  38. package/scripts/lib/core-shared.mjs +55 -2
  39. package/scripts/lib/dashboard-data.mjs +126 -18
  40. package/scripts/lib/dashboard-workbench.mjs +80 -1
  41. package/scripts/lib/dashboard-writer.mjs +6 -2
  42. package/scripts/lib/git-status-summary.mjs +1 -1
  43. package/scripts/lib/governance-sync.mjs +180 -83
  44. package/scripts/lib/harness-core.mjs +1 -0
  45. package/scripts/lib/markdown-utils.mjs +33 -0
  46. package/scripts/lib/migration-planner.mjs +4 -6
  47. package/scripts/lib/phase-kind.mjs +50 -0
  48. package/scripts/lib/preset-engine.mjs +5 -8
  49. package/scripts/lib/preset-registry.mjs +188 -39
  50. package/scripts/lib/review-confirm-git-gate.mjs +1 -1
  51. package/scripts/lib/status-builder.mjs +88 -0
  52. package/scripts/lib/status-dashboard-renderer.mjs +7 -4
  53. package/scripts/lib/task-audit-metadata.mjs +385 -0
  54. package/scripts/lib/task-audit-migration.mjs +350 -0
  55. package/scripts/lib/task-completion-consistency.mjs +11 -1
  56. package/scripts/lib/task-lifecycle/create-task-helpers.mjs +67 -0
  57. package/scripts/lib/task-lifecycle/phase-sync.mjs +88 -0
  58. package/scripts/lib/task-lifecycle/review-confirm.mjs +40 -29
  59. package/scripts/lib/task-lifecycle/review-gates.mjs +13 -10
  60. package/scripts/lib/task-lifecycle/review-submission.mjs +63 -0
  61. package/scripts/lib/task-lifecycle/scaffold-provenance.mjs +49 -0
  62. package/scripts/lib/task-lifecycle/template-files.mjs +53 -0
  63. package/scripts/lib/task-lifecycle.mjs +114 -147
  64. package/scripts/lib/task-metadata.mjs +118 -0
  65. package/scripts/lib/task-review-model.mjs +54 -68
  66. package/scripts/lib/task-scanner.mjs +70 -143
  67. package/skills/preset-creator/references/complex-task-skeleton/brief.md +11 -0
  68. package/templates/AGENTS.md.template +7 -5
  69. package/templates/dashboard/assets/app-src/00-state.js +12 -0
  70. package/templates/dashboard/assets/app-src/10-router.js +3 -0
  71. package/templates/dashboard/assets/app-src/20-overview.js +7 -3
  72. package/templates/dashboard/assets/app-src/35-task-detail.js +46 -6
  73. package/templates/dashboard/assets/app-src/55-presets.js +375 -0
  74. package/templates/dashboard/assets/app-src/60-shared.js +3 -1
  75. package/templates/dashboard/assets/app-src/90-bindings.js +131 -0
  76. package/templates/dashboard/assets/app.css +583 -0
  77. package/templates/dashboard/assets/app.css.manifest.json +1 -0
  78. package/templates/dashboard/assets/app.js +578 -10
  79. package/templates/dashboard/assets/app.manifest.json +1 -0
  80. package/templates/dashboard/assets/css-src/00-foundation.css +4 -0
  81. package/templates/dashboard/assets/css-src/40-detail-modules-migration.css +62 -0
  82. package/templates/dashboard/assets/css-src/45-presets.css +516 -0
  83. package/templates/dashboard/assets/i18n.js +140 -2
  84. package/templates/planning/INDEX.md +87 -0
  85. package/templates/planning/brief.md +1 -1
  86. package/templates/planning/module_session_prompt.md +1 -0
  87. package/templates/planning/review.md +0 -18
  88. package/templates/planning/task_plan.md +4 -43
  89. package/templates/planning/visual_map.md +13 -9
  90. package/templates/planning/visual_map.simple.md +52 -0
  91. package/templates/reference/execution-workflow-standard.md +29 -2
  92. package/templates-zh-CN/AGENTS.md.template +7 -5
  93. package/templates-zh-CN/planning/INDEX.md +87 -0
  94. package/templates-zh-CN/planning/brief.md +1 -1
  95. package/templates-zh-CN/planning/module_session_prompt.md +1 -0
  96. package/templates-zh-CN/planning/review.md +0 -18
  97. package/templates-zh-CN/planning/task_plan.md +3 -63
  98. package/templates-zh-CN/planning/visual_map.md +14 -7
  99. package/templates-zh-CN/planning/visual_map.simple.md +48 -0
  100. package/templates-zh-CN/reference/execution-workflow-standard.md +31 -6
@@ -0,0 +1,276 @@
1
+ # 05 — 数据流:从 Markdown 到 Dashboard
2
+
3
+ ## Level 0 — 数据的起点和终点
4
+
5
+ ```mermaid
6
+ flowchart LR
7
+ A["📄 Markdown 文件\n(docs/ 下的源文件)"]
8
+ B["⚙️ Scanner\n(解析 + 验证)"]
9
+ C["📊 Dashboard\n(HTML + JSON)"]
10
+
11
+ A -->|"读取"| B -->|"生成"| C
12
+ ```
13
+
14
+ 所有数据都来自 Markdown 文件,没有数据库,没有外部服务。
15
+ 每次运行都从文件系统重新读取,不缓存任何中间状态。
16
+
17
+ ---
18
+
19
+ ## Level 1 — 哪些文件是数据源
20
+
21
+ ```mermaid
22
+ flowchart TD
23
+ Sources["数据源文件"]
24
+
25
+ Sources --> TP["task_plan.md\n预算 / 标题 / 元数据 / Preset 信息\nTask Contract 标记"]
26
+ Sources --> PR["progress.md\n当前状态 / 操作日志"]
27
+ Sources --> VM["visual_map.md\n阶段列表 / 完成度 / 证据状态"]
28
+ Sources --> RV["review.md\nfindings 表格 / 人工确认块"]
29
+ Sources --> BR["brief.md\n任务摘要"]
30
+ Sources --> LC["lesson_candidates.md\nLesson 候选 / 决策状态"]
31
+ Sources --> HL["Harness-Ledger.md\n全局账本(所有任务汇总行)"]
32
+ Sources --> ES["execution_strategy.md\nsubagent 授权状态"]
33
+ ```
34
+
35
+ ---
36
+
37
+ ## Level 2 — Scanner 如何处理这些文件
38
+
39
+ Scanner 层(`task-scanner.mjs` + `task-review-model.mjs`)把原始 Markdown 解析成结构化对象。
40
+
41
+ ### collectTasks() 的发现流程
42
+
43
+ ```mermaid
44
+ flowchart TD
45
+ CT["collectTasks()"]
46
+
47
+ CT --> Discover["listTaskPlanPaths()\n扫描两个根目录:\n09-PLANNING/TASKS/\n09-PLANNING/MODULES/\n过滤模板和归档目录"]
48
+
49
+ Discover --> ReadFiles["对每个任务目录读取 9 个文件\ntask_plan / brief / progress\nreview / visual_map\nexecution_strategy\nlesson_candidates / findings / context"]
50
+
51
+ ReadFiles --> Parse["解析各文件"]
52
+
53
+ Parse --> P1["parseTaskBudget()\n从 task_plan.md 提取 budget"]
54
+ Parse --> P2["parseTaskState()\n从 progress.md 提取 state"]
55
+ Parse --> P3["parsePhases()\n从 visual_map.md 提取阶段列表"]
56
+ Parse --> P4["parseAgentReviewSubmission()\n从 review.md 提取 Agent 提交状态"]
57
+ Parse --> P5["parseReviewConfirmation()\n从 review.md 提取人工确认块"]
58
+ Parse --> P6["parseLessonCandidateStatus()\n从 lesson_candidates.md 提取决策状态"]
59
+ Parse --> P7["parseTaskTombstone()\n从 task_plan.md 提取软删除状态"]
60
+
61
+ P1 & P2 & P3 & P4 & P5 & P6 & P7 --> Derive["派生计算(纯函数)"]
62
+
63
+ Derive --> LS["deriveLifecycleState()\n综合推导 lifecycleState"]
64
+ Derive --> QS["deriveTaskQueues()\n决定任务属于哪些队列"]
65
+ Derive --> RQS["deriveReviewQueueState()\n推导 reviewQueueState"]
66
+ ```
67
+
68
+ ### parseTaskState() 的格式
69
+
70
+ 从 `progress.md` 的 `## Current Status` 或 `## Status` 标题后的第一行提取状态:
71
+
72
+ ```markdown
73
+ ## Current Status
74
+
75
+ in_progress
76
+ ```
77
+
78
+ 支持中文别名(`进行中` → `in_progress`)。如果格式不符合预期,会降级到遗留表格解析模式。
79
+
80
+ ### parsePhases() 的表格格式
81
+
82
+ 从 `visual_map.md` 中查找 `Phase ID` 列标题的表格,提取 9 个字段:
83
+
84
+ ```markdown
85
+ | Phase ID | Depends On | State | Completion | Output | Required Evidence | Evidence Status | Blocking Risk | Owner / Handoff |
86
+ | --- | --- | --- | --- | --- | --- | --- | --- | --- |
87
+ | P1 | — | done | 100 | ... | E-001 | present | low | coordinator |
88
+ ```
89
+
90
+ 依赖字段支持逗号、分号、`&` 分隔的多值。
91
+
92
+ ---
93
+
94
+ ## Level 2 — buildStatus() 把 Scanner 结果组装成什么
95
+
96
+ ```mermaid
97
+ flowchart TD
98
+ BS["buildStatus()"]
99
+
100
+ BS --> Tasks["tasks[]\n每个任务的完整结构化数据\n(见下方字段列表)"]
101
+ BS --> Failures["failures[]\n硬失败列表"]
102
+ BS --> Warnings["warnings[]\n软警告列表"]
103
+ BS --> Caps["capabilities[]\n能力注册表状态"]
104
+ BS --> Git["git\nGit 状态摘要(dirty files 等)"]
105
+ BS --> Summary["summary\nbriefCoverage / visualMapCoverage\nfullCutoverEligible 等汇总指标"]
106
+ ```
107
+
108
+ **task 对象的完整字段**:
109
+
110
+ | 字段 | 含义 |
111
+ | --- | --- |
112
+ | `path` | 相对路径 |
113
+ | `title` | 任务标题 |
114
+ | `state` | 任务状态(`in_progress / done / ...`) |
115
+ | `stateSource` | 状态来源(`valid / invalid`) |
116
+ | `budget` | 预算等级(`simple / standard / complex`) |
117
+ | `lifecycleState` | 派生的生命周期状态 |
118
+ | `reviewQueueState` | 审查队列状态 |
119
+ | `taskQueues[]` | 所属队列列表 |
120
+ | `phases[]` | 阶段列表(含 id / state / completion / evidenceStatus) |
121
+ | `closeoutStatus` | 收口状态 |
122
+ | `tombstone` | 软删除信息 |
123
+ | `briefSource` | brief 来源(`standalone / missing / ...`) |
124
+ | `visualMapSource` | visual map 来源(`canonical / legacy / missing`) |
125
+ | `taskPreset` | 使用的 Preset ID |
126
+ | `presetVersion` | Preset 版本 |
127
+ | `handoffs` | 交接信息数组 |
128
+ | `lessonCandidateDecisionComplete` | Lesson 决策是否完成 |
129
+
130
+ ---
131
+
132
+ ## Level 3 — buildDashboardBundle() 在 status 基础上再加什么
133
+
134
+ `buildDashboardBundle()` 调用 `buildStatus()` 后,再额外收集四类数据:
135
+
136
+ ```mermaid
137
+ flowchart TD
138
+ Bundle["buildDashboardBundle()"]
139
+
140
+ Bundle --> Status["status\n(来自 buildStatus)"]
141
+ Bundle --> Documents["documents[]\n所有 Markdown 文档的内容\n每个文档:id / path / title / type / content"]
142
+ Bundle --> Tables["tables[]\n从文档中提取的 Markdown 表格\n每个表格:列定义 + 行数据"]
143
+ Bundle --> Graph["graph\n任务依赖关系图\nnodes(任务/阶段/模块/步骤)\nedges(依赖/包含/交接关系)"]
144
+ Bundle --> Adoption["adoption\n迁移采纳状态分析\n(warnings 分类汇总,含优先级和修复建议)"]
145
+ ```
146
+
147
+ ### documents 收集范围
148
+
149
+ `collectMarkdownDocuments()` 收集哪些文件:
150
+
151
+ ```mermaid
152
+ flowchart TD
153
+ Collect["collectMarkdownDocuments()"]
154
+
155
+ Collect --> Fixed["固定路径(存在时收集)\nHarness-Ledger.md\n09-PLANNING/Module-Registry.md\n05-TEST-QA/Regression-SSoT.md\n10-WALKTHROUGH/Closeout-SSoT.md"]
156
+
157
+ Collect --> Walkthrough["10-WALKTHROUGH/ 下所有 .md\n(排除 _archive/ 和 _ 开头文件)"]
158
+
159
+ Collect --> TaskDocs["每个任务目录下:\nbrief / task_plan / execution_strategy\nvisual_map / lesson_candidates\nprogress / review / findings\nreferences/INDEX.md / artifacts/INDEX.md"]
160
+
161
+ Collect --> ModuleDocs["09-PLANNING/MODULES/ 下:\n每个模块的 module_plan.md\n每个模块的 brief.md"]
162
+
163
+ Collect --> Lessons["01-GOVERNANCE/lessons/ 下所有 .md"]
164
+ ```
165
+
166
+ 收集后统一过滤掉 `_archive/`、`_task-template/`、`_optional-structures/` 路径。
167
+
168
+ ### graph 数据结构
169
+
170
+ 图包含两类元素:
171
+
172
+ - **nodes**:任务节点、阶段节点、模块节点、步骤节点
173
+ - **edges**:依赖关系(阶段 → 阶段)、包含关系(任务 → 阶段)、交接关系(步骤 → 步骤)
174
+
175
+ 如果依赖的源节点不存在,会创建一个 `external-dependency` 类型的虚拟节点。
176
+
177
+ ---
178
+
179
+ ## Level 2 — 两种 Dashboard 生成模式
180
+
181
+ ```mermaid
182
+ flowchart LR
183
+ subgraph "静态模式(只读快照)"
184
+ SC["harness dashboard\n--out-dir ./out"]
185
+ SC --> SH["index.html\n(内联所有资源)"]
186
+ SC --> SJ["dashboard-data.json\n(供外部工具读取)"]
187
+ SC --> SF["status.json / tables.json\ndocuments.json / graph.json\nadoption.json"]
188
+ end
189
+
190
+ subgraph "动态模式(Workbench)"
191
+ DC["harness dev"]
192
+ DC --> HTTP["本地 HTTP 服务\nlocalhost:PORT"]
193
+ DC --> Watch["文件监听\n轮询模式,每 1 秒检查一次\n变化后延迟 250ms 重新生成"]
194
+ HTTP --> Browser["浏览器实时查看\n支持 review-confirm 等写操作"]
195
+ end
196
+ ```
197
+
198
+ **关键边界**:静态 Dashboard 是只读的,不能触发任何写操作。
199
+ 只有 `harness dev`(Workbench 模式)才能执行 `review-confirm`、`task-start` 等写操作。
200
+
201
+ ### Dashboard HTML 生成方式
202
+
203
+ Dashboard HTML 通过字符串拼接生成(非模板引擎)。
204
+ `app.js` 通过 manifest 或直接读取获得——如果存在 manifest,
205
+ 按顺序读取并拼接多个源文件(`app-src/` 目录下的模块化源码)。
206
+
207
+ payload 中的 `<` 被转义为 `<` 防止 HTML 注入。
208
+
209
+ ### 文件监听实现
210
+
211
+ `dashboard-workbench.mjs` 的文件监听采用**轮询模式**(`startPollingWatch()`):
212
+ - 每 1000ms 检查一次目录树的最新修改时间(mtime)
213
+ - 检测到变化时,延迟 250ms 后触发重新生成(防抖)
214
+ - 监听范围:整个 `target.docsRoot`,排除 `.git`、`node_modules`、`tmp`
215
+
216
+ ---
217
+
218
+ ## Level 3 — markdown-utils.mjs 的核心能力
219
+
220
+ 整个系统能从 Markdown 文件派生状态,技术基础是 `markdown-utils.mjs` 提供的表格解析能力:
221
+
222
+ | 函数 | 用途 |
223
+ | --- | --- |
224
+ | `markdownTableRows()` | 提取所有表格行 |
225
+ | `parseAllMarkdownTables()` | 解析文档中所有表格,返回结构化对象数组 |
226
+ | `splitMarkdownRow()` | 分割行单元格(处理转义管道符和代码块) |
227
+ | `tableAfterHeading()` | 定位特定标题后的表格 |
228
+ | `getCell()` | 按列名获取单元格(支持多个别名) |
229
+ | `splitList()` | 分割逗号/分号/加号分隔的列表 |
230
+ | `splitDependencies()` | 分割依赖,过滤 `none/n/a` 等占位符 |
231
+
232
+ **代码块内的管道符**:`splitMarkdownRow()` 会跟踪代码块状态,
233
+ 代码块内的 `|` 不会被当作列分隔符,保留原始内容。
234
+
235
+ ---
236
+
237
+ ## Level 2 — 设计决策
238
+
239
+ ### 为什么 Dashboard 是纯 HTML + vanilla JS,不用 React/Vite
240
+
241
+ harness 通过 `npx` 分发,引入 React/Vite 意味着用户每次运行都要拉取大量构建依赖,
242
+ 破坏零依赖的可移植性。静态 HTML 可以直接从 `file://` 打开,
243
+ 也可以作为 CI 证据快照分享,不需要任何运行时。
244
+
245
+ app-src 里的 vanilla JS 组件(`DashboardShell`、`SidebarNav`、`TableView` 等)
246
+ 通过 manifest 按顺序拼接,每个文件 < 600 行,git diff 可读,不需要 webpack/esbuild。
247
+
248
+ ### 为什么静态 Dashboard 是只读的
249
+
250
+ 静态 Dashboard 的定位是"可分享的证据快照"——它可以被 CI 生成、离线打开、
251
+ 发给外部审查者。这些场景下写操作没有安全边界(没有 CSRF/Origin/Host 校验)。
252
+ 写操作只能在 Workbench 模式下执行,因为 Workbench server 绑定 `127.0.0.1`
253
+ 并有完整的安全校验链。
254
+
255
+ ### 为什么 `harness dev` 和 `harness dashboard` 是两个命令
256
+
257
+ `harness dashboard` 生成静态只读快照(适合 CI、迁移报告、离线证据)。
258
+ `harness dev` 启动本地动态 Workbench server,支持文件 watch、自动刷新、
259
+ review-confirm 写操作。两者的边界是:**静态快照可以分享,动态 Workbench 只能本地用**。
260
+
261
+ ### 为什么文件监听用轮询而不是 fs.watch
262
+
263
+ `fs.watch` 在 macOS 上对深层目录树有已知的漏报问题,
264
+ 而 harness 的 docs 目录结构是多层嵌套的 Markdown 文件树。
265
+ 轮询方案实现简单、行为可预期、不引入 chokidar 等第三方依赖(与零依赖原则一致)。
266
+ 1 秒轮询 + 250ms debounce 对人类编辑场景足够。
267
+
268
+ ### 为什么不引入 SQLite 或 JSON 数据库
269
+
270
+ 引入 JSON/SQLite 若没有清楚的 authority 边界,会形成 Markdown、JSON、SQLite 三份事实互相漂移。
271
+ Git review 对 Markdown/JSON 友好,对 SQLite diff 不友好。
272
+ 当前规模是"几百任务",generated JSON + indexed in-memory filtering 足够。
273
+
274
+ 决策是:Markdown 是唯一事实源,generated JSON index 是可再生缓存,
275
+ SQLite 只在任务数量和查询复杂度超过 JSON 能承载时才考虑引入,
276
+ 且只能作为可再生查询缓存,不能手写、不能作为权威事实源。
@@ -0,0 +1,303 @@
1
+ # 06 — Preset 系统与迁移引擎
2
+
3
+ ## Level 0 — 两个独立子系统
4
+
5
+ 本文档覆盖两个相关但独立的子系统:
6
+
7
+ ```mermaid
8
+ flowchart LR
9
+ A["Preset 系统\n可复用的任务方法包\n(new-task 时注入)"]
10
+ B["迁移引擎\n旧项目接入 harness\n(migrate-* 命令)"]
11
+
12
+ A -->|"legacy-migration preset\n是迁移引擎的专用 Preset"| B
13
+ ```
14
+
15
+ 它们的关系:迁移引擎有一个专用 Preset(`legacy-migration`),但 Preset 系统本身是通用的,
16
+ 任何类型的任务都可以有自己的 Preset。
17
+
18
+ ---
19
+
20
+ ## Part 1 — Preset 系统
21
+
22
+ ### Level 1 — Preset 是什么
23
+
24
+ Preset 是一个**可复用的任务方法包**,打包了特定类型任务所需的:
25
+ - 模板追加内容(在标准模板基础上追加 preset 专属内容)
26
+ - 执行脚本(plan / scaffold 阶段运行)
27
+ - 检查脚本(验证 preset 合规)
28
+ - 资源声明(参考文件、工件、必需阅读)
29
+
30
+ ```mermaid
31
+ flowchart TD
32
+ Preset["Preset 包\npresets/<id>/"]
33
+
34
+ Preset --> Manifest["preset.yaml\n包清单(必须)"]
35
+ Preset --> Templates["templates/\n模板追加文件"]
36
+ Preset --> Scripts["scripts/\nplan / scaffold 脚本"]
37
+ Preset --> Checks["checks/\n检查脚本"]
38
+ Preset --> Workbench["workbench/\nDashboard 面板定义(可选)"]
39
+ ```
40
+
41
+ ### Level 1 — 分层发现:三个搜索层
42
+
43
+ Preset 按 **project → user → builtin** 的顺序搜索,同名 Preset 采用**首匹配优先**策略:
44
+
45
+ ```mermaid
46
+ flowchart LR
47
+ P["project 层\n<target>/.coding-agent-harness/presets/\n(优先级最高)"]
48
+ U["user 层\n~/.coding-agent-harness/presets/\n(次优先)"]
49
+ B["builtin 层\npackage/presets/\n(最低优先级)"]
50
+
51
+ P -->|"未找到时继续"| U -->|"未找到时继续"| B
52
+ ```
53
+
54
+ 这个设计允许:
55
+ - **项目级覆盖**:在项目里放一个同名 Preset,覆盖 builtin 版本
56
+ - **用户级覆盖**:在 home 目录放 Preset,对所有项目生效
57
+ - **builtin 兜底**:package 自带的 Preset 作为默认实现
58
+
59
+ `harness preset install --project <preset-id>` 把 Preset 安装到项目层;
60
+ `harness preset uninstall --project <preset-id>` 从项目层移除(不影响 user 和 builtin 层)。
61
+
62
+ ### Level 2 — preset.yaml 的结构
63
+
64
+ ```mermaid
65
+ flowchart TD
66
+ YAML["preset.yaml"]
67
+
68
+ YAML --> Meta["基本信息\nid / version / purpose\ncompatibleBudgets / localeSupport"]
69
+ YAML --> Task["task 配置\nkind / requiresFromSession\nprojectLevelOnly"]
70
+ YAML --> EP["entrypoints\n(见下)"]
71
+ YAML --> WS["writeScopes\n限定写入路径(安全边界)"]
72
+ YAML --> Resources["resources\nreferences / artifacts / context.requiredReads"]
73
+ YAML --> Audit["audit\nmanifestRequired: true\nevidenceFiles[]"]
74
+ ```
75
+
76
+ ### Level 3 — Entrypoint 类型系统
77
+
78
+ 每个 entrypoint 有两个维度:**name**(触发时机)和 **type**(执行方式):
79
+
80
+ ```mermaid
81
+ flowchart LR
82
+ subgraph "Name(触发时机)"
83
+ N1["newTask\nharness new-task --preset X 时"]
84
+ N2["plan\n迁移计划阶段"]
85
+ N3["scaffold\n批量补全阶段"]
86
+ N4["check\n验证阶段"]
87
+ end
88
+
89
+ subgraph "Type(执行方式)"
90
+ T1["template\n追加模板内容到任务文件"]
91
+ T2["script\n运行 Node.js 脚本"]
92
+ T3["check\n运行检查脚本"]
93
+ end
94
+ ```
95
+
96
+ 三种执行方式的区别:
97
+ - **template**:渲染模板文件,将结果追加到任务文件(`task_plan.md`、`execution_strategy.md` 等)
98
+ - **script**:执行 Node.js 脚本,脚本可读写文件系统,返回结果作为 audit 证据
99
+ - **check**:执行检查脚本,验证 preset 应用的完整性,失败时阻止任务创建
100
+
101
+ 每个 entrypoint 还声明 `writes`(允许写入的路径 glob)和 `reads`(允许读取的路径 glob)。
102
+
103
+ ### Level 3 — writeScopes 安全边界
104
+
105
+ `writeScopes` 是一个路径白名单,限制 Preset 只能写入声明的目录。
106
+ `assertPresetWriteScope()` 在每次文件写入时检查相对路径是否匹配任何 scope。
107
+
108
+ ```
109
+ writeScopes:
110
+ tasks:
111
+ path: docs/09-PLANNING/TASKS/**
112
+ access: write
113
+ ```
114
+
115
+ 支持 `path/**` 通配符表示目录及其所有子目录。
116
+ 相对路径必须规范化(无 `../`、无绝对路径),防止目录遍历攻击。
117
+
118
+ ### Level 2 — Preset 的生命周期
119
+
120
+ ```mermaid
121
+ sequenceDiagram
122
+ participant User
123
+ participant CLI as harness CLI
124
+ participant Registry as preset-registry.mjs
125
+ participant Engine as preset-engine.mjs
126
+ participant TaskDir as 任务目录
127
+
128
+ User->>CLI: harness new-task my-task --preset legacy-migration --from-session session.json
129
+ CLI->>Registry: listPresetPackages()\n(分层发现,首匹配优先)
130
+ Registry-->>CLI: preset 对象(含 manifest + 路径)
131
+ CLI->>TaskDir: 创建标准任务骨架(brief / task_plan / visual_map 等)
132
+ CLI->>Engine: executeEntrypoint("newTask", preset, taskDir)
133
+ Engine->>TaskDir: 追加 preset 模板内容到任务文件
134
+ CLI->>TaskDir: 写入 preset metadata 到 task_plan.md
135
+ CLI->>TaskDir: 生成证据包(preset-manifest.json / preset-audit.json)
136
+ CLI-->>User: 任务创建完成
137
+ ```
138
+
139
+ ### Level 1 — 当前可用 Preset
140
+
141
+ | Preset ID | 用途 | 兼容 Budget | 特殊要求 |
142
+ | --- | --- | --- | --- |
143
+ | `legacy-migration` | 旧 harness 项目迁移到 v1.0 | complex | 需要 `--from-session session.json` |
144
+ | `lesson-sedimentation` | Lesson 沉淀任务 | standard, complex | 无 |
145
+ | `module` | 模块并行工作任务 | standard, complex | 需要 `--module <module-id>` |
146
+ | `standard-task` | 标准任务方法 | standard, complex | 无 |
147
+
148
+ ---
149
+
150
+ ## Part 2 — 迁移引擎
151
+
152
+ ### Level 1 — 迁移的三个阶段
153
+
154
+ ```mermaid
155
+ flowchart LR
156
+ A["① migrate-plan\n分析差距\n生成动作队列"] --> B["② migrate-run\n执行迁移动作\n写入 session 记录"] --> C["③ migrate-verify\n验证迁移结果\n对比 baseline"]
157
+ ```
158
+
159
+ ### Level 2 — migrate-plan 做什么
160
+
161
+ `buildMigrationPlan()` 识别 6 类差距:
162
+
163
+ ```mermaid
164
+ flowchart TD
165
+ Plan["harness migrate-plan"]
166
+
167
+ Plan --> Scan["扫描目标仓库\nbuildStatus(非严格模式)"]
168
+ Scan --> Detect["检测已有能力\nreadCapabilityRegistry()"]
169
+ Detect --> Gaps["识别差距(6 类)"]
170
+
171
+ Gaps --> TaskActions["taskActions\n缺失 execution_strategy.md\n缺失 visual_map.md 的活跃任务"]
172
+ Gaps --> ReviewActions["reviewActions\n缺失审查字段\n(Reviewer Identity 等)"]
173
+ Gaps --> LegacyActions["legacyActions\n缺失必需的参考文件"]
174
+ Gaps --> LegacyResiduals["legacyResiduals\n历史任务的合同差距\n(不应自动迁移)"]
175
+ Gaps --> WeakBriefs["weakBriefTasks\nbrief.md 质量不达标"]
176
+ Gaps --> UnknownClass["unknownClassificationTasks\n任务分类不明确"]
177
+ ```
178
+
179
+ **动作队列格式**:每个 taskAction 包含 `taskId`、`path`、`files[]`、`commands[]`、`action` 描述。
180
+ `commands[]` 是可以直接执行的 harness CLI 命令列表。
181
+
182
+ ### Level 2 — migrate-verify 的两种模式
183
+
184
+ ```mermaid
185
+ flowchart TD
186
+ Verify["harness migrate-verify session.json"]
187
+
188
+ Verify --> Normal["普通模式\n重新运行检查\n报告 failures / warnings"]
189
+
190
+ Verify --> FullCutover["--full-cutover 模式\n最严格验证(8 个条件)"]
191
+
192
+ FullCutover --> C1{"session.result == complete?"}
193
+ C1 -->|"否"| F1["FAIL"]
194
+ C1 -->|"是"| C2{"无 strictDeferred?"}
195
+ C2 -->|"否"| F2["FAIL"]
196
+ C2 -->|"是"| C3{"所有计数器 == 0?\n(warnings / taskActions\nreviewSchemaGaps / legacyResiduals 等)"}
197
+ C3 -->|"否"| F3["FAIL(列出非零计数器)"]
198
+ C3 -->|"是"| C4{"fullCutoverEligible == true?"}
199
+ C4 -->|"否"| F4["FAIL"]
200
+ C4 -->|"是"| P1["PASS(完全切换就绪)"]
201
+ ```
202
+
203
+ `--full-cutover` 是迁移完成的最终验收标准:8 个条件全部满足才算通过。
204
+
205
+ ---
206
+
207
+ ## Part 3 — Capability 注册表
208
+
209
+ ### Level 1 — 能力依赖图
210
+
211
+ ```mermaid
212
+ flowchart TD
213
+ Core["core\n(必选,所有其他能力的基础)"]
214
+
215
+ Core --> ModParallel["module-parallel\n模块注册表 + 并行工作"]
216
+ Core --> AdvReview["adversarial-review\n对抗审查报告\n(别名:review-contract)"]
217
+ Core --> LongRunning["long-running-task\n长时间运行任务合约"]
218
+ Core --> Dashboard["dashboard\n本地 HTML Dashboard"]
219
+ Core --> SafeAdoption["safe-adoption\n旧项目平滑接入"]
220
+
221
+ ModParallel --> SubagentWorker["subagent-worker\ncommit-backed worker 交接协议"]
222
+ ```
223
+
224
+ ### Level 2 — 每个能力的 selectWhen
225
+
226
+ | 能力 | 何时启用 |
227
+ | --- | --- |
228
+ | `core` | 始终,这是必选基础 |
229
+ | `module-parallel` | 项目有 2 个以上独立模块需要并行所有权时 |
230
+ | `subagent-worker` | 代码变更 subagent 需要在独立 worktree 工作并 commit 交接时 |
231
+ | `adversarial-review` | 发布、架构、安全、数据或策略风险需要独立审查产物时 |
232
+ | `long-running-task` | Agent 可能跨多个 loop 运行而无需每步用户确认时 |
233
+ | `dashboard` | 需要本地只读状态可视化时 |
234
+ | `safe-adoption` | 将 v1.0 接入已有 harness 项目而不重写历史时 |
235
+
236
+ ### Level 2 — Preset 资源声明(resources)
237
+
238
+ Preset 可以声明三类资源,这些资源会在任务创建时自动生成,并由检查器验证:
239
+
240
+ | 资源类型 | 含义 | 验证方式 |
241
+ | --- | --- | --- |
242
+ | `resources.references` | Preset 提供的参考文件 | 文件存在 + `references/INDEX.md` 中有索引 + `task_plan.md` 中有必需阅读声明 |
243
+ | `resources.artifacts` | Preset 生成的工件 | 文件存在 + `artifacts/INDEX.md` 中有索引 |
244
+ | `context.requiredReads` | 必需阅读的参考文件 ID | 在两个索引中都有记录 |
245
+
246
+ 这是一个**三层验证链**:文件存在 → 索引表中有记录 → task_plan.md 中有必需阅读声明。
247
+ 任何一层缺失都会产生 failure。
248
+
249
+ ---
250
+
251
+ ## Part 4 — 设计决策
252
+
253
+ ### 为什么 Preset 不是一开始就有的
254
+
255
+ Preset 系统是从"旧项目迁移太复杂"这个需求演进出来的。最初只有 `new-task --budget complex`,
256
+ 没有任何 preset 概念。当用户提出旧项目迁移需求时,最初的方案是发明一个新的 "Ultra" 任务等级。
257
+
258
+ 研究后发现问题不是 Complex 不够用,而是"没有 preset 时每个 Agent 都要自己想迁移流程该怎么拆"。
259
+ 三个独立 subagent 的对抗审查都指向同一结论——preset 是正确抽象,Ultra 是过度设计。
260
+
261
+ 否决 Ultra 的理由:
262
+ 1. Ultra 会引入第二套任务系统,破坏 simple/standard/complex 的一致性
263
+ 2. 问题的根因不是 Complex 承载不了,而是没有预填骨架
264
+ 3. Preset 是通用抽象,不只服务迁移——任何类型的任务都可以有自己的 Preset
265
+
266
+ ### 为什么 Preset manifest 用 YAML
267
+
268
+ 选择 YAML 是因为可读性——preset manifest 需要人工编写和审查,YAML 比 JSON 少引号和括号。
269
+ 选择自写轻量解析器(`parseSimpleYaml()`)而不是引入 `js-yaml` 是为了保持零依赖。
270
+ JS 格式被排除是因为 preset 需要跨工具可审计,不能是可执行代码。
271
+
272
+ ### 为什么分层发现(project/user/builtin)
273
+
274
+ 分层发现比 preset 系统本身晚了两天引入。最初只从 package 的 `presets/` 固定路径读取。
275
+ 分层设计解决的核心问题是:
276
+ - 不同项目可以有自己的私有 preset(project 层)
277
+ - 用户可以安装跨项目共享的 preset(user 层)
278
+ - Package bundled preset 作为兜底,不需要安装就能用(builtin 层)
279
+
280
+ 优先级顺序(project > user > builtin)确保项目级定制能覆盖全局默认。
281
+
282
+ ### 为什么迁移分三个阶段而不是一步到位
283
+
284
+ 核心考量是"不能自动迁移"——preset 只搭骨架,不自动改历史文档,
285
+ 不自动 stage 或 commit。真正写入目标仓库前,仍然要先让用户确认写入范围和迁移深度。
286
+
287
+ - `migrate-plan`:只读分析,无副作用
288
+ - `migrate-run`:有写入的执行,需要用户确认
289
+ - `migrate-verify`:事后验证,确认迁移结果
290
+
291
+ 三步分离让用户在每个有副作用的节点都有确认机会。
292
+
293
+ ### 为什么 full-cutover 验证这么严格
294
+
295
+ `full-cutover` 是不可逆的声明——一旦宣称完成迁移,后续 Agent 就不会再把这个项目当成迁移目标处理。
296
+ 更宽松的方案(只检查 strict pass)被否决,因为真实项目验证(471 个任务)证明了
297
+ 即使 strict pass,仍然可能有 weak brief 或 legacy-only visual map 残留。
298
+
299
+ ### writeScopes 安全边界
300
+
301
+ writeScopes 和 preset 系统同时引入,不是后来补的安全加固。
302
+ Preset 是第三方可安装的包,如果不限制写入范围,恶意或错误的 preset 可以覆盖任意文件。
303
+ 运行时强制检查路径,拒绝 `../` 开头的路径和绝对路径(path traversal 防护)。
@@ -0,0 +1,67 @@
1
+ # Coding Agent Harness — 架构解释文档
2
+
3
+ 这组文档帮助你理解 `coding-agent-harness` 的系统架构。
4
+ 无论你是想贡献代码、集成到自己的项目、还是只是想搞清楚"这东西到底怎么工作的",
5
+ 这里都是最好的起点。
6
+
7
+ 每篇文档采用**自顶向下、逐层展开**的方式:先给你一张大图建立整体感,
8
+ 再一个模块一个模块地深入。你可以在任意层级停下来——不需要读完所有细节。
9
+
10
+ ---
11
+
12
+ ## 为什么要有这套文档
13
+
14
+ `coding-agent-harness` 的代码库本身并不复杂,但它的**设计意图**不容易从代码里直接读出来。
15
+
16
+ 比如:
17
+ - 为什么状态存在 Markdown 文件里,而不是数据库?
18
+ - 为什么有三种 check profile,而不是一种?
19
+ - `governance-sync` 和 `governance rebuild` 有什么区别,为什么要分开?
20
+ - `review-confirm` 为什么必须是人工操作,不能自动化?
21
+
22
+ 这些问题的答案散落在设计决策、历史演进和操作规范里。
23
+ 这套文档把它们集中起来,让你不需要翻 git log 就能理解系统的"为什么"。
24
+
25
+ ---
26
+
27
+ ## 阅读顺序
28
+
29
+ 按顺序读效果最好,每篇 15-25 分钟:
30
+
31
+ | 文件 | 主题 | 你会理解什么 |
32
+ | --- | --- | --- |
33
+ | [01-system-overview.md](01-system-overview.md) | 系统全景 | 这个东西是什么,解决什么问题,四个大块分别做什么 |
34
+ | [02-module-dependency.md](02-module-dependency.md) | 代码模块 | CLI 怎么分发,lib/ 里 30+ 个模块怎么分层,依赖关系 |
35
+ | [03-task-lifecycle.md](03-task-lifecycle.md) | 任务生命周期 | 一个任务从创建到收口的完整流转、门禁和队列系统 |
36
+ | [04-check-and-governance.md](04-check-and-governance.md) | 检查体系 | 三种 profile,9 个验证器各验什么,治理索引如何重建 |
37
+ | [05-data-flow.md](05-data-flow.md) | 数据流 | Markdown 文件如何变成 Dashboard,两种生成模式的边界 |
38
+ | [06-preset-and-migration.md](06-preset-and-migration.md) | Preset 与迁移 | Preset 包结构和 entrypoint 类型系统,旧项目迁移三阶段 |
39
+
40
+ ---
41
+
42
+ ## 文档约定
43
+
44
+ 每个文件用 `Level 0 / 1 / 2 / 3` 标注层级深度:
45
+
46
+ - **Level 0**:最高层,3-5 个大块,建立整体感(必读)
47
+ - **Level 1**:展开大块,看清子模块(推荐读)
48
+ - **Level 2**:深入子模块,理解内部逻辑(按需读)
49
+ - **Level 3**:最细节,函数级别的流程(查阅用)
50
+
51
+ 可以只读到 Level 1 就停,有需要再往下看。
52
+
53
+ ---
54
+
55
+ ## 快速定位
56
+
57
+ 如果你有具体问题,直接跳到对应文件:
58
+
59
+ | 我想知道… | 去哪里 |
60
+ | --- | --- |
61
+ | 这个系统解决什么问题 | [01 — 系统全景](01-system-overview.md) |
62
+ | `harness check` 在验什么 | [04 — 检查体系](04-check-and-governance.md) |
63
+ | 任务的 `review` 状态是什么意思 | [03 — 任务生命周期](03-task-lifecycle.md) |
64
+ | Dashboard 数据从哪来 | [05 — 数据流](05-data-flow.md) |
65
+ | Preset 怎么写 | [06 — Preset 与迁移](06-preset-and-migration.md) |
66
+ | 代码里某个模块是干什么的 | [02 — 代码模块](02-module-dependency.md) |
67
+ | 旧项目怎么迁移进来 | [06 — Preset 与迁移](06-preset-and-migration.md) |