Compare commits

..
44 Commits
Author SHA1 Message Date
QiuSW 947a06846e docs: add DevHarness upgrade baseline guidance (#16) 2026-08-16 19:51:20 +08:00
QiuSW fe99ca52e3 docs: add project baseline examples (#15) 2026-08-16 19:39:08 +08:00
QiuSW a959fc385d docs: evaluate open-source project baselines (#15) 2026-08-16 19:30:22 +08:00
QiuSW d1f55f9df3 feat: export task archives on demand (#14) 2026-08-16 19:21:36 +08:00
QiuSW f23c2cf81f docs: record task #13 acceptance 2026-08-11 10:24:51 +08:00
QiuSW fb9229a74e docs: archive task #13 2026-08-10 19:08:33 +08:00
QiuSW 4e1db4558c docs: support multi-project delivery units (#13) 2026-08-10 19:05:07 +08:00
QiuSW 105328ee2e docs: record task #12 acceptance 2026-08-10 18:01:12 +08:00
QiuSW eba851561a docs: archive task #12 2026-08-10 15:04:43 +08:00
QiuSW 0271a3ebd7 docs: 新增已有项目接入指南 (#12) 2026-08-10 15:01:15 +08:00
QiuSW eb92fb3033 docs: 归档任务 #11 2026-08-10 14:29:06 +08:00
QiuSW 3474870499 docs: 建立最小交付文档体系 (#11) 2026-08-10 14:25:34 +08:00
QiuSW 4e9f2e1380 docs: 记录任务 #8 #9 #10 验收完成 2026-08-10 12:01:19 +08:00
QiuSW e9c9fb415b docs: 归档任务 #10 2026-08-10 11:56:39 +08:00
QiuSW 353bd97212 feat: 增加需求记录规则 (#10) 2026-08-10 11:53:38 +08:00
QiuSW 48404070d0 docs: 归档任务 #9 2026-08-10 01:13:24 +08:00
QiuSW 10e82d61e6 feat: 定义 Agent 快捷指令 (#9) 2026-08-10 01:09:13 +08:00
QiuSW f652cbd46c docs: 归档任务 #8 2026-08-10 00:36:37 +08:00
QiuSW a59e3b5374 feat: 增加最小工单依赖规则 (#8) 2026-08-10 00:30:31 +08:00
QiuSW d1136c2bc5 docs: 记录任务 #5 #6 #7 验收完成 2026-08-08 11:27:19 +08:00
QiuSW 98df3a2dc5 docs: 归档任务 #7 2026-08-08 11:21:08 +08:00
QiuSW 3bfe752a92 docs: 增加 Claude Code 模型路由 (#7) 2026-08-08 11:18:06 +08:00
QiuSW c6f353cbbb docs: 归档任务 #6 2026-08-08 10:18:09 +08:00
QiuSW 2adb30ada2 refactor: 优化 Claude Code 规则入口 (#6) 2026-08-08 10:15:24 +08:00
QiuSW 2509d9ec2e docs: 归档任务 #5 2026-08-08 10:06:12 +08:00
QiuSW c9f1bc8832 refactor: 精简 AGENTS 重复说明 (#5) 2026-08-08 10:03:38 +08:00
QiuSW 874ca626b3 docs: 记录任务 #4 验收完成 2026-08-08 10:00:15 +08:00
QiuSW e29b9e4391 docs: 归档任务 #4 2026-08-08 09:40:02 +08:00
QiuSW d3df733fb0 docs: 增加 Agent 效率与范围控制 (#4) 2026-08-08 09:38:12 +08:00
QiuSW e72b161fa8 docs: 记录任务 #3 验收完成 2026-08-08 09:25:04 +08:00
QiuSW 1d3104c002 docs: 归档任务 #3 2026-08-08 09:21:38 +08:00
QiuSW 51278fa47f docs: 修正 dev_scripts 编译检查命令 (#3) 2026-08-08 09:19:45 +08:00
QiuSW 1c998673f3 refactor: 将 Harness 工具移至 dev_scripts (#3) 2026-08-08 09:18:09 +08:00
QiuSW c69c0fde03 docs: 记录任务 #1 #2 验收完成 2026-08-08 09:11:57 +08:00
QiuSW 5c432da6df docs: 归档任务 #2 2026-08-08 09:04:40 +08:00
QiuSW c8ccb62a98 test: 覆盖核心文档映射错误 (#2) 2026-08-08 09:02:48 +08:00
QiuSW b09658ee15 docs: 修正文档命令与初始化边界 (#2) 2026-08-08 09:01:56 +08:00
QiuSW 250b0555e4 feat: 建立初级维护者文档体系 (#2) 2026-08-08 09:00:44 +08:00
QiuSW 3aa73fcb1e docs: 更新任务 #1 归档证据 2026-08-08 00:02:21 +08:00
QiuSW fec8e91f6d docs: 归档任务 #1 2026-08-08 00:00:57 +08:00
QiuSW 1e3915ba59 fix: 支持中文 Wiki 页面路径 (#1) 2026-08-08 00:00:57 +08:00
QiuSW 8c52ea27ad test: 覆盖 Wiki 镜像脏文件保护 (#1) 2026-08-07 23:58:34 +08:00
QiuSW f1a7b09c82 docs: 同步 Wiki 文档镜像 (#1) 2026-08-07 23:57:48 +08:00
QiuSW b08a919fd7 feat: 引入 Wiki 文档镜像流程 (#1) 2026-08-07 23:57:13 +08:00
40 changed files with 4243 additions and 243 deletions
+51
View File
@@ -5,6 +5,29 @@
- 所属 MVP / 版本:# - 所属 MVP / 版本:#
- 阶段: - 阶段:
## 依赖与并行
- 前置工单:无 / #编号
- 是否允许与前置工单并行:是 / 否
- 原因:
## 子项目影响
<!-- 单应用项目填写唯一交付单元;多应用单仓库必须明确单端、跨端和共享契约影响。 -->
- 仅影响的子项目 / 交付单元:
- 是否跨子项目:是 / 否
- 是否修改共享接口或契约:是 / 否;唯一事实来源:
- 各子项目需要执行的验证:
## 原始需求
- 来源:用户对话 / Gitea / 其他
- 提出时间:
- 关键原话或脱敏摘要:
<!-- 只保留表达用户目的、场景和限制所需的内容;不要复制完整聊天、内部推理或敏感信息。 -->
## 要解决什么 ## 要解决什么
<!-- 描述现状和目标。缺陷需要写清复现步骤、实际结果和期望结果。 --> <!-- 描述现状和目标。缺陷需要写清复现步骤、实际结果和期望结果。 -->
@@ -22,6 +45,34 @@
- <!-- 填写 --> - <!-- 填写 -->
## 需求变化记录
<!-- 只记录影响范围、接口、数据、风险或验收的变化;没有变化时填写“无”。 -->
| 日期 | 变化内容 | 原因 | 用户确认 |
|---|---|---|---|
| | | | 是 / 否 |
## 文档影响
<!-- 至少选择一项;不影响长期文档时必须写明原因。 -->
- [ ] 不影响长期文档,原因:
- [ ] 更新项目档案或本地开发与验证
- [ ] 更新架构与代码地图
- [ ] 更新业务规则与术语
- [ ] 更新常见修改或故障排查
- [ ] 更新其他 Wiki 页面:
## 交付文档影响
<!-- 至少选择一项;面向用户、客户或其他岗位的行为、配置、部署、接口或支持方式变化时,必须列出受众和页面。 -->
- [ ] 无交付文档影响,原因:
- [ ] 更新已有交付文档,受众与页面:
- [ ] 新增交付文档,受众与页面:
- [ ] 需要目标岗位或客户代表验证:是 / 否;验证方式:
## 验收标准 ## 验收标准
- [ ] <!-- 填写 --> - [ ] <!-- 填写 -->
+98 -27
View File
@@ -1,12 +1,12 @@
# Agent 开发规则 # Agent 开发规则
本仓库采用 DevHarness 工作流:Gitea 工单是实施期间的事实来源,Git 是代码变更记录,`docs/task` 是完成后的最终归档。人负责确认方案与验收,Agent 负责检查、实现、测试和留下证据。 本仓库采用 DevHarness 工作流:Gitea 工单是任务过程的事实来源,Gitea Wiki 是长期开发文档和任务归档的事实来源,Git 是代码与版本绑定资料的变更记录。`docs/` 默认保存核心 Wiki 的只读镜像,`docs/task/` 只保存人工按需导出的任务归档快照。人负责确认方案与验收,Agent 负责检查、实现、测试和留下证据。
开始工作前先阅读 [项目档案](docs/00-project-profile.md) 和任务涉及目录中的 `AGENTS.md`。目录越深的规则越具体,但不得削弱上级安全规则。 开始工作前先阅读 [项目档案](docs/00-project-profile.md) 和任务涉及目录中的 `AGENTS.md`。目录越深的规则越具体,但不得削弱上级安全规则。
## 1. 永久规则 ## 1. 永久规则
- 不把密码、令牌、Cookie、私钥、个人数据或生产数据写入代码、日志、工单和文档。 - 不把密码、令牌、Cookie、私钥、个人数据或生产数据写入代码、日志、工单、Wiki 和文档。
- 不执行未经用户明确授权的发布、付款、删除数据、破坏性迁移或其他不可逆操作。 - 不执行未经用户明确授权的发布、付款、删除数据、破坏性迁移或其他不可逆操作。
- 保留用户已有和任务无关的工作区改动,不擅自重置、覆盖或混入提交。 - 保留用户已有和任务无关的工作区改动,不擅自重置、覆盖或混入提交。
- 发现需求与安全规则、已确认方案或现有数据冲突时,先停止实施并说明影响。 - 发现需求与安全规则、已确认方案或现有数据冲突时,先停止实施并说明影响。
@@ -30,30 +30,82 @@
## 3. 需求到实施 ## 3. 需求到实施
1. 先复述目标,阅读相关代码、日志和文档,区分事实与假设。 1. 先复述目标,阅读相关代码、日志和文档,区分事实与假设。
2. 给出目标、非目标、方案、影响范围、风险、回退方式和验证方法。 2. 给出目标、非目标、方案、影响范围、风险、回退方式、验证方法和文档影响。
3. 方案没有得到用户明确确认前,只做只读诊断和方案整理,不实施正式代码。 3. 方案没有得到用户明确确认前,只做只读诊断和方案整理,不实施正式代码。
4. 方案确认后,先建立单元任务工单,再修改代码。 4. 方案确认后,先建立单元任务工单,再修改代码。
5. 开始实施前检查分支和工作区,明确哪些现有改动不属于本任务。 5. 建立新工单不要求其他工单已经完成;开始实施前检查工单声明的前置工单。真实依赖未满足时保持“待实施”,允许并行时必须写明原因。
6. 严格按工单范围实现;新发现的问题先记录,不顺手混入当前任务。 6. 开始实施前检查分支和工作区,明确哪些现有改动不属于本任务。
7. 执行与风险相称的测试,把关键结果和未验证部分更新到工单。 7. 严格按工单范围实现;新发现的问题先记录,不顺手混入当前任务。
8. 执行与风险相称的测试,把关键结果和未验证部分更新到工单。实施过程中出现计划外、当前无法解除的问题时才标记“阻塞”。
9. 长期核心文档必须先修改 Wiki、读取确认,再运行 `python dev_scripts/sync_wiki_docs.py` 导出本地镜像;任务归档默认只更新 Wiki,不自动导出本地。不得直接编辑镜像后反向覆盖 Wiki。
Gitea 不可用时,输出完整工单草稿并说明阻塞。未经用户明确授权,不得默认绕过建单。 Gitea 不可用时,输出完整工单草稿并说明阻塞。未经用户明确授权,不得默认绕过建单。
### 自然语言快捷指令
快捷指令只是本工作流的自然语言别名,不得绕过方案确认、前置依赖、安全规则、工单范围、Wiki 主源、必要验证或人工验收:
- `只分析`:只读检查并给出方案;不建单、不修改,停在等待确认。
- `建工单`:根据已确认方案创建单元任务工单;建单后停止,不修改代码。
- `执行工单 #N`:检查工单和依赖,实施、测试、提交、归档、推送并回写证据;停在“待验收”。
- `建工单并做`:依次建单和执行,`建工单,做`、`建工单,做` 含义相同;停在“待验收”。
- `继续工单 #N`:核对工单、Git 和 Wiki 证据,从首个未完成步骤继续,不重复仍然有效的检查。
- `检查工单 #N`:只读核对范围、验收、测试和证据并输出报告;不自动修复。
- `同步文档`:读取 Wiki、导出核心 `docs/` 并检查一致性,不处理任务归档;不修改 Wiki、不自动提交。
- `导出任务归档`:人工触发 `python dev_scripts/export_task_archives.py`,只导出新增或 revision 已变化的任务归档;不删除本地文件、不自动提交。
- `导出全部任务归档`:人工触发 `python dev_scripts/export_task_archives.py --all`,读取并导出全部线上任务归档;不删除本地文件、不自动提交。
- `#N 验收通过`:仅在用户明确验收后,更新 Wiki 归档、同步必要的核心文档、推送、同步父工单并关闭任务;不自动导出任务归档。
方案未确认或前置依赖未满足时,实施类指令必须停在对应门禁;除 `#N 验收通过` 外,快捷指令不得关闭待验收工单。任务归档导出必须由用户明确提出,其他指令不得隐式执行。Gitea 工单不导出全文,`docs/task/` 只是可能不完整的 Wiki 任务归档快照。详细语义见 [开发工作流](docs/01-workflow.md)。
### 需求记录与流转
- 创建单元任务工单时,记录原始需求的来源、提出时间,以及能表达用户目的、场景和限制的少量关键原话或脱敏摘要;不得臆造用户原话。
- 工单中的目标、非目标、已确认方案、验收标准和文档影响构成确认后的正式任务需求。
- 影响范围、接口、数据、风险或验收的需求变化必须记录日期、内容、原因和用户确认;会改变已确认结果时先更新工单并等待再次确认。
- 不复制完整聊天,不保存 Agent 内部推理,不写入密码、令牌、个人数据或生产数据;包含敏感信息的原话必须删除敏感部分或改写为脱敏摘要。
- 长期有效的产品需求、业务规则和系统边界进入对应 Wiki 主题页并导出核心 `docs/`;完成结果进入 Wiki 任务归档,仅在用户明确要求时导出 `docs/task/`。Gitea 工单全文不导出到仓库。
详细记录边界见 [开发工作流](docs/01-workflow.md) 与 [业务规则和术语](docs/03-business-rules-and-glossary.md)。
### 效率与范围控制
本节只用于减少无关工作和重复检查,不得削弱安全规则、已确认方案、工单范围、必要测试、Wiki 同步、Git 提交和验收归档要求。
#### 严格控制范围
- 默认严格按用户确认的目标和单元任务范围执行,不主动扩展相邻问题。
- 除非任务目标、仓库强制规则或已发现的真实阻塞需要,不新增额外文档、辅助脚本、备份文件、框架、重构或扩展性设计。
- 不执行与本次验收无关的验证;安全检查、受影响范围测试、回归测试和仓库规定的闭环验证不属于“额外验证”。
- 新发现的相邻问题最多用一句话提示或记录到独立工单,不自动修复或混入当前提交。
#### 渐进执行和修复
- 完成已知必要的安全与前置检查后,优先执行能够产生真实反馈的最小命令。
- 一次执行后先处理首个可定位、可行动的真实错误,不同时猜测并修改多个可能原因。
- 采用“执行 → 查看错误 → 最小修复 → 从失败点继续或按需重跑”的闭环。
- 不在真实证据出现前堆叠与已知风险无关的预防性检查。
- 涉及凭据、权限、安全、数据、迁移、并发、删除、发布或不可逆操作时,必须先完成相应前置检查,不得通过试错获取风险反馈。
#### 复用已验证事实
- 在同一任务和同一环境状态下,已经通过的路由、连接、恢复和环境检查不重复执行。
- 只有会话、环境、代码、配置、依赖、凭据、远端状态或关键前提发生变化时才重新检查。
- 代码修改后,受影响测试和最终验收必须重新执行;提交前工作区检查、推送前远端分支检查不得因为之前通过而省略。
- Skill 和平台规则是否需要重新读取,按当前 Agent 平台和任务触发规则执行,不自行跳过。
#### 明确停止条件
- 完成用户确认的验收标准和仓库规定的必要闭环后立即停止,不主动继续优化。
- “最小验收条件”包括当前工单要求的实现、必要测试、文档影响处理、Wiki 镜像检查、提交和证据回写,不等同于功能第一次运行成功。
- 未影响当前验收的相邻问题只提示或建单,不顺手处理。
## 4. 工单层级 ## 4. 工单层级
```text - 单元任务是唯一正式实施单位,记录方案、范围、验收标准、过程和测试结果。
Epic:完整产品目标和长期路线 - Epic 和 MVP 只维护目标、风险、汇总及 `- [ ] #编号` / `- [x] #编号` 子工单索引,不复制单元任务全文。
└── MVP:第一个可交付版本
└── 单元任务:唯一的实施单位
```
- Epic 维护总体目标、范围、路线、风险和所有任务索引。
- MVP 维护首个可交付范围、阶段、集成风险和任务清单。
- 单元任务记录具体方案、修改范围、验收标准、过程和测试结果。
- 父工单只维护 `- [ ] #编号` 或 `- [x] #编号` 的索引和汇总,不复制子工单全文。
- 新任务先建单元工单,再把编号同步到所属 MVP 和 Epic。 - 新任务先建单元工单,再把编号同步到所属 MVP 和 Epic。
- 详细层级、状态和模板入口见 [开发工作流](docs/01-workflow.md) 与 [业务规则和术语](docs/03-business-rules-and-glossary.md)。
工单模板位于 `.gitea/issue_template/`。
## 5. 实施中的变化 ## 5. 实施中的变化
@@ -61,6 +113,7 @@ Epic:完整产品目标和长期路线
- 变化影响 MVP 或 Epic 时,同时更新父工单。 - 变化影响 MVP 或 Epic 时,同时更新父工单。
- 会改变用户已确认结果的变化,更新工单后必须再次等待用户确认。 - 会改变用户已确认结果的变化,更新工单后必须再次等待用户确认。
- 阻塞、失败方案和新发现根因不能只留在聊天或代码注释中。 - 阻塞、失败方案和新发现根因不能只留在聊天或代码注释中。
- Wiki 页面删除、重命名或事实源边界变化时,必须先更新工单并等待用户确认;同步工具不得自动传播删除或重命名。
- 工单状态应使用:待确认、待实施、进行中、阻塞、待验收、已完成。 - 工单状态应使用:待确认、待实施、进行中、阻塞、待验收、已完成。
## 6. Git 与验证 ## 6. Git 与验证
@@ -73,25 +126,38 @@ Epic:完整产品目标和长期路线
## 7. 完成、验收和归档 ## 7. 完成、验收和归档
1. 实现完成后逐项检查验收标准,并提交代码。 1. 逐项完成验收、测试和实现提交,并把最终方案、差异、结果、提交及遗留问题写回工单。
2. 更新单元工单:最终方案、方案差异、测试结果、提交哈希和遗留问题。 2. 工单保持“待验收”,用户没有明确验收通过前不得关闭。
3. 工单保持“待验收”,用户没有明确验收通过前不得关闭。 3. 运行 `python dev_scripts/new_task_archive.py <编号> "<短标题>"`,只创建 Wiki 任务归档,不登记或导出本地镜像。
4. 按 `docs/templates/task-archive.md` 创建 `docs/task/<编号>-<短标题>.md`。 4. 读取确认 Wiki,运行 `python dev_scripts/sync_wiki_docs.py --check` 检查核心镜像,并把任务归档页面、revision 和提交哈希写回工单。只有用户明确提出时才增量或全量导出任务归档。
5. 归档文档单独提交,例如:`docs: 归档任务 #123`。 5. 用户验收通过后关闭单元工单,并同步更新 MVP 和 Epic。
6. 把归档路径和提交哈希回写工单。
7. 用户验收通过后关闭单元工单,并同步更新 MVP 和 Epic。
MVP 内所有单元任务通过后才能做 MVP 集成验收;MVP 通过后才能关闭 MVP。Epic 的全部范围完成后才能关闭 Epic。 MVP 内所有单元任务通过后才能做 MVP 集成验收;MVP 通过后才能关闭 MVP。Epic 的全部范围完成后才能关闭 Epic。
详细归档顺序和字段见 [开发工作流](docs/01-workflow.md)。
## 8. 可维护性 ## 8. 可维护性
- 优先使用直白、常见的实现;不要为了少写几行引入晦涩技巧。 - 优先使用直白、常见的实现;不要为了少写几行引入晦涩技巧。
- 类和函数保持单一职责,名称表达业务含义。 - 类和函数保持单一职责,名称表达业务含义。
- 注释解释原因、边界和风险,不逐行翻译代码。 - 注释解释原因、边界和风险,不逐行翻译代码。
- 错误必须可定位,不静默吞掉失败。 - 错误必须可定位,不静默吞掉失败。
- 文档先写结论和用途,再写步骤;示例命令应可直接复制。 - Wiki 文档先写结论和用途,再写步骤;示例命令应可直接复制,本地 `docs/` 由同步工具生成。
- 面向初级维护者说明从哪里开始读、怎样运行和怎样验证。 - 面向初级维护者说明从哪里开始读、怎样运行和怎样验证。
### 修改风险
- 低风险修改可由初级程序员在 Agent 协助下处理;接口、配置、依赖、跨模块逻辑和数据结构由 Agent 实现并验证。
- 权限、安全、并发、迁移、支付、删除数据和不可逆操作属于高风险;高风险修改必须停止,由 Agent 分析并等待人工确认。
- 风险按影响范围判断,不按代码行数判断;示例见 [常见修改指南](docs/05-common-changes.md)。
### 文档影响
- 每个单元任务必须在工单中选择“无长期文档影响并说明原因”或列出需要更新的 Wiki 页面。
- 启动、测试、部署、排错命令,模块入口、目录职责、主要调用路径,配置、API、数据结构、状态、业务规则、安全边界、日志位置发生变化时,必须更新对应 Wiki。
- 普通内部重构只有在入口、行为、配置和验证方式均未改变时,才可以记录为不影响长期文档。
- 必需核心页面及结构以 `python dev_scripts/check_harness.py --strict` 和 [新项目文档初始化](docs/07-new-project-documentation-setup.md) 为准;稳定文档与任务归档的分工见 [开发工作流](docs/01-workflow.md)。
## 9. 引导提交例外 ## 9. 引导提交例外
从本模板创建全新仓库时,Gitea 远端和工单尚不存在,允许一次不带工单号的初始引导提交。该提交只能包含仓库骨架、Harness 规则和远端配置准备,不能包含产品功能。 从本模板创建全新仓库时,Gitea 远端和工单尚不存在,允许一次不带工单号的初始引导提交。该提交只能包含仓库骨架、Harness 规则和远端配置准备,不能包含产品功能。
@@ -102,4 +168,9 @@ MVP 内所有单元任务通过后才能做 MVP 集成验收;MVP 通过后才
<!-- 在项目初始化时填写不可违反的技术、安全和业务约束。复杂子项目请在其目录中增加 AGENTS.md。 --> <!-- 在项目初始化时填写不可违反的技术、安全和业务约束。复杂子项目请在其目录中增加 AGENTS.md。 -->
- 尚未配置。开始产品开发前必须填写项目档案,并删除本行。 - 长期开发文档以 Gitea Wiki 为事实来源,`docs/` 默认保存显式映射生成的核心只读镜像;`docs/task/` 是人工按需快照,可能不完整或不是最新状态。
- 核心 Wiki 与镜像的固定顺序是:修改 Wiki → 读取确认 → 导出核心 `docs` → 校验差异 → 提交镜像。
- 任务归档默认只保存在 Wiki;`导出任务归档` 或 `导出全部任务归档` 必须由用户明确提出,且不得自动传播删除或重命名。
- 同步配置只允许写入 `docs/` 下的 Markdown;发现镜像有未提交修改时必须停止。
- Gitea 凭据只通过进程环境或 MCP 安全配置提供,不得写入仓库。
- `dev_scripts/` 只存放 DevHarness 自身工具;业务项目的通用脚本必须使用独立目录,不得混放。
+24 -13
View File
@@ -1,20 +1,31 @@
# Claude Code 项目入口 # Claude Code 项目入口
本仓库的完整开发规则以 [AGENTS.md](AGENTS.md) 为唯一事实来源。 @AGENTS.md
Claude Code 开始任何工作前,必须按顺序阅读: ## Claude Code 专用说明
1. 根目录的 `AGENTS.md`; - 上方导入的 `AGENTS.md` 是所有编码 Agent 的共同规则事实来源,Claude Code 必须完整遵守。
2. `docs/00-project-profile.md`; - 本文件只记录 Claude Code 特有的模型路由、工具和 Agent 协作规则。
3. 任务涉及目录中更具体的 `AGENTS.md`; - 共同规则变化时只修改 `AGENTS.md`,不要在本文件重复维护。
4. 当前 Gitea 工单及其父级 MVP、Epic 工单。
必须遵守以下入口规则: ## 模型路由
- 方案经用户确认并建立单元任务工单后,才能修改产品代码。 - 目标、范围和修改位置已经明确时,默认使用 Sonnet 分析、建单和实施。
- 只修改当前工单范围内的文件,保留用户已有和无关的改动。 - 需求模糊、根因不明,或涉及跨模块架构、安全、权限、并发、迁移和不可逆操作时,使用 Opus 或 `opusplan` 制定方案。
- 实现、测试、Git 提交、待验收、任务归档和关闭工单的顺序不得跳过。 - Opus 输出方案后必须等待用户确认;确认后由 Sonnet 根据方案创建工单并实施。
- 未经用户明确验收,不得关闭工单。 - Haiku 只用于范围明确的只读任务,例如查找代码入口、读取项目文档、提取日志事实和整理调用关系。
- 不得把密码、令牌、Cookie、私钥或生产数据写入代码、日志、工单和文档。 - 不让 Haiku 决定最终根因、技术方案、风险等级或验收结论。
- 当前模型足以完成任务时不升级模型,也不为了形式固定依次调用三个模型。
不要在本文件复制完整工作流。流程发生变化时只更新 `AGENTS.md`,本文件保持为 Claude Code 的稳定入口,防止两套规则不一致。 ## Agent 交接
- Opus 向 Sonnet 交接目标、非目标、事实、假设、方案、修改范围、风险、回退方式和验收标准。
- Haiku 向主 Agent 交接结论、证据位置和仍不确定的内容,不返回与任务无关的大段原文。
- Sonnet 严格按照已确认方案和工单实施;发现范围变化时返回主流程重新确认。
- 不同 Agent 复用仍然有效的检查结果;会话、环境或关键前提变化时才重新检查。
## Haiku 只读约束
- Haiku 子 Agent 只允许使用读取、搜索和只读代码图谱工具。
- 禁止 Haiku 修改文件、创建或更新工单、提交 Git、更新 Wiki 或执行具有副作用的命令。
- 只读必须通过子 Agent 工具权限实现,不能只依赖提示语;未配置只读权限时不得调用 Haiku 处理项目内容。
+19 -14
View File
@@ -1,6 +1,6 @@
# DevHarness # DevHarness
DevHarness 是一个以 Gitea 工单为事实来源、以 Git 提交为变更记录、由人负责确认和验收的 AI 辅助开发模板。 DevHarness 是一个以 Gitea 工单管理任务过程、以 Gitea Wiki 管理长期文档、以 Git 提交记录代码变更、由人负责确认和验收的 AI 辅助开发模板。
它约束的是开发过程,不限制项目使用 Python、Go、JavaScript 或其他技术栈。 它约束的是开发过程,不限制项目使用 Python、Go、JavaScript 或其他技术栈。
@@ -14,21 +14,22 @@ DevHarness 是一个以 Gitea 工单为事实来源、以 Git 提交为变更记
-> Agent 实现并测试 -> Agent 实现并测试
-> 提交代码并更新工单 -> 提交代码并更新工单
-> 人工验收 -> 人工验收
-> 归档 docs/task -> 归档 Wiki;任务快照仅在人工提出时导出
-> 关闭工单并更新父工单 -> 关闭工单并更新父工单
``` ```
## 快速开始 ## 快速开始
1. 复制或克隆本仓库,并修改仓库名称。 1. 复制或克隆本仓库,并修改仓库名称。
2. 填写 [项目档案](docs/00-project-profile.md),特别是 Gitea 地址、仓库名和验证命令。 2. 按 [新项目文档初始化](docs/07-new-project-documentation-setup.md) 在 Gitea Wiki 填写项目档案和核心主题页。
3. 把项目不可违反的安全规则写入根目录或子项目的 `AGENTS.md`。 3. 把项目不可违反的安全规则写入根目录或子项目的 `AGENTS.md`。
4. 创建 Gitea 远端仓库并推送当前引导提交。 4. 创建 Gitea 远端仓库并推送当前引导提交。
5. 使用 `.gitea/issue_template/` 中的模板创建第一个 Epic、MVP 和单元任务。 5. 配置 `wiki-docs.json` 的页面映射;读取 Wiki 可匿名访问或使用 `GITEA_TOKEN`,写 Wiki 必须通过环境变量提供令牌。
6. 开始产品代码前运行: 6. 使用 `.gitea/issue_template/` 中的模板创建第一个 Epic、MVP 和单元任务。
7. 开始产品代码前运行:
```powershell ```powershell
python scripts/check_harness.py --strict python dev_scripts/check_harness.py --strict
``` ```
新仓库在 Gitea 尚未建立前允许一次不关联工单的引导提交。远端和工单系统配置完成后,所有改变程序行为的工作都必须先有单元任务工单。 新仓库在 Gitea 尚未建立前允许一次不关联工单的引导提交。远端和工单系统配置完成后,所有改变程序行为的工作都必须先有单元任务工单。
@@ -39,18 +40,22 @@ DevHarness 是一个以 Gitea 工单为事实来源、以 Git 提交为变更记
AGENTS.md Agent 的通用工作规则 AGENTS.md Agent 的通用工作规则
CLAUDE.md Claude Code 的规则入口 CLAUDE.md Claude Code 的规则入口
.gitea/issue_template/ Epic、MVP、单元任务工单模板 .gitea/issue_template/ Epic、MVP、单元任务工单模板
docs/00-project-profile.md 每个项目需要填写的档案 docs/00-project-profile.md Wiki 项目档案的只读镜像
docs/01-workflow.md 人和 Agent 都能阅读的流程说明 docs/01-workflow.md Wiki 开发工作流的只读镜像
docs/templates/task-archive.md 完成后的本地归档模板 docs/02-07*.md 代码地图、业务、验证、修改、排错和初始化镜像
docs/task/ 已完成任务的最终记录 docs/templates/task-archive.md Wiki 任务归档模板的只读镜像
scripts/check_harness.py 模板和归档的最小自检 docs/task/ 人工按需导出的 Wiki 任务归档快照
scripts/new_task_archive.py 创建任务归档文件 wiki-docs.json 核心 Wiki 页面到本地镜像的显式映射
dev_scripts/check_harness.py 模板和归档的最小自检
dev_scripts/sync_wiki_docs.py 单向导出或检查 Wiki 镜像
dev_scripts/new_task_archive.py 只创建 Wiki 任务归档
dev_scripts/export_task_archives.py 人工增量或全量导出任务归档
``` ```
## 设计原则 ## 设计原则
- 人决定目标、范围和验收结果,Agent 负责检查、实现和验证。 - 人决定目标、范围和验收结果,Agent 负责检查、实现和验证。
- 工单记录实施过程,`docs/task` 只保存完成后的最终事实。 - 工单记录实施过程,Wiki 保存长期有效的最终事实,`docs/` 默认保存核心镜像;任务归档快照按需导出。
- 一个单元工单只解决一个可独立测试和回退的问题。 - 一个单元工单只解决一个可独立测试和回退的问题。
- 实现提交与归档提交分开,便于审查与追溯。 - 实现提交与必要的核心文档镜像提交分开,便于审查与追溯。
- 凭据、个人数据和生产数据不得进入代码、工单或归档。 - 凭据、个人数据和生产数据不得进入代码、工单或归档。
+403
View File
@@ -0,0 +1,403 @@
"""检查 DevHarness 必需文件、核心文档和任务归档的基本结构。"""
from __future__ import annotations
import argparse
import re
from pathlib import Path
from wiki_docs import WikiDocsError, load_config, parse_mirror
ROOT = Path(__file__).resolve().parents[1]
CORE_PAGE_PATHS = {
"Home": "docs/README.md",
"Project-Profile": "docs/00-project-profile.md",
"Development-Workflow": "docs/01-workflow.md",
"Architecture-and-Code-Map": "docs/02-architecture-and-code-map.md",
"Business-Rules-and-Glossary": "docs/03-business-rules-and-glossary.md",
"Local-Development-and-Verification": (
"docs/04-local-development-and-verification.md"
),
"Common-Changes": "docs/05-common-changes.md",
"Troubleshooting": "docs/06-troubleshooting.md",
"New-Project-Documentation-Setup": (
"docs/07-new-project-documentation-setup.md"
),
"Existing-Project-Adoption-Guide": (
"docs/08-existing-project-adoption.md"
),
"Delivery-Documentation-Guide": "docs/delivery/README.md",
"Audience-Document-Template": (
"docs/delivery/audience-document-template.md"
),
"Task-Archive-Template": "docs/templates/task-archive.md",
}
CORE_DOCUMENT_REQUIREMENTS = {
"docs/README.md": (
"## 第一次阅读",
"## 五分钟开始",
"## 简单修改从哪里开始",
"## 事实来源",
),
"docs/00-project-profile.md": (
"## 基本信息",
"## DevHarness 来源与基线",
"## 子项目与交付单元",
"## 技术栈与运行环境",
"## 阅读入口",
"## 常用命令",
"## 环境、配置与凭据",
),
"docs/01-workflow.md": (
"## 面向初级维护者的修改边界",
"## 每个任务的文档影响",
"## 需求记录与流转",
"## 稳定文档与任务归档",
"## 自然语言快捷指令",
"## 效率与范围控制",
"### 严格控制范围",
"### 渐进执行和修复",
"### 复用已验证事实",
"### 明确停止条件",
),
"docs/02-architecture-and-code-map.md": (
"## 项目定位",
"## 代码地图",
"## 两条主要执行路径",
"## 不可破坏的边界",
),
"docs/03-business-rules-and-glossary.md": (
"## 核心术语",
"## 工单状态",
"## 稳定业务规则",
"## 新项目需要补充什么",
),
"docs/04-local-development-and-verification.md": (
"## 环境要求",
"## 第一次运行",
"## 常用调试方式",
"## 完成修改前",
),
"docs/05-common-changes.md": (
"## 风险分级",
"## 修改 Wiki 文案",
"## 调整 Harness 检查",
"## 看懂 Agent 的修改",
),
"docs/06-troubleshooting.md": (
"## 排查顺序",
"## 必须停止的情况",
),
"docs/07-new-project-documentation-setup.md": (
"## 初始化顺序",
"### 2. 选择建设基线",
"#### 判断案例",
"### 3. 识别子项目与交付单元",
"### 7. 确定交付对象和文档",
"## 完成标准",
),
"docs/08-existing-project-adoption.md": (
"## 与新项目初始化的区别",
"## 接入前只读盘点",
"## 已有内容保护原则",
"## 多应用单仓库判断",
"### 适合继续单仓库",
"### 可以考虑拆仓",
"### 保持单仓库时的最小规则",
"## 增量接入顺序",
"## 后续升级",
"### 升级步骤",
"### 可复制升级指令",
"## 冲突处理和停止条件",
"## 可复制 Agent 指令",
"### 只分析",
"### 方案确认后实施",
"## 最小验收清单",
"## 回退原则",
),
"docs/delivery/README.md": (
"## 什么时候需要交付文档",
"## 受众与文档选择",
"## 内部文档与交付文档边界",
"## 编写和维护流程",
"## 最小验收清单",
),
"docs/delivery/audience-document-template.md": (
"## 文档信息",
"## 目的与适用范围",
"## 前置条件",
"## 操作步骤",
"## 常见错误与恢复",
"## 安全与权限",
"## 已知限制",
"## 支持与升级处理",
"## 版本记录",
"## 交付前检查",
),
}
REQUIRED_FILES = (
"AGENTS.md",
"CLAUDE.md",
"README.md",
"docs/00-project-profile.md",
"docs/01-workflow.md",
"docs/templates/task-archive.md",
*CORE_DOCUMENT_REQUIREMENTS,
"wiki-docs.json",
"dev_scripts/wiki_docs.py",
"dev_scripts/sync_wiki_docs.py",
"dev_scripts/export_task_archives.py",
".gitea/issue_template/epic.md",
".gitea/issue_template/mvp.md",
".gitea/issue_template/task.md",
)
ARCHIVE_HEADINGS = (
"## 背景与目标",
"## 最终方案",
"## 修改文件",
"## 验收结果",
"## 测试",
"## 相关提交",
)
def check_required_files(errors: list[str]) -> None:
for relative_path in REQUIRED_FILES:
if not (ROOT / relative_path).is_file():
errors.append(f"缺少必需文件:{relative_path}")
def check_project_profile(errors: list[str], warnings: list[str], strict: bool) -> None:
profile = ROOT / "docs" / "00-project-profile.md"
if not profile.is_file():
return
if "<填写" in profile.read_text(encoding="utf-8"):
message = "项目档案仍有未填写内容"
(errors if strict else warnings).append(message)
def check_archives(errors: list[str]) -> None:
task_dir = ROOT / "docs" / "task"
for path in task_dir.glob("*.md"):
if not re.match(r"^\d+-.+\.md$", path.name):
errors.append(f"归档文件名不符合 <编号>-<标题>.md:{path.name}")
content = path.read_text(encoding="utf-8")
try:
metadata, _ = parse_mirror(content)
except WikiDocsError as exc:
errors.append(f"{path.name} 的任务镜像无效:{exc}")
continue
if re.fullmatch(r"Task-\d+-.+", metadata.get("wiki_page", "")) is None:
errors.append(f"{path.name} 的 wiki_page 不是任务归档页面")
if re.fullmatch(r"[0-9a-f]{40,64}", metadata.get("wiki_revision", "")) is None:
errors.append(f"{path.name} 的 wiki_revision 无效")
for heading in ARCHIVE_HEADINGS:
if heading not in content:
errors.append(f"{path.name} 缺少章节:{heading}")
if "**未验证部分**:" not in content:
errors.append(f"{path.name} 没有记录未验证部分")
def missing_sections(content: str, required: tuple[str, ...]) -> list[str]:
return [section for section in required if section not in content]
def check_core_documents(errors: list[str], root: Path = ROOT) -> None:
"""检查初级维护者所需主题页的固定结构。"""
for relative_path, required in CORE_DOCUMENT_REQUIREMENTS.items():
path = root / relative_path
if not path.is_file():
continue
try:
_, body = parse_mirror(path.read_text(encoding="utf-8"))
except (OSError, UnicodeDecodeError, WikiDocsError):
continue
for section in missing_sections(body, required):
errors.append(f"{relative_path} 缺少核心章节:{section}")
def check_task_template(errors: list[str], root: Path = ROOT) -> None:
path = root / ".gitea" / "issue_template" / "task.md"
if not path.is_file():
return
content = path.read_text(encoding="utf-8")
required = (
"## 依赖与并行",
"- 前置工单:无 / #编号",
"- 是否允许与前置工单并行:是 / 否",
"- 原因:",
"## 子项目影响",
"- 仅影响的子项目 / 交付单元:",
"- 是否跨子项目:是 / 否",
"- 是否修改共享接口或契约:是 / 否;唯一事实来源:",
"- 各子项目需要执行的验证:",
"## 原始需求",
"- 来源:用户对话 / Gitea / 其他",
"- 提出时间:",
"- 关键原话或脱敏摘要:",
"## 需求变化记录",
"| 日期 | 变化内容 | 原因 | 用户确认 |",
"## 文档影响",
"- [ ] 不影响长期文档,原因:",
"- [ ] 更新架构与代码地图",
"- [ ] 更新业务规则与术语",
"- [ ] 更新常见修改或故障排查",
"## 交付文档影响",
"- [ ] 无交付文档影响,原因:",
"- [ ] 更新已有交付文档,受众与页面:",
"- [ ] 新增交付文档,受众与页面:",
"- [ ] 需要目标岗位或客户代表验证:是 / 否;验证方式:",
)
for section in missing_sections(content, required):
errors.append(f"单元任务模板缺少:{section}")
def check_agent_efficiency_rules(errors: list[str], root: Path = ROOT) -> None:
path = root / "AGENTS.md"
if not path.is_file():
return
content = path.read_text(encoding="utf-8")
required = (
"### 效率与范围控制",
"#### 严格控制范围",
"#### 渐进执行和修复",
"#### 复用已验证事实",
"#### 明确停止条件",
"单元任务是唯一正式实施单位",
"高风险修改必须停止",
"用户没有明确验收通过前不得关闭",
"长期核心文档必须先修改 Wiki",
"提交只包含当前工单相关文件",
"### 自然语言快捷指令",
"`只分析`",
"`建工单`",
"`执行工单 #N`",
"`建工单并做`",
"`继续工单 #N`",
"`检查工单 #N`",
"`同步文档`",
"`导出任务归档`",
"`导出全部任务归档`",
"`#N 验收通过`",
"### 需求记录与流转",
"不得臆造用户原话",
"不复制完整聊天",
"Gitea 工单全文不导出到仓库",
)
for section in missing_sections(content, required):
errors.append(f"AGENTS.md 缺少:{section}")
def check_claude_code_entry(errors: list[str], root: Path = ROOT) -> None:
"""检查 Claude Code 入口直接复用共同 Agent 规则。"""
path = root / "CLAUDE.md"
if not path.is_file():
return
content = path.read_text(encoding="utf-8")
lines = {line.strip() for line in content.splitlines()}
if "@AGENTS.md" not in lines:
errors.append("CLAUDE.md 缺少独立的 @AGENTS.md 导入")
required = (
"共同规则事实来源",
"只记录 Claude Code 特有",
"只修改 `AGENTS.md`",
"## 模型路由",
"## Agent 交接",
"## Haiku 只读约束",
"当前模型足以完成任务时不升级模型",
"Opus 输出方案后必须等待用户确认",
"不让 Haiku 决定最终根因",
"只读必须通过子 Agent 工具权限实现",
)
for section in missing_sections(content, required):
errors.append(f"CLAUDE.md 缺少:{section}")
def core_mapping_errors(configured_mappings: dict[str, str]) -> list[str]:
errors: list[str] = []
for page, expected_path in CORE_PAGE_PATHS.items():
if configured_mappings.get(page) != expected_path:
errors.append(
f"核心 Wiki 页面映射缺失或路径错误:{page} -> {expected_path}"
)
return errors
def check_wiki_mirrors(errors: list[str]) -> None:
"""检查核心映射与镜像头;任务快照由 check_archives 单独检查。"""
try:
config = load_config()
except WikiDocsError as exc:
errors.append(str(exc))
return
configured_mappings = {mapping.page: mapping.path for mapping in config.mappings}
errors.extend(core_mapping_errors(configured_mappings))
mapped_paths = {mapping.path for mapping in config.mappings}
actual_paths = {
path.relative_to(ROOT).as_posix() for path in (ROOT / "docs").rglob("*.md")
if path.parent != ROOT / "docs" / "task"
}
for path in sorted(actual_paths - mapped_paths):
errors.append(f"docs 中存在未登记的 Wiki 镜像:{path}")
for mapping in config.mappings:
path = ROOT / mapping.path
if not path.is_file():
errors.append(f"缺少 Wiki 镜像:{mapping.path}")
continue
try:
metadata, _ = parse_mirror(path.read_text(encoding="utf-8"))
except (OSError, UnicodeDecodeError, WikiDocsError) as exc:
errors.append(f"Wiki 镜像无效 {mapping.path}:{exc}")
continue
if metadata.get("generated") != "true (请先修改 Gitea Wiki,禁止直接编辑本文件)":
errors.append(f"{mapping.path} 没有只读镜像标记")
if metadata.get("wiki_page") != mapping.page:
errors.append(f"{mapping.path} 的 wiki_page 与映射不一致")
revision = metadata.get("wiki_revision", "")
if re.fullmatch(r"[0-9a-f]{40,64}", revision) is None:
errors.append(f"{mapping.path} 的 wiki_revision 无效")
if not metadata.get("synchronized_at"):
errors.append(f"{mapping.path} 缺少 synchronized_at")
def main() -> int:
parser = argparse.ArgumentParser(description="检查 DevHarness 项目结构")
parser.add_argument(
"--strict",
action="store_true",
help="项目档案有占位内容时返回失败",
)
args = parser.parse_args()
errors: list[str] = []
warnings: list[str] = []
check_required_files(errors)
check_project_profile(errors, warnings, args.strict)
check_wiki_mirrors(errors)
check_core_documents(errors)
check_task_template(errors)
check_agent_efficiency_rules(errors)
check_claude_code_entry(errors)
check_archives(errors)
for warning in warnings:
print(f"警告:{warning}")
for error in errors:
print(f"错误:{error}")
if errors:
print(f"检查失败:{len(errors)} 个问题")
return 1
print("DevHarness 检查通过")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+131
View File
@@ -0,0 +1,131 @@
"""把 Gitea Wiki 任务归档人工按需导出到 docs/task。"""
from __future__ import annotations
import argparse
import re
from pathlib import Path
from typing import Any
from new_task_archive import safe_title
from wiki_docs import (
DEFAULT_CONFIG,
ROOT,
WikiClient,
WikiDocsError,
dirty_paths,
load_config,
parse_mirror,
write_mirror,
)
TASK_PAGE_PATTERN = re.compile(r"^Task-(?P<number>\d+)-(?P<title>.+)$")
def task_revision(metadata: dict[str, Any], page_name: str) -> str:
last_commit = metadata.get("last_commit")
revision = last_commit.get("sha") if isinstance(last_commit, dict) else None
if not isinstance(revision, str) or not revision:
raise WikiDocsError(f"Wiki 页面缺少 revision:{page_name}")
return revision
def existing_task_mirrors(root: Path = ROOT) -> dict[str, Path]:
"""按镜像头匹配已有文件,兼容历史自定义文件名。"""
mirrors: dict[str, Path] = {}
task_dir = root / "docs" / "task"
if not task_dir.is_dir():
return mirrors
for path in task_dir.glob("*.md"):
try:
metadata, _ = parse_mirror(path.read_text(encoding="utf-8"))
except (OSError, UnicodeDecodeError, WikiDocsError) as exc:
raise WikiDocsError(f"已有任务镜像无效 {path.name}:{exc}") from exc
page_name = metadata.get("wiki_page", "")
if not TASK_PAGE_PATTERN.fullmatch(page_name):
raise WikiDocsError(f"已有任务镜像页面名无效 {path.name}:{page_name}")
if page_name in mirrors:
raise WikiDocsError(f"任务页面存在重复本地镜像:{page_name}")
mirrors[page_name] = path
return mirrors
def task_target(page_name: str, root: Path = ROOT) -> Path:
match = TASK_PAGE_PATTERN.fullmatch(page_name)
if match is None:
raise WikiDocsError(f"不是任务归档页面:{page_name}")
title = safe_title(match.group("title"))
if not title:
raise WikiDocsError(f"任务归档标题无效:{page_name}")
return root / "docs" / "task" / f"{match.group('number')}-{title}.md"
def export_task_archives(
client: WikiClient, *, export_all: bool = False, root: Path = ROOT
) -> list[str]:
"""增量或全量读取任务归档;绝不删除本地文件。"""
dirty = dirty_paths(["docs/task"], root)
if dirty:
raise WikiDocsError(
"本地任务镜像存在未提交改动,已停止以防覆盖:\n" + "\n".join(dirty)
)
existing = existing_task_mirrors(root)
pages = []
for metadata in client.list_pages():
title = metadata.get("title")
if isinstance(title, str) and TASK_PAGE_PATTERN.fullmatch(title):
pages.append((int(title.split("-", 2)[1]), title, metadata))
pages.sort(key=lambda item: (item[0], item[1]))
messages: list[str] = []
targets: set[Path] = set()
for _, page_name, metadata in pages:
target = existing.get(page_name, task_target(page_name, root))
if target in targets:
raise WikiDocsError(f"多个任务页面映射到同一本地路径:{target.name}")
targets.add(target)
revision = task_revision(metadata, page_name)
if not export_all and target.is_file():
local_metadata, _ = parse_mirror(target.read_text(encoding="utf-8"))
if (
local_metadata.get("wiki_page") == page_name
and local_metadata.get("wiki_revision") == revision
):
messages.append(f"跳过:{target.relative_to(root)} <- {page_name}@{revision[:12]}")
continue
page = client.get_page_from_metadata(metadata, page_name)
changed = write_mirror(target, page)
action = "已导出" if changed else "无变化"
messages.append(f"{action}:{target.relative_to(root)} <- {page_name}@{revision[:12]}")
return messages
def main() -> int:
parser = argparse.ArgumentParser(description="人工按需导出 Gitea Wiki 任务归档")
parser.add_argument(
"--all", action="store_true", help="全量读取全部线上任务归档;默认按 revision 增量"
)
parser.add_argument(
"--config", default=str(DEFAULT_CONFIG), help="核心 Wiki 映射配置"
)
args = parser.parse_args()
try:
config = load_config(Path(args.config).resolve())
messages = export_task_archives(
WikiClient(config), export_all=args.all
)
except WikiDocsError as exc:
print(f"错误:{exc}")
return 1
for message in messages:
print(message)
print("任务归档全量导出完成" if args.all else "任务归档增量导出完成")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+87
View File
@@ -0,0 +1,87 @@
"""只在 Gitea Wiki 创建任务归档;本地镜像由人工按需导出。"""
from __future__ import annotations
import argparse
import re
from datetime import date
from pathlib import Path
from wiki_docs import (
DEFAULT_CONFIG,
WikiClient,
WikiDocsError,
load_config,
)
def safe_title(title: str) -> str:
"""把标题转换为适合 Wiki 页面名和 Windows 文件名的短文本。"""
cleaned = re.sub(r'[<>:"/\\|?*]', "-", title.strip())
cleaned = re.sub(r"\s+", "-", cleaned)
cleaned = re.sub(r"-+", "-", cleaned)
return cleaned.strip(".-")
def build_archive(
template: str,
issue_number: str,
title: str,
page_name: str,
issue_url: str,
) -> str:
content = template.replace("<工单号>", issue_number, 1)
content = content.replace("<标题>", title.strip(), 1)
content = content.replace("YYYY-MM-DD", date.today().isoformat(), 1)
content = content.replace("<链接>", issue_url, 1)
return content.replace("<页面名>", page_name, 1)
def main() -> int:
parser = argparse.ArgumentParser(
description="在 Gitea Wiki 创建任务归档,不自动导出本地镜像"
)
parser.add_argument("issue_number", help="Gitea 工单号,例如 123")
parser.add_argument("title", help="简短任务标题")
parser.add_argument("--config", default=str(DEFAULT_CONFIG), help="Wiki 映射配置")
args = parser.parse_args()
short_title = safe_title(args.title)
if not args.issue_number.isdigit():
print("错误:工单号必须是数字")
return 1
if not short_title:
print("错误:标题不能为空")
return 1
try:
config = load_config(Path(args.config).resolve())
page_name = f"Task-{args.issue_number}-{short_title}"
client = WikiClient(config)
if any(item.get("title") == page_name for item in client.list_pages()):
raise WikiDocsError(f"任务归档已经存在:{page_name}")
template = client.get_page("Task-Archive-Template").text
issue_url = (
f"{config.gitea_url}/{config.owner}/{config.repository}/issues/"
f"{args.issue_number}"
)
content = build_archive(
template, args.issue_number, args.title, page_name, issue_url
)
page = client.create_page(
page_name,
content,
f"docs: 创建任务 #{args.issue_number} 归档草稿",
)
except WikiDocsError as exc:
print(f"错误:{exc}")
return 1
print(f"已创建 Wiki:{page.html_url}")
print("未导出本地任务归档;需要时运行 export_task_archives.py")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+33
View File
@@ -0,0 +1,33 @@
"""从 Gitea Wiki 单向导出配置中的核心 docs 镜像。"""
from __future__ import annotations
import argparse
from pathlib import Path
from wiki_docs import DEFAULT_CONFIG, WikiClient, WikiDocsError, load_config, sync_all
def main() -> int:
parser = argparse.ArgumentParser(description="从 Gitea Wiki 单向同步核心 docs 镜像")
parser.add_argument(
"--check", action="store_true", help="只检查 Wiki 与镜像是否一致,不写文件"
)
parser.add_argument(
"--config", default=str(DEFAULT_CONFIG), help="Wiki 页面映射 JSON 文件"
)
args = parser.parse_args()
try:
config = load_config(Path(args.config).resolve())
messages = sync_all(config, WikiClient(config), check=args.check)
except WikiDocsError as exc:
print(f"错误:{exc}")
return 1
for message in messages:
print(message)
print("Wiki 镜像检查通过" if args.check else "Wiki 镜像同步完成")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+422
View File
@@ -0,0 +1,422 @@
"""Gitea Wiki 到本地 docs 镜像的共享实现。"""
from __future__ import annotations
import base64
import json
import os
import re
import subprocess
import tempfile
from dataclasses import dataclass
from datetime import datetime, timezone
from pathlib import Path, PurePosixPath
from typing import Any
from urllib.error import HTTPError, URLError
from urllib.parse import quote, urlencode
from urllib.request import Request, urlopen
ROOT = Path(__file__).resolve().parents[1]
DEFAULT_CONFIG = ROOT / "wiki-docs.json"
MIRROR_START = "<!-- gitea-wiki-mirror:start -->"
MIRROR_END = "<!-- gitea-wiki-mirror:end -->"
HEADER_PATTERN = re.compile(
rf"\A{re.escape(MIRROR_START)}\n(?P<metadata>.*?)\n"
rf"{re.escape(MIRROR_END)}\n\n(?P<body>.*)\Z",
re.DOTALL,
)
class WikiDocsError(RuntimeError):
"""可供命令行直接展示的 Wiki 文档错误。"""
@dataclass(frozen=True)
class Mapping:
page: str
path: str
@dataclass(frozen=True)
class Config:
path: Path
gitea_url: str
owner: str
repository: str
mappings: tuple[Mapping, ...]
@dataclass(frozen=True)
class WikiPage:
title: str
sub_url: str
text: str
revision: str
html_url: str
def _required_string(data: dict[str, Any], key: str) -> str:
value = data.get(key)
if not isinstance(value, str) or not value.strip():
raise WikiDocsError(f"配置字段 {key!r} 必须是非空字符串")
return value.strip()
def validate_mappings(raw_mappings: Any) -> tuple[Mapping, ...]:
"""校验显式页面映射,确保只会写入 docs 下的 Markdown。"""
if not isinstance(raw_mappings, list) or not raw_mappings:
raise WikiDocsError("配置字段 'mappings' 必须是非空数组")
mappings: list[Mapping] = []
pages: set[str] = set()
paths: set[str] = set()
for index, item in enumerate(raw_mappings, start=1):
if not isinstance(item, dict):
raise WikiDocsError(f"第 {index} 个映射必须是对象")
page = _required_string(item, "page")
path = _required_string(item, "path").replace("\\", "/")
pure_path = PurePosixPath(path)
if (
pure_path.is_absolute()
or ".." in pure_path.parts
or not pure_path.parts
or pure_path.parts[0] != "docs"
or pure_path.suffix.lower() != ".md"
):
raise WikiDocsError(f"镜像路径必须是 docs/ 下的 Markdown:{path}")
if page in pages:
raise WikiDocsError(f"Wiki 页面重复映射:{page}")
if path in paths:
raise WikiDocsError(f"本地路径重复映射:{path}")
pages.add(page)
paths.add(path)
mappings.append(Mapping(page=page, path=path))
return tuple(mappings)
def load_config(path: Path = DEFAULT_CONFIG) -> Config:
try:
raw = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
raise WikiDocsError(f"无法读取 Wiki 映射配置 {path}: {exc}") from exc
if not isinstance(raw, dict) or raw.get("schema_version") != 1:
raise WikiDocsError("wiki-docs.json 的 schema_version 必须为 1")
configured_url = _required_string(raw, "gitea_url")
gitea_url = os.environ.get("GITEA_URL", configured_url).rstrip("/")
if gitea_url.endswith("/api/v1"):
gitea_url = gitea_url[: -len("/api/v1")]
return Config(
path=path,
gitea_url=gitea_url,
owner=_required_string(raw, "owner"),
repository=_required_string(raw, "repository"),
mappings=validate_mappings(raw.get("mappings")),
)
class WikiClient:
"""只使用标准库访问 Gitea Wiki API。"""
def __init__(self, config: Config, token: str | None = None) -> None:
self.config = config
self.token = token if token is not None else os.environ.get("GITEA_TOKEN")
def _request(
self,
method: str,
api_path: str,
*,
payload: dict[str, Any] | None = None,
query: dict[str, Any] | None = None,
) -> Any:
url = f"{self.config.gitea_url}/api/v1{api_path}"
if query:
url = f"{url}?{urlencode(query)}"
headers = {"Accept": "application/json"}
if self.token:
headers["Authorization"] = f"Bearer {self.token}"
data = None
if payload is not None:
data = json.dumps(payload, ensure_ascii=False).encode("utf-8")
headers["Content-Type"] = "application/json"
request = Request(url, data=data, headers=headers, method=method)
try:
with urlopen(request, timeout=30) as response:
body = response.read()
except HTTPError as exc:
if self.token and method == "GET" and exc.code in {401, 403, 404}:
# 公共仓库可能可匿名读取,而当前 shell 中的通用令牌属于
# 另一个实例或已失效。只对只读请求安全降级为匿名访问。
anonymous_headers = {"Accept": "application/json"}
anonymous_request = Request(
url, data=data, headers=anonymous_headers, method=method
)
try:
with urlopen(anonymous_request, timeout=30) as response:
body = response.read()
except HTTPError as anonymous_exc:
detail = anonymous_exc.read().decode("utf-8", errors="replace")
raise WikiDocsError(
f"Gitea API {method} {api_path} 返回 "
f"{anonymous_exc.code}: {detail}"
) from anonymous_exc
except URLError as anonymous_exc:
raise WikiDocsError(
f"无法连接 Gitea:{anonymous_exc.reason}"
) from anonymous_exc
else:
detail = exc.read().decode("utf-8", errors="replace")
raise WikiDocsError(
f"Gitea API {method} {api_path} 返回 {exc.code}: {detail}"
) from exc
except URLError as exc:
raise WikiDocsError(f"无法连接 Gitea:{exc.reason}") from exc
if not body:
return None
try:
return json.loads(body.decode("utf-8"))
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
raise WikiDocsError("Gitea API 返回了无效的 UTF-8 JSON") from exc
def list_pages(self) -> list[dict[str, Any]]:
pages: list[dict[str, Any]] = []
page_number = 1
while True:
batch = self._request(
"GET",
f"/repos/{quote(self.config.owner, safe='')}/"
f"{quote(self.config.repository, safe='')}/wiki/pages",
query={"page": page_number, "limit": 50},
)
if not isinstance(batch, list):
raise WikiDocsError("Gitea Wiki 页面列表格式无效")
pages.extend(item for item in batch if isinstance(item, dict))
if len(batch) < 50:
return pages
page_number += 1
def get_page(self, page_name: str) -> WikiPage:
metadata = next(
(
item
for item in self.list_pages()
if item.get("title") == page_name or item.get("sub_url") == page_name
),
None,
)
if metadata is None:
raise WikiDocsError(
f"Wiki 页面不存在:{page_name};不会自动删除或重命名本地镜像"
)
return self.get_page_from_metadata(metadata, page_name)
def get_page_from_metadata(
self, metadata: dict[str, Any], page_name: str | None = None
) -> WikiPage:
"""使用页面列表元数据读取正文,避免重复获取完整页面列表。"""
resolved_name = page_name or _required_string(metadata, "title")
sub_url = _required_string(metadata, "sub_url")
page = self._request(
"GET",
f"/repos/{quote(self.config.owner, safe='')}/"
f"{quote(self.config.repository, safe='')}/wiki/page/"
f"{quote(sub_url, safe='%')}",
)
if not isinstance(page, dict):
raise WikiDocsError(f"Wiki 页面响应格式无效:{resolved_name}")
encoded_content = page.get("content_base64")
if not isinstance(encoded_content, str):
raise WikiDocsError(f"Wiki 页面没有 content_base64:{resolved_name}")
try:
text = base64.b64decode(encoded_content, validate=True).decode("utf-8")
except (ValueError, UnicodeDecodeError) as exc:
raise WikiDocsError(
f"Wiki 页面不是有效的 UTF-8 Markdown:{resolved_name}"
) from exc
last_commit = page.get("last_commit")
revision = last_commit.get("sha") if isinstance(last_commit, dict) else None
if not isinstance(revision, str) or not revision:
raise WikiDocsError(f"Wiki 页面缺少 revision:{resolved_name}")
title = page.get("title")
resolved_title = title if isinstance(title, str) and title else resolved_name
html_url = (
f"{self.config.gitea_url}/{quote(self.config.owner, safe='')}/"
f"{quote(self.config.repository, safe='')}/wiki/{quote(sub_url, safe='%')}"
)
return WikiPage(
title=resolved_title,
sub_url=sub_url,
text=normalize_body(text),
revision=revision,
html_url=html_url,
)
def create_page(self, title: str, content: str, message: str) -> WikiPage:
if not self.token:
raise WikiDocsError("创建 Wiki 页面需要通过 GITEA_TOKEN 提供写入令牌")
encoded = base64.b64encode(content.encode("utf-8")).decode("ascii")
self._request(
"POST",
f"/repos/{quote(self.config.owner, safe='')}/"
f"{quote(self.config.repository, safe='')}/wiki/new",
payload={"title": title, "content_base64": encoded, "message": message},
)
return self.get_page(title)
def normalize_body(text: str) -> str:
return text.replace("\r\n", "\n").replace("\r", "\n").rstrip() + "\n"
def parse_mirror(text: str) -> tuple[dict[str, str], str]:
match = HEADER_PATTERN.match(text.replace("\r\n", "\n").replace("\r", "\n"))
if match is None:
raise WikiDocsError("缺少或损坏 gitea-wiki-mirror 元数据头")
metadata: dict[str, str] = {}
for line in match.group("metadata").splitlines():
key, separator, value = line.partition(": ")
if not separator or not key or not value:
raise WikiDocsError(f"无效的镜像元数据行:{line}")
metadata[key] = value
return metadata, normalize_body(match.group("body"))
def render_mirror(page: WikiPage, existing: str | None = None) -> str:
synchronized_at: str | None = None
if existing is not None:
try:
metadata, body = parse_mirror(existing)
except WikiDocsError:
pass
else:
if metadata.get("wiki_revision") == page.revision and body == page.text:
synchronized_at = metadata.get("synchronized_at")
if not synchronized_at:
synchronized_at = datetime.now(timezone.utc).isoformat(timespec="seconds").replace(
"+00:00", "Z"
)
header = "\n".join(
(
MIRROR_START,
"generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)",
f"wiki_page: {page.title}",
f"wiki_url: {page.html_url}",
f"wiki_revision: {page.revision}",
f"synchronized_at: {synchronized_at}",
MIRROR_END,
)
)
return f"{header}\n\n{page.text}"
def dirty_mirror_paths(config: Config, root: Path = ROOT) -> list[str]:
return dirty_paths([mapping.path for mapping in config.mappings], root)
def dirty_paths(paths: list[str], root: Path = ROOT) -> list[str]:
"""返回指定路径中已有、修改或未跟踪的工作区条目。"""
if not paths:
return []
result = subprocess.run(
["git", "status", "--porcelain", "--", *paths],
cwd=root,
check=True,
capture_output=True,
text=True,
encoding="utf-8",
)
return [line for line in result.stdout.splitlines() if line.strip()]
def _write_atomic(path: Path, content: str) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
handle, temporary_name = tempfile.mkstemp(
prefix=f".{path.name}.", suffix=".tmp", dir=path.parent
)
try:
with os.fdopen(handle, "w", encoding="utf-8", newline="\n") as stream:
stream.write(content)
os.replace(temporary_name, path)
except BaseException:
Path(temporary_name).unlink(missing_ok=True)
raise
def write_mirror(path: Path, page: WikiPage) -> bool:
"""写入一份 Wiki 镜像;内容无变化时返回 False。"""
existing = path.read_text(encoding="utf-8") if path.is_file() else None
rendered = render_mirror(page, existing)
if existing == rendered:
return False
_write_atomic(path, rendered)
return True
def check_mirror(mapping: Mapping, page: WikiPage, path: Path) -> list[str]:
if not path.is_file():
return [f"缺少镜像:{mapping.path}"]
try:
metadata, body = parse_mirror(path.read_text(encoding="utf-8"))
except (OSError, UnicodeDecodeError, WikiDocsError) as exc:
return [f"镜像无效 {mapping.path}: {exc}"]
expected = {
"wiki_page": page.title,
"wiki_url": page.html_url,
"wiki_revision": page.revision,
}
errors = [
f"{mapping.path} 的 {key} 不一致"
for key, value in expected.items()
if metadata.get(key) != value
]
if not metadata.get("synchronized_at"):
errors.append(f"{mapping.path} 缺少 synchronized_at")
if body != page.text:
errors.append(f"{mapping.path} 的正文与 Wiki 不一致")
return errors
def sync_all(config: Config, client: WikiClient, *, check: bool = False) -> list[str]:
"""检查或写入所有显式映射;绝不处理映射外的文件。"""
if not check:
dirty = dirty_mirror_paths(config)
if dirty:
details = "\n".join(dirty)
raise WikiDocsError(
"已映射的本地镜像存在未提交改动,已停止以防覆盖:\n" + details
)
messages: list[str] = []
for mapping in config.mappings:
page = client.get_page(mapping.page)
target = ROOT / PurePosixPath(mapping.path)
if check:
errors = check_mirror(mapping, page, target)
if errors:
raise WikiDocsError("\n".join(errors))
messages.append(f"一致:{mapping.path} <- {page.title}@{page.revision[:12]}")
continue
existing = target.read_text(encoding="utf-8") if target.is_file() else None
rendered = render_mirror(page, existing)
if existing != rendered:
_write_atomic(target, rendered)
messages.append(f"已更新:{mapping.path} <- {page.title}@{page.revision[:12]}")
else:
messages.append(f"无变化:{mapping.path} <- {page.title}@{page.revision[:12]}")
return messages
def append_mapping(config: Config, mapping: Mapping) -> None:
raw = json.loads(config.path.read_text(encoding="utf-8"))
mappings = validate_mappings(raw.get("mappings"))
if any(item.page == mapping.page or item.path == mapping.path for item in mappings):
raise WikiDocsError(f"页面或路径已经登记:{mapping.page} -> {mapping.path}")
raw["mappings"].append({"page": mapping.page, "path": mapping.path})
rendered = json.dumps(raw, ensure_ascii=False, indent=2) + "\n"
_write_atomic(config.path, rendered)
+86 -21
View File
@@ -1,23 +1,71 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Project-Profile
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Project-Profile.-
wiki_revision: 16456d6ed083197759d553b1033e3e5c8a6b8932
synchronized_at: 2026-08-16T11:49:52Z
<!-- gitea-wiki-mirror:end -->
# 项目档案 # 项目档案
复制模板后先填写本页。这里保存不经常变化、所有维护者都需要知道的信息。 本页记录不经常变化、所有维护者都需要知道的信息。它是项目档案的事实来源;仓库内 `docs/00-project-profile.md` 是只读镜像。
## 基本信息 ## 基本信息
| 项目 | 内容 | | 项目 | 内容 |
|---|---| |---|---|
| 项目名称 | `<填写>` | | 项目名称 | DevHarness |
| 一句话目标 | `<填写>` | | 一句话目标 | 提供以 Gitea 工单、Wiki 和 Git 为事实来源的 AI 辅助开发工作流模板 |
| Gitea 地址 | `<例如 https://gitea.example.com>` | | 主要使用者 | 项目负责人、Claude/Codex Agent、接手简单维护的初级程序员 |
| 仓库 | `<owner/repository>` | | Gitea 地址 | http://ilaer.eicp.net:8418 |
| 仓库 | `opc/dev_harness` |
| 默认分支 | `main` | | 默认分支 | `main` |
| 主要维护者 | `<填写>` | | 主要维护者 | `ila` |
| 文档适用范围 | 默认分支当前版本;具体镜像 revision 见每个本地文件头 |
## 技术栈 ## DevHarness 来源与基线
每个采用 DevHarness 的业务项目都必须填写本节。它记录的是所采用的 DevHarness 上游版本,不是业务项目自己的提交。不得使用“最新版本”“当前 main”等动态描述代替完整提交哈希。
| 项目 | 内容 |
|---|---|
| DevHarness 来源仓库 | `http://ilaer.eicp.net:8418/opc/dev_harness` |
| 当前基线提交 | 本仓库是 DevHarness 上游源模板,不适用;复制到业务项目后必须替换为实际采用的完整提交哈希 |
| 最后接入或升级日期 | 2026-08-16 |
| 项目适配说明 | 本仓库维护源模板;业务项目填写保留、改写或未采用的 Harness 规则与工具 |
首次接入和后续升级都必须在目标项目工单中记录旧基线、新基线和差异分类。升级验收通过后,目标项目应把“当前基线提交”和日期更新为已采用的上游提交;未完成或已回退的升级不得更新基线。
## 子项目与交付单元
“子项目”是仓库中具有明确职责和规则边界的应用或模块;“交付单元”是能够独立构建、测试、版本化或发布的程序、服务、库或文档包。一个子项目可以对应一个交付单元,也可以包含多个交付单元。
| 子项目 / 交付单元 | 职责 | 技术栈 | 构建与测试 | 版本与发布方式 | 规则入口 | 共享边界 |
|---|---|---|---|---|---|---|
| DevHarness 模板 | 提供 Agent 开发流程、Wiki 镜像和结构检查 | Markdown、Python 3 标准库、Gitea 1.25 | `python dev_scripts/check_harness.py --strict`;`python -m unittest discover -s tests -v` | 跟随仓库 `main` 分支,不单独发布产品程序 | 根目录 `AGENTS.md` | Gitea 工单、Wiki、Git 和 `docs/` 的事实来源边界 |
单应用项目只填写一行。多应用单仓库必须逐个填写,并为技术栈、构建测试或安全规则不同的目录增加子目录 `AGENTS.md`。技术栈不同不等于必须拆分 Git 仓库;是否拆仓应根据团队、权限、发布周期、仓库效率、复用关系和共享接口稳定性判断。
跨子项目接口或契约必须指定唯一事实来源,并说明各交付单元的兼容范围和验证命令。不得在多个页面维护互不确认的“权威版本”。
## 技术栈与运行环境
| 部分 | 技术 | 规则文件 | | 部分 | 技术 | 规则文件 |
|---|---|---| |---|---|---|
| `<子项目或服务>` | `<语言、框架、版本>` | `<路径/AGENTS.md>` | | Harness 规则和模板 | Markdown、Gitea 1.25 | `AGENTS.md` |
| Wiki 镜像与结构检查 | Python 3 标准库 | `AGENTS.md` |
| 主要开发环境 | Windows、PowerShell、Git | `AGENTS.md` |
本项目不需要安装第三方 Python 包。复制到业务项目后,必须把真实语言、框架、版本和支持平台写入本节。
## 阅读入口
- 新人入口:Home。
- 代码入口:[架构与代码地图](Architecture-and-Code-Map.-)。
- 业务边界:[业务规则与术语](Business-Rules-and-Glossary.-)。
- 运行验证:[本地开发与验证](Local-Development-and-Verification.-)。
- 简单维护:[常见修改指南](Common-Changes.-)。
- 错误定位:[故障排查](Troubleshooting)。
## 常用命令 ## 常用命令
@@ -25,26 +73,43 @@
| 用途 | 命令 | 预期结果 | | 用途 | 命令 | 预期结果 |
|---|---|---| |---|---|---|
| 安装依赖 | `<填写>` | `<填写>` | | 查看工作区 | `git status --short --branch` | 显示分支且没有无关修改 |
| 启动开发环境 | `<填写>` | `<填写>` | | 检查模板结构 | `python dev_scripts/check_harness.py --strict` | 输出“DevHarness 检查通过” |
| 格式检查 | `<填写>` | `<填写>` | | 运行单元测试 | `python -m unittest discover -s tests -v` | 所有测试通过 |
| 静态检查 | `<填写>` | `<填写>` | | 导出核心 Wiki 镜像 | `python dev_scripts/sync_wiki_docs.py` | 核心页面写入 `docs/`,不处理任务归档 |
| 单元测试 | `<填写>` | `<填写>` | | 检查核心 Wiki 镜像 | `python dev_scripts/sync_wiki_docs.py --check` | 输出核心镜像与 Wiki 一致 |
| 集成测试 | `<填写或写“不适用”>` | `<填写>` | | 创建任务归档 | `python dev_scripts/new_task_archive.py 123 "修复登录超时"` | 只创建 Wiki 归档页,不写入本地 |
| 增量导出任务归档 | `python dev_scripts/export_task_archives.py` | 只导出新增或 revision 已变化的任务归档 |
| 全量导出任务归档 | `python dev_scripts/export_task_archives.py --all` | 读取并导出全部线上任务归档 |
## 目录边界 ## 目录边界
| 目录 | 职责 | 不应放入 | | 目录 | 职责 | 不应放入 |
|---|---|---| |---|---|---|
| `<路径>` | `<填写>` | `<填写>` | | `.gitea/issue_template/` | Gitea 工单模板 | 凭据、任务最终归档 |
| `docs/` | Wiki 自动导出的只读镜像 | 人工直接维护的长期文档 |
| `docs/task/` | 人工按需导出的 Wiki 任务归档只读快照,可能不是完整历史 | 讨论过程和临时方案 |
| `dev_scripts/` | Harness 检查、Wiki 同步和归档工具 | 产品功能代码 |
| `tests/` | Harness 工具自动化测试 | 生产数据 |
## 环境与凭据 ## 环境、配置与凭据
- 本地配置文件:`<填写>` - 核心 Wiki 同步配置:仓库根目录 `wiki-docs.json`;任务归档不逐页登记,由按需导出工具动态发现。
- 配置示例文件:`<填写>` - Gitea 地址可由配置提供,也可通过 `GITEA_URL` 覆盖。
- 凭据保存位置:`<只写保存方式,不填写真实凭据>` - Gitea PAT 仅通过 `GITEA_TOKEN` 或 MCP 安全配置提供,不写入仓库。
- 日志和构建产物位置:`<填写>` - Token 至少需要读取仓库权限;创建或更新 Wiki 时还需要写仓库权限。
- 配置示例:`wiki-docs.json` 只保存非敏感仓库信息。
- 日志:本项目不持久化运行日志,命令行错误是主要诊断信息。
- 测试数据:只使用测试构造的字符串、路径和模拟响应,不使用生产数据。
- 构建产物:Python 缓存和临时文件不提交。
## 项目专用验收要求 ## 项目专用验收要求
- `<填写>` - 长期核心文档必须先更新 Wiki,再导出本地镜像;任务归档默认只保存在 Wiki,用户明确要求时才增量或全量导出。
- 镜像必须包含来源页面、revision 和同步时间。
- 页面删除、重命名和映射变更必须人工确认。
- 新增核心文档时必须更新 Home、显式映射和 Harness 检查。
- 代码入口、命令、配置、业务规则或排错方式变化时必须评估文档影响。
- `python dev_scripts/check_harness.py --strict` 必须通过。
- `python -m unittest discover -s tests -v` 必须通过。
- 未执行或无法覆盖的验证必须记录到工单。
+201 -19
View File
@@ -1,14 +1,29 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Development-Workflow
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Development-Workflow.-
wiki_revision: 3f50238b637bc2f896ceab6caa5ab8ab1ded2a4b
synchronized_at: 2026-08-16T11:18:52Z
<!-- gitea-wiki-mirror:end -->
# 开发工作流 # 开发工作流
## 事实来源边界
- Gitea 工单记录任务状态、讨论、阻塞、方案变化、验证和验收。
- Gitea Wiki 保存架构说明、开发规范、操作手册和完成后的任务归档。
- Git 保存源码、与特定代码版本强绑定的文档,以及 Wiki 的本地镜像。
- 本地 `docs/` 仅供浏览和审查,不是长期文档编辑入口。
## 一次任务怎样完成 ## 一次任务怎样完成
### 1. 讨论 ### 1. 讨论
用户描述需求或故障。Agent 先检查现状,再给出目标、非目标、方案、风险、回退和验证方法。存在不同实现方向时,说明取舍,让用户确认。 用户描述需求或故障。Agent 先检查现状,再给出目标、非目标、方案、风险、回退和验证方法。存在不同实现方向时,说明取舍并等待用户确认。
### 2. 建单 ### 2. 建单
方案确认后,使用 `.gitea/issue_template/task.md` 创建单元任务工单。没有工单号之前不修改产品代码。 方案确认后,使用 `.gitea/issue_template/task.md` 创建单元任务工单。没有工单号之前不修改产品代码或正式文档。
新产品或较大版本先建立 Epic,再建立 MVP: 新产品或较大版本先建立 Epic,再建立 MVP:
@@ -22,31 +37,197 @@
每个单元任务都应目标单一,能够独立测试、提交和回退。 每个单元任务都应目标单一,能够独立测试、提交和回退。
#### 依赖与并行
建立新工单不要求其他工单已经完成,也不按工单编号限制实施顺序。每个单元任务必须声明:
- 前置工单,没有时填写“无”;
- 是否允许与未完成的前置工单并行;
- 判断可以或不可以并行的原因。
开始修改前,Agent 检查工单声明的前置工单:
- 没有前置工单,或前置工单已经完成,可以进入“进行中”;
- 前置工单未完成且存在实际依赖时,不得开始实施,工单保持“待实施”;
- 与前置工单没有实施冲突、允许并行时,可以进入“进行中”,但必须在工单写明原因;
- 已经进入实施后出现计划外、当前无法解除的问题,才使用“阻塞”。
依赖不改变单元任务边界。依赖满足后,该任务仍须拥有独立的范围、提交、测试和回退方式。
### 3. 实施 ### 3. 实施
Agent 检查工作区,只修改工单范围内的文件。发现新问题时先记录到工单;如果它不影响当前验收,另建工单,不扩大当前任务。 Agent 检查分支和工作区,只修改工单范围内的文件。发现新问题时先记录到工单;如果不影响当前验收,则另建工单,不扩大当前任务。
重要进度应及时写回工单: 重要进度及时写回工单:
- 已确认的根因; - 已确认的根因;
- 方案或范围变化; - 方案或范围变化;
- 测试结果; - 测试结果;
- 阻塞和未验证内容; - 阻塞和未验证内容;
- Git 提交哈希。 - Git 提交哈希;
- 相关 Wiki 页面及 revision。
长期核心文档遵循唯一顺序:
```text
修改 Wiki → 读取确认 → 导出核心 docs → 校验差异 → 提交镜像
```
任务归档默认只更新 Wiki,不自动导出到 `docs/task/`。不得先编辑本地镜像再反向覆盖 Wiki。
### 4. 待验收 ### 4. 待验收
实现和测试完成后,Agent 提交代码并将工单更新为待验收。用户验收前工单保持开启。 实现和测试完成后,Agent 提交实现代码并将工单更新为“待验收”。用户验收前工单保持开启。
### 5. 归档和关闭 ### 5. 归档和关闭
使用以下命令创建归档草稿: 使用以下命令只在 Wiki 创建任务归档页:
```powershell ```powershell
python scripts/new_task_archive.py 123 "修复登录超时" python dev_scripts/new_task_archive.py 123 "修复登录超时"
``` ```
填写实际结果后单独提交归档,再把路径和提交哈希写回工单。用户明确验收通过后,关闭单元工单并勾选父工单中的任务。 归档内容以 Wiki 页面为事实来源。默认不修改 `wiki-docs.json`,也不写入 `docs/task/`。把 Wiki 页面、revision 和实现提交哈希写回工单;用户明确验收通过后,关闭单元工单并勾选父工单中的任务。
只有用户明确提出时才导出任务归档:
```powershell
python dev_scripts/export_task_archives.py # 增量:新增或 revision 变化
python dev_scripts/export_task_archives.py --all # 全量:读取全部线上任务归档
```
导出不得自动删除本地文件。`docs/task/` 只是人工按需生成的只读快照,可能不是完整或最新的任务历史。
## 文档同步规则
- 核心页面映射保存在 `wiki-docs.json`;普通同步只处理这些核心长期文档。
- 任务归档不逐页登记映射,由按需导出工具根据 `Task-<编号>-<标题>` 动态发现;已有镜像优先按镜像头匹配原页面。
- 所有同步和导出只实现 Wiki → `docs/`,不提供反向同步。
- 镜像头必须记录页面名、页面地址、revision 和同步时间。
- 已跟踪镜像存在未提交改动时,同步必须停止;确认改动来源后再处理。
- 核心同步的 `--check` 只检查核心镜像,不要求线上任务归档全部存在于本地。
- 已经导出的任务镜像仍必须具有来源页面、revision 和同步时间,并通过 Harness 格式检查。
- 页面删除和重命名不会自动传播,必须先更新工单并人工确认映射变化。
- Wiki 更新成功而导出失败时,在工单记录部分完成状态,不得把任务标为完成。
- 与具体代码版本强绑定的接口或迁移资料可直接随代码维护,但必须在 Wiki 提供入口或适用版本说明。
## 面向初级维护者的修改边界
| 风险 | 示例 | 处理方式 |
|---|---|---|
| 低 | 文案、简单校验、查询条件、独立 UI、小范围回归 Bug | 初级程序员可在 Agent 协助下理解、修改和验证 |
| 中 | API、配置、依赖、跨模块逻辑、数据结构 | 由 Agent 实现,程序员检查差异并执行验证 |
| 高 | 权限、安全、并发、迁移、支付、删除数据、不可逆操作 | 停止修改,由 Agent 分析并等待人工确认 |
风险由影响范围决定,不按代码行数判断。
## 每个任务的文档影响
单元任务必须明确选择:
- 不影响长期文档,并说明原因;
- 更新项目档案或运行验证;
- 更新架构与代码地图;
- 更新业务规则与术语;
- 更新常见修改或故障排查;
- 新增或调整其他 Wiki 页面。
以下变化必须更新相关 Wiki:
- 启动、测试、部署或排错命令变化;
- 模块入口、目录职责或主要调用路径变化;
- 配置项、API、数据结构或状态变化;
- 业务规则、安全边界或权限变化;
- 日志位置、错误定位或常见处理方式变化。
普通内部重构如果入口、行为、配置和验证方式均未改变,可以记录“不影响长期文档”及原因。
## 需求记录与流转
聊天用于分析和确认,不是正式需求的长期事实来源。创建单元任务工单时,Agent 应记录:
- 原始需求的来源和提出时间;
- 能表达用户目的、使用场景和限制的少量关键原话;
- 整理后的目标、非目标、确认方案、验收标准和文档影响;
- 实施期间影响范围、接口、数据、风险或验收的需求变化,以及变化原因和用户确认。
只摘录完成追踪所需的内容,不复制完整聊天,不保存 Agent 内部推理,不写入密码、令牌、个人数据或生产数据。包含敏感信息的原话必须删除敏感部分或改写为脱敏摘要。
需求按以下边界流转:
| 内容 | 事实来源 | 本地镜像 |
|---|---|---|
| 关键原始需求、确认后的单次任务需求 | Gitea 单元任务工单 | 无 |
| 讨论、决定和需求变化 | Gitea 工单正文或评论 | 无 |
| 长期有效的产品需求、业务规则和系统边界 | 对应 Gitea Wiki 主题页 | `docs/` |
| 完成后的实现、验证和遗留问题 | Wiki 任务归档 | 人工按需导出的 `docs/task/` 快照(可能不完整) |
任务产生长期结论时,先更新对应 Wiki 主题页,再导出本地镜像。Gitea 工单全文不导出到仓库,避免形成第二份任务过程记录。
## 稳定文档与任务归档
- Home、项目档案、代码地图、业务规则、开发验证、常见修改和故障排查描述项目现在怎样工作。
- 工单和 Wiki 任务归档解释某次为什么修改、实际改了什么以及如何验证;本地任务快照不是完整历史。
- 新人先读稳定主题页,只有追查历史原因时才读任务归档。
- 任务产生的长期结论必须合并到主题页,不能只留在归档。
## 效率与范围控制
本节用于减少无关工作和重复检查,不得削弱安全规则、已确认方案、工单范围、必要测试、Wiki 同步、Git 提交和验收归档要求。
### 严格控制范围
- 默认严格按用户确认的目标和单元任务范围执行,不主动扩展相邻问题。
- 除非任务目标、仓库强制规则或已发现的真实阻塞需要,不新增额外文档、辅助脚本、备份文件、框架、重构或扩展性设计。
- 不执行与本次验收无关的验证;安全检查、受影响范围测试、回归测试和仓库规定的闭环验证不属于“额外验证”。
- 新发现的相邻问题最多用一句话提示或记录到独立工单,不自动修复或混入当前提交。
### 渐进执行和修复
- 完成已知必要的安全与前置检查后,优先执行能够产生真实反馈的最小命令。
- 一次执行后先处理首个可定位、可行动的真实错误,不同时猜测并修改多个可能原因。
- 采用“执行 → 查看错误 → 最小修复 → 从失败点继续或按需重跑”的闭环。
- 不在真实证据出现前堆叠与已知风险无关的预防性检查。
- 涉及凭据、权限、安全、数据、迁移、并发、删除、发布或不可逆操作时,必须先完成相应前置检查,不得通过试错获取风险反馈。
### 复用已验证事实
- 在同一任务和同一环境状态下,已经通过的路由、连接、恢复和环境检查不重复执行。
- 只有会话、环境、代码、配置、依赖、凭据、远端状态或关键前提发生变化时才重新检查。
- 代码修改后,受影响测试和最终验收必须重新执行;提交前工作区检查、推送前远端分支检查不得因为之前通过而省略。
- Skill 和平台规则是否需要重新读取,按当前 Agent 平台和任务触发规则执行,不自行跳过。
### 明确停止条件
- 完成用户确认的验收标准和仓库规定的必要闭环后立即停止,不主动继续优化。
- “最小验收条件”包括当前工单要求的实现、必要测试、文档影响处理、Wiki 镜像检查、提交和证据回写,不等同于功能第一次运行成功。
- 未影响当前验收的相邻问题只提示或建单,不顺手处理。
## 自然语言快捷指令
快捷指令是对本工作流的自然语言别名,供 Claude Code、Codex 和维护者使用。它们只减少重复描述,不得绕过方案确认、前置依赖、安全规则、工单范围、Wiki 主源、必要验证或人工验收。
| 指令 | 执行动作 | 停止位置 |
|---|---|---|
| `只分析` | 只读检查需求、代码、日志和文档,区分事实与假设并给出方案 | 输出方案并等待确认;不建单、不修改 |
| `建工单` | 根据已经确认的方案创建单元任务工单 | 工单创建并记录完成;不修改代码 |
| `执行工单 #N` | 读取工单和前置依赖,实施、测试、提交、更新 Wiki、导出镜像、推送并回写证据 | 工单保持“待验收” |
| `建工单并做` | 依次执行“建工单”和“执行工单”;`建工单,做`、`建工单,做` 含义相同 | 工单保持“待验收” |
| `继续工单 #N` | 核对工单、Git 和 Wiki 证据,从首个未完成步骤继续,不重复仍然有效的检查 | 到达该工单当前流程的停止条件 |
| `检查工单 #N` | 只读对照范围、验收标准、测试和证据,报告通过项、缺失项及未验证部分 | 输出检查报告;不自动修复 |
| `同步文档` | 读取 Wiki,导出核心长期文档镜像并检查一致性,不处理任务归档 | 显示结果和差异;不修改 Wiki、不自动提交 |
| `导出任务归档` | 人工触发增量导出,只写入新增或 revision 已变化的任务归档 | 显示导出或跳过结果;不删除本地文件、不自动提交 |
| `导出全部任务归档` | 人工触发全量读取并导出线上全部任务归档 | 显示导出结果;不删除本地文件、不自动提交 |
| `#N 验收通过` | 记录明确验收,更新 Wiki 归档为“已完成”,同步必要的核心文档,推送、同步父工单并关闭任务;不自动导出任务归档 | 工单“已完成”并关闭 |
补充边界:
- 方案未确认时,`建工单`、`建工单并做` 和 `执行工单 #N` 不得绕过确认;Agent 应停在方案确认。
- 前置依赖未满足且不允许并行时,实施类指令停在“待实施”。
- `#N 验收通过` 必须来自用户明确表达;其他快捷指令不得关闭待验收工单。
- `同步文档` 或任务归档导出发现目标镜像有未提交改动时停止,不覆盖现有修改。
- `导出任务归档` 和 `导出全部任务归档` 必须由用户明确提出,其他快捷指令不隐式执行。
- Gitea 工单保留讨论和过程,不把工单全文导出到本地;`docs/task/` 只保存人工按需导出的 Wiki 最终任务归档快照。
## 什么时候重新确认方案 ## 什么时候重新确认方案
@@ -56,17 +237,18 @@ python scripts/new_task_archive.py 123 "修复登录超时"
- 增加或删除接口、数据库字段或迁移; - 增加或删除接口、数据库字段或迁移;
- 安全边界、权限或不可逆操作发生变化; - 安全边界、权限或不可逆操作发生变化;
- 原方案不可行,需要更换主要技术路线; - 原方案不可行,需要更换主要技术路线;
- 任务范围明显扩大。 - 任务范围明显扩大;
- Wiki 页面删除、重命名或事实源边界改变。
普通内部实现细节不需要反复确认,但重要取舍应记录在工单中。 普通内部实现细节不需要反复确认,但重要取舍应记录在工单中。
## 工单与文档分别写什么 ## 工单、Wiki 与 Git 分别写什么
| 信息 | Gitea 工单 | `docs/task` | | 信息 | Gitea 工单 | Gitea Wiki | Git / `docs` 镜像 |
|---|---:|---:| |---|---:|---:|---:|
| 讨论过程和临时方案 | 是 | 否 | | 讨论过程和临时方案 | 是 | 否 | 否 |
| 实施进度和阻塞 | 是 | 否 | | 实施进度和阻塞 | 是 | 否 | 否 |
| 最终实现方案 | 是 | 是 | | 长期有效的最终方案 | 链接 | 是 | 镜像 |
| 测试结果与未验证内容 | 是 | 是 | | 测试结果与未验证内容 | 是 | 任务归档 | 按需镜像 |
| 提交哈希 | 是 | 是 | | 提交哈希 | 是 | 任务归档 | 按需镜像 |
| 长期有效的最终结论 | 可链接 | 是 | | 与具体代码版本绑定的说明 | 可链接 | 提供入口 | 是 |
+88
View File
@@ -0,0 +1,88 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Architecture-and-Code-Map
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Architecture-and-Code-Map.-
wiki_revision: f93132ddd5bbc8fe233270579a63fc182db3b795
synchronized_at: 2026-08-08T01:16:49Z
<!-- gitea-wiki-mirror:end -->
# 架构与代码地图
## 本页用途
帮助第一次接触项目的人回答三个问题:
1. 项目由哪些部分组成;
2. 一个功能应该从哪里开始读;
3. 修改后应该运行哪些验证。
阅读代码前先看本页;目录、入口或主要数据流变化时必须更新本页。
## 项目定位
DevHarness 不是业务应用,而是一套开发工作流模板。它约束 Agent 和维护者如何讨论需求、建立工单、修改代码、更新 Wiki、测试、提交、验收和归档。
```text
用户确认方案
→ Gitea 单元任务工单
→ Agent 修改代码与测试
→ 长期结论更新 Wiki
→ Wiki 单向导出 docs 镜像
→ Git 提交并回写工单
→ 用户验收
```
## 代码地图
| 能力 | 路径 | 阅读入口 | 主要对象或函数 | 验证位置 | 风险 |
|---|---|---|---|---|---|
| Agent 工作规则 | `AGENTS.md` | “需求到实施” | 工作流条款 | 人工审查、Harness 检查 | 高 |
| 工单结构 | `.gitea/issue_template/` | `task.md` | Epic、MVP、Task 模板 | 创建测试工单或检查模板 | 中 |
| Wiki 页面映射 | `wiki-docs.json` | `mappings` | 页面名、本地路径 | `sync_wiki_docs.py --check` | 中 |
| Wiki API 和镜像生成 | `dev_scripts/wiki_docs.py` | `WikiClient`、`sync_all` | 配置、页面、镜像元数据 | `tests/test_wiki_docs.py` | 中 |
| 手动同步入口 | `dev_scripts/sync_wiki_docs.py` | `main()` | `--check` | 线上 Wiki 对照检查 | 低 |
| 任务归档 | `dev_scripts/new_task_archive.py` | `main()` | 创建页面、登记映射 | 单元测试和正式归档 | 中 |
| Harness 结构检查 | `dev_scripts/check_harness.py` | `main()` | 必需文件、镜像、归档检查 | `--strict` | 中 |
| 本地文档镜像 | `docs/` | `docs/README.md` | 生成元数据和 Wiki 正文 | 同步检查 | 低 |
| 自动化测试 | `tests/` | `test_wiki_docs.py` | 映射、同步和安全边界 | `unittest discover` | 低 |
## 两条主要执行路径
### Wiki 镜像
```text
wiki-docs.json
→ WikiClient 列出并解析页面
→ 读取 Markdown 与 last_commit.sha
→ 检查本地镜像是否有未提交修改
→ 写入来源、URL、revision、同步时间
→ --check 对照正文和 revision
```
### 任务归档
```text
读取 Wiki 归档模板
→ 创建 Task-<编号>-<标题> 页面
→ 追加显式页面映射
→ 导出 docs/task 镜像
→ 提交镜像并回写工单
```
## 修改影响判断
| 修改内容 | 通常还要检查 |
|---|---|
| 修改 Agent 工作流 | `README.md`、`CLAUDE.md`、Development-Workflow、工单模板 |
| 修改 Wiki 页面名称 | `wiki-docs.json`、Home 链接、同步测试;必须人工确认 |
| 修改镜像格式 | 解析器、检查器、已有镜像、单元测试 |
| 增加核心文档 | Wiki、显式映射、Home、Harness 必需页面检查 |
| 修改归档字段 | Wiki 归档模板、归档脚本、归档检查和测试 |
## 不可破坏的边界
- 工单管理过程,Wiki 管理长期文档,Git 管理代码和镜像。
- `docs/` 不是长期文档编辑入口。
- 同步只允许写入 `docs/` 下的 Markdown。
- 页面删除、重命名和本地脏镜像不能被静默处理。
- 凭据不得进入代码、Wiki、工单、日志或镜像。
+68
View File
@@ -0,0 +1,68 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Business-Rules-and-Glossary
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Business-Rules-and-Glossary.-
wiki_revision: f78f2f92f563ec8dce5e399d4ee2b37896fa98dd
synchronized_at: 2026-08-10T03:51:38Z
<!-- gitea-wiki-mirror:end -->
# 业务规则与术语
## 本页用途
解释 DevHarness 中容易混淆的术语、状态和不可破坏的流程规则。新项目复制模板后,应把项目自身的业务术语、状态和关键约束补充到本页。
## 核心术语
| 术语 | 含义 | 不要误解为 |
|---|---|---|
| Epic | 完整产品目标和长期路线 | 可以直接实施的单个任务 |
| MVP | 第一个可交付范围及集成边界 | 任意里程碑名称 |
| 单元任务 | 唯一正式实施单位,可独立测试和回退 | 临时聊天待办 |
| 关键原始需求 | 能表达用户目的、场景和限制的少量原话或脱敏摘要 | 完整聊天记录 |
| 正式任务需求 | 用户确认后写入单元工单的目标、非目标、方案和验收标准 | Agent 未确认的理解 |
| 需求变化记录 | 实施期间影响范围或验收的变化、原因及用户确认 | 每一句普通讨论 |
| 事实来源 | 某类信息被正式维护的位置 | 多处内容可以随意覆盖 |
| Wiki 主源 | 长期开发文档首先修改的位置 | 本地 docs 的备份副本 |
| docs 镜像 | 从 Wiki 单向生成的浏览副本 | 可以直接编辑并反向同步的文档 |
| 待验收 | 实现和测试已完成,等待用户确认 | 已完成并可关闭 |
| 未验证部分 | 本次无法真实覆盖的行为 | 可以省略的测试备注 |
## 工单状态
| 状态 | 含义 | 可以进入下一状态的条件 |
|---|---|---|
| 待确认 | 目标或方案仍需用户选择 | 用户明确确认方案 |
| 待实施 | 方案已确认但尚未修改,或真实前置依赖尚未满足 | 前置依赖已满足或明确允许并行,且工作区和范围检查完成 |
| 进行中 | 正在实现、测试或同步文档 | 验收标准逐项检查完成 |
| 阻塞 | 实施过程中出现计划外、当前无法解除的问题 | 阻塞解除并更新工单 |
| 待验收 | 代码、测试、归档和证据已完成 | 用户明确验收 |
| 已完成 | 用户已验收并完成父任务同步 | 无 |
## 稳定业务规则
- 没有确认方案和单元任务工单,不修改产品行为。
- 一个单元任务只解决一个可独立验证和回退的问题。
- 建立后续工单不要求已有工单全部完成;实施前必须检查工单声明的前置依赖。
- 前置工单未完成且存在实际依赖时保持“待实施”;允许并行时必须写明原因。
- 需求、接口、数据、安全边界或验收标准变化时先更新工单。
- 工单只保存关键原始需求、确认后的正式需求和重要变化,不保存完整聊天或 Agent 内部推理。
- 长期有效的产品需求和业务规则进入 Wiki;Gitea 工单全文不导出到本地。
- 长期文档必须先修改 Wiki,再导出本地镜像。
- 测试结果必须真实;未执行的验证必须明确记录。
- 用户未明确验收前,工单保持开启。
- 初级程序员可以理解和验证低风险修改,但高风险决策仍由 Agent 分析并等待人工确认。
## 新项目需要补充什么
复制模板后,至少补充:
- 项目的用户和核心目标;
- 业务名词及容易混淆的概念;
- 主要对象和状态;
- 关键状态流转;
- 必须始终满足的业务规则;
- 数据保留、权限和安全边界;
- 典型输入、输出和失败示例。
业务规则必须由项目负责人确认,Agent 可以整理和举例,但不能根据代码自行臆造。
@@ -0,0 +1,85 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Local-Development-and-Verification
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Local-Development-and-Verification.-
wiki_revision: b31ef962e1a0a368f067b18bace3071bab60a20d
synchronized_at: 2026-08-08T01:18:31Z
<!-- gitea-wiki-mirror:end -->
# 本地开发与验证
## 本页用途
让维护者能够安装、运行、检查和验证项目。所有命令默认在仓库根目录执行,示例以 Windows PowerShell 为主。
## 环境要求
| 工具 | 用途 | 检查命令 |
|---|---|---|
| Git | 版本管理和脏文件保护 | `git --version` |
| Python 3 | Harness 脚本和测试 | `python --version` |
| Gitea 连接 | 工单和 Wiki | 浏览仓库或调用 MCP |
| Gitea PAT | 写 Wiki 时使用 | 仅通过 MCP 安全配置或 `GITEA_TOKEN` 提供 |
不要打印或提交 PAT。
## 第一次运行
### 1. 检查工作区
- 目的:确认没有混入其他任务的修改。
- 命令:`git status --short --branch`
- 预期:显示当前分支;开始新任务时没有无关文件。
- 失败检查:确认变更归属,不要擅自重置或覆盖。
### 2. 检查 Harness
- 目的:验证必需文件、项目档案、Wiki 映射和归档结构。
- 命令:`python dev_scripts/check_harness.py --strict`
- 预期:输出“DevHarness 检查通过”。
- 失败检查:按错误提示检查缺失页面、未填占位符或损坏的镜像头。
### 3. 运行测试
- 目的:验证同步、路径和安全保护。
- 命令:`python -m unittest discover -s tests -v`
- 预期:所有测试显示 `ok`。
- 失败检查:先单独运行失败测试,再查看最近修改的对应脚本。
### 4. 对照线上 Wiki
- 目的:确认本地 docs 是最新镜像。
- 命令:`python dev_scripts/sync_wiki_docs.py --check`
- 预期:所有映射显示“一致”。
- 失败检查:先读取线上页面;确认页面名、revision、网络和 `GITEA_URL`。
## 常用调试方式
- 只检查 Python 语法:`python -m compileall -q dev_scripts tests`。
- 查看一个脚本帮助:`python dev_scripts/sync_wiki_docs.py --help`。
- 查看未提交差异:`git diff --check` 和 `git diff`。
- 查看最近提交:`git log -5 --oneline`。
- 调试失败测试时优先运行单个测试文件,不要先修改多个模块。
## 测试数据与日志
DevHarness 不使用生产数据,也不需要固定业务测试数据。命令输出是主要诊断信息,不应包含令牌。如果复制到业务项目,应在本节写明:
- 合成或脱敏测试数据的创建方式;
- 日志路径和日志级别;
- 请求或任务标识如何追踪;
- 禁止使用的数据来源。
## 完成修改前
依次执行:
```powershell
python -m unittest discover -s tests -v
python dev_scripts/check_harness.py --strict
python dev_scripts/sync_wiki_docs.py --check
git diff --check
git status --short
```
无法执行的命令必须写入工单“未验证部分”。
+78
View File
@@ -0,0 +1,78 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Common-Changes
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Common-Changes.-
wiki_revision: 9b223d2feada753a6d38bffce6eb0848c4067b85
synchronized_at: 2026-08-08T01:16:55Z
<!-- gitea-wiki-mirror:end -->
# 常见修改指南
## 本页用途
帮助初级程序员在 Claude/Codex Agent 协助下处理简单 Bug 和小需求。这里说明常见入口、验证方法和停止条件,不代替工单和方案确认。
## 风险分级
| 等级 | 常见修改 | 处理方式 |
|---|---|---|
| 低风险 | 文案、简单校验、查询条件、独立 UI、小范围回归 Bug | 初级程序员可在 Agent 协助下修改和验证 |
| 中风险 | API、配置、依赖、跨模块逻辑、数据结构 | 由 Agent 实现,程序员理解差异并执行验证 |
| 高风险 | 权限、安全、并发、迁移、支付、删除数据、不可逆操作 | 停止修改,由 Agent 分析并等待人工确认 |
“代码行数少”不等于低风险。
## 修改 Wiki 文案
1. 在相关工单确认目标。
2. 读取线上 Wiki 页面和当前 revision。
3. 修改线上 Wiki,不直接编辑 `docs/`。
4. 运行 `python dev_scripts/sync_wiki_docs.py`。
5. 运行 `python dev_scripts/sync_wiki_docs.py --check`。
6. 审查本地镜像差异并提交。
停止条件:页面需要删除、重命名或改变事实源边界。
## 增加工单字段
1. 阅读 `.gitea/issue_template/task.md` 和 Development-Workflow。
2. 判断字段是否影响所有任务,避免只为一个任务增加永久字段。
3. 修改模板和对应流程说明。
4. 为 Harness 检查增加或调整测试。
5. 创建一份示例工单草稿检查可读性。
停止条件:字段改变权限、审批或关闭条件。
## 调整 Harness 检查
1. 从 `dev_scripts/check_harness.py` 的 `main()` 开始读。
2. 新检查应输出具体文件和缺失内容。
3. 检查结构事实,不声称自动判断文档语义质量。
4. 在 `tests/` 添加成功和失败用例。
5. 运行严格检查及全部测试。
停止条件:检查会删除、重写文件或依赖生产环境。
## 修复 Wiki 同步 Bug
1. 从 `dev_scripts/wiki_docs.py` 的 `WikiClient`、`parse_mirror` 和 `sync_all` 开始读。
2. 先编写能复现问题的测试。
3. 保持 Wiki → docs 单向关系。
4. 验证中文、路径编码、revision 和脏文件保护。
5. 使用测试页面验证时,不删除正式页面。
停止条件:需要自动删除/重命名页面、覆盖本地未提交修改或输出令牌。
## 看懂 Agent 的修改
审查时至少回答:
- 这次解决了哪个工单目标;
- 修改入口和调用路径在哪里;
- 有哪些行为变化;
- 增加或修改了哪些测试;
- 哪些内容没有验证;
- 是否更新了受影响的 Wiki 页面;
- 怎样回退。
回答不了时,让 Agent补充说明,不要仅凭“测试通过”验收。
+40
View File
@@ -0,0 +1,40 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Troubleshooting
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Troubleshooting
wiki_revision: e1a806bb8c6b6c32a4e271c94a9bcaaed36003db
synchronized_at: 2026-08-08T00:58:12Z
<!-- gitea-wiki-mirror:end -->
# 故障排查
## 本页用途
按“现象 → 原因 → 检查 → 处理”定位 DevHarness 常见问题。处理后如果形成稳定结论,应更新本页;临时过程记录在工单。
| 现象 | 常见原因 | 检查方法 | 处理 |
|---|---|---|---|
| `--strict` 提示项目档案未填写 | 新项目仍有占位内容 | 搜索 `<填写` | 先在 Wiki 填写真实内容,再导出镜像 |
| 同步提示镜像有未提交改动 | 有人直接修改 docs,或上次镜像尚未提交 | `git status --short -- docs` | 确认来源;保留人工内容并先更新 Wiki,不要强制覆盖 |
| Wiki 页面不存在 | 页面未创建、标题或映射错误 | 查看 Wiki 页面列表和 `wiki-docs.json` | 修正明确的页面或映射;不要自动删除本地文件 |
| API 路径出现重复 `/api/v1` | `GITEA_URL` 已包含 API 后缀 | 查看非敏感 URL 配置 | 同步器会规范化;新工具也应接受两种写法 |
| 公共仓库读取返回 401/403/404 | 环境令牌失效或属于其他实例 | 不打印令牌;尝试浏览公开页面 | 只读请求可安全降级匿名;写请求必须使用正确 PAT |
| 中文 Wiki 页面读取 404 | `sub_url` 被重复百分号编码 | 查看页面列表返回的 `sub_url` | 保留已有 `%`,不要再次编码 |
| Wiki 页面标题多出 `.-` | Gitea 1.25 的页面规范路径或更新时未显式传标题 | 对照页面 title 和 `sub_url` | 更新中文页面时显式保留原 title;不要猜测路径 |
| `--check` 正文不一致 | Wiki 已更新但镜像未导出,或本地被修改 | 对照 revision 和 Git 差异 | 确认 Wiki 后运行正式同步 |
| 单元测试能过但真实同步失败 | 测试使用模拟数据,网络或 Gitea 行为不同 | 查看工单“未验证部分” | 增加最小真实验证并记录服务端版本 |
| Git 工作区包含无关修改 | 同时存在其他任务或人工工作 | `git status --short` | 保留并隔离无关修改,不重置用户工作 |
## 排查顺序
1. 读取完整错误信息,不只看最后一行。
2. 检查当前工单、分支和工作区。
3. 检查项目档案中的真实命令和环境。
4. 用最小命令复现。
5. 对照最近提交和 Wiki revision。
6. 修复后增加回归测试或稳定排错条目。
7. 无法验证的部分写回工单。
## 必须停止的情况
出现凭据泄露、数据损坏风险、权限边界变化、不可逆操作或不明来源的工作区改动时,立即停止并说明影响,不继续尝试破坏性修复。
+191
View File
@@ -0,0 +1,191 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: New-Project-Documentation-Setup
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/New-Project-Documentation-Setup.-
wiki_revision: 864d81b515b420d17fa4ff0fc7f528109b1a3d99
synchronized_at: 2026-08-16T11:38:04Z
<!-- gitea-wiki-mirror:end -->
# 新项目文档初始化
## 本页用途
从 DevHarness 创建新项目时,指导 Claude/Codex Agent快速建立可供初级程序员阅读的项目文档。初始化只生成可靠的第一版,不允许 Agent 臆造业务规则、凭据、部署环境或生产数据。
## 初始化顺序
### 1. 建立项目边界
由项目负责人确认:
- 项目名称和一句话目标;
- 用户和主要使用场景;
- 技术栈和支持环境;
- Gitea 仓库、默认分支和维护者;
- 安全、权限、数据和发布红线。
把项目专用红线写入根目录或子目录 `AGENTS.md`。
### 2. 选择建设基线
确定技术方案前,优先评估是否存在功能和架构匹配、持续维护、许可证兼容且工程流程完善的开源项目。这里要求的是“先评估”,不是强制采用开源项目,也不能只根据知名度、Star 数量或演示效果决定。
至少检查:
- 核心功能、架构和支持平台是否匹配,哪些能力可以直接保留;
- 许可证是否允许预期的使用、修改、分发和商业模式;不确定时交由负责人或法律专业人员确认;
- 最近维护活跃度、发布频率、Issue 处理和社区或维护团队的持续性;
- 已知安全问题、依赖健康度、供应链风险和安全响应方式;
- 自动化测试、CI、发布、升级、回退和文档是否足以支持长期维护;
- 定制、学习、迁移和后续跟踪上游的总成本是否低于从零开发;
- 是否能够固定上游仓库和基线版本,并建立合并上游更新、兼容验证和退出方案。
满足适配、许可证、安全、维护和总成本条件时,优先在该基线上二次开发。不存在合适基线,或引入后会增加不可接受的许可证、安全、架构或维护风险时,可以从零开发,但必须记录排除候选项目和选择从零开发的主要原因。
采用开源基线时,在 Project-Profile 的“技术栈与运行环境”记录上游项目名称、仓库地址、基线版本或提交、许可证、保留能力、定制范围和上游升级策略。尚未确认的候选和取舍先写入首个技术方案工单,不得把假设写成项目事实。
#### 判断案例
以下案例用于说明判断方式,不代表必须选择某种技术或具体开源项目。
##### 案例一:适合基于成熟项目二次开发
计划开发企业内部管理系统。候选项目已经具备用户、权限、审计日志、基础数据管理和自动化测试;功能与目标架构基本匹配,许可证允许预期使用,项目持续维护,发布与升级流程完整,预计只需修改业务模块和界面。
- 结论:优先基于该项目二次开发。
- 原因:可以减少通用功能的开发和验证成本,定制范围可控。
- 记录:上游仓库、基线版本、许可证、保留功能、定制模块和上游升级方式。
##### 案例二:项目成熟但许可证不兼容
候选项目功能完整、维护活跃、文档充分,但许可证与当前产品的闭源分发、商业模式或交付条件不兼容。
- 结论:不采用该项目作为建设基线。
- 原因:技术成熟度不能消除许可证风险;不确定结论必须交由负责人或法律专业人员确认。
- 记录:候选项目、许可证限制、确认人员和排除原因。
##### 案例三:功能相似但改造成本过高
候选项目表面上覆盖大部分功能,但数据模型、权限体系和部署结构与目标项目差异很大,需要大量删除模块、重写主要接口,并长期维护上游冲突。
- 结论:不直接基于完整项目二次开发,可以评估只复用合适的组件或设计思路。
- 原因:二次开发的总成本、理解成本和长期维护风险已经高于自主实现核心业务。
- 记录:主要结构差异、改造估算、长期维护风险和最终选择。
##### 案例四:只复用成熟框架或组件
没有功能高度匹配的完整开源产品,但存在成熟的应用框架、更新组件、日志组件或通信库。
- 结论:从零开发业务功能,同时复用经过评估的成熟框架或组件。
- 原因:复用基础能力不等于必须采用完整产品,可以避免被不匹配的业务架构绑定。
- 记录:每个依赖的用途、版本、许可证、安全边界、升级方式和可替换方案。
每个案例的实际评估都必须记录候选项目、判断依据、最终选择、未采用原因,以及升级或退出方式。
### 3. 识别子项目与交付单元
先判断仓库中有几个应用、服务、客户端、库或其他可独立交付的部分。对每个部分确认:
- 职责和目录边界;
- 技术栈、依赖和支持环境;
- 构建、测试和运行命令;
- 是否拥有独立版本号和发布方式;
- 适用的根目录或子目录 `AGENTS.md`;
- 与其他子项目共享的接口、数据或业务流程;
- 共享契约的唯一事实来源和兼容要求。
把结果写入 Project-Profile 的“子项目与交付单元”。单应用项目只填写一个交付单元;多应用单仓库为规则不同的目录增加子目录 `AGENTS.md`,但不因为技术栈不同自动拆仓,也不强制统一版本和发布周期。
### 4. 建立 Gitea
创建远端仓库并完成允许的初始引导提交。开启工单和 Wiki。任何产品功能开发在引导提交后都必须先有单元任务工单。
### 5. 修改镜像配置
把 `wiki-docs.json` 中的地址、owner 和 repository 改成新项目;只保留核心主题映射,任务归档不逐页登记。
确认当前目录确实是新项目副本、且 DevHarness 历史归档快照不需要保留后,可以移除对应 `docs/task/` 文件。不要在原 DevHarness 仓库或已有业务项目中执行这项清理。
不要把 PAT 写入配置。
### 6. Agent 检查项目事实
Agent 只读检查:
- README、配置和依赖文件;
- 启动入口;
- 主要模块和目录规则;
- 测试、格式和静态检查命令;
- 日志、示例配置和测试数据;
- 已存在的接口、数据模型和状态。
区分“代码中确认的事实”“负责人确认的业务规则”和“仍待确认的假设”。
### 7. 确定交付对象和文档
由项目负责人确认哪些岗位或客户会实际使用、部署、管理、支持、集成或验收产品,并为每类对象确定:
- 需要完成的工作;
- 所需文档类型;
- 文档可见范围;
- 适用版本、负责人和验证人;
- 不得对外披露的内部信息。
按照[交付文档指南](Delivery-Documentation-Guide.-)选择文档,使用[岗位文档模板](Audience-Document-Template.-)按需创建。没有明确读者的文档不创建,不预建空白的用户手册、管理员手册或运维手册。
### 8. 先创建线上 Wiki
至少创建或填写:
1. Home;
2. Project-Profile;
3. Architecture-and-Code-Map;
4. Business-Rules-and-Glossary;
5. Local-Development-and-Verification;
6. Common-Changes;
7. Troubleshooting;
8. Development-Workflow;
9. Delivery-Documentation-Guide;
10. Audience-Document-Template;
11. Task-Archive-Template。
Home 给出建议阅读顺序;每个命令必须有预期结果;代码地图必须指出入口和测试位置。具体岗位文档仅按第 7 步确认的受众创建。
### 9. 人工确认
项目负责人至少确认:
- 一句话目标和业务术语;
- 关键业务规则和状态;
- 权限、安全和数据边界;
- 真实运行、测试和部署命令;
- 哪些修改属于高风险;
- 交付对象、文档可见范围和外部信息边界。
### 10. 导出镜像并检查
```powershell
python dev_scripts/sync_wiki_docs.py
python dev_scripts/check_harness.py --strict
python dev_scripts/sync_wiki_docs.py --check
python -m unittest discover -s tests -v
```
只有线上 Wiki 确认后才导出核心 `docs/`。任务归档默认不导出;用户明确要求时再运行 `python dev_scripts/export_task_archives.py` 或加 `--all`。旧项目的任务归档快照不能带入新项目历史。
## 完成标准
初级程序员应能仅依靠 Home 和链接页面回答:
- 项目解决什么问题;
- 怎样启动和运行测试;
- 常用功能从哪个目录和入口开始读;
- 一个简单修改通常要改哪里、验证什么;
- 哪些情况必须停止并交给 Agent 或负责人;
- 项目采用哪个建设基线,为什么适合二次开发,或者为什么选择从零开发;
- 采用开源基线时,上游仓库、基线版本、许可证、定制范围和升级策略是什么;
- 项目包含哪些子项目和独立交付单元,各自怎样构建、测试和发布;
- 跨子项目共享什么接口或契约,其唯一事实来源在哪里;
- 项目需要向哪些岗位交付什么文档,以及哪些内容不能对外提供。
回答不了的问题应继续补充主题文档,而不是堆入任务归档。
+235
View File
@@ -0,0 +1,235 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Existing-Project-Adoption-Guide
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Existing-Project-Adoption-Guide.-
wiki_revision: 2ef3d7cd26874f652e9bfab2a5c2f6c8273ab7ac
synchronized_at: 2026-08-16T11:50:18Z
<!-- gitea-wiki-mirror:end -->
# 已有项目接入 DevHarness 指南
## 本页用途
本页用于把 DevHarness 的文档模板和开发流程增量接入已经存在的项目。已有项目通常已经有代码、规则、文档、工单、Wiki、Git 历史和未完成工作,因此接入目标是补齐必要能力,不是把项目重置成 DevHarness 模板副本。
接入必须先只读盘点、确认差异方案,再建立单元任务工单实施。未经确认不得覆盖、删除、重命名或批量迁移已有内容。
## 与新项目初始化的区别
| 场景 | 新项目初始化 | 已有项目接入 |
|---|---|---|
| 项目事实 | 从代码骨架和负责人确认开始建立 | 优先保留并核对已有事实 |
| 规则文件 | 可以从模板建立第一版 | 必须合并已有规则,不能直接覆盖 |
| 文档 | 创建核心主题页 | 逐页判断保留、迁移、合并或停止维护 |
| 工单和 Wiki | 新建并开始使用 | 先检查已有工单、Wiki 和状态体系 |
| Git 历史 | 允许一次引导提交 | 保留全部历史,不使用引导提交例外 |
| 任务归档 | 从新项目任务开始 | 不复制 DevHarness 或其他项目的历史归档 |
| 接入方式 | 一次建立最小骨架 | 分阶段增量接入并逐步验收 |
从模板创建全新仓库时使用[新项目文档初始化](New-Project-Documentation-Setup.-);项目已有业务提交、用户或维护历史时使用本页。
## 接入前只读盘点
Agent 在提出方案前只读检查:
- 根目录和相关子目录中的 `AGENTS.md`、`CLAUDE.md` 及其他 Agent 规则;
- README、现有 `docs/`、Wiki 页面、工单模板和任务状态;
- Git 默认分支、远端、提交历史、未提交修改和忽略规则;
- 语言、框架、依赖、启动入口、主要模块和目录职责;
- 格式检查、静态检查、单元测试和必要集成测试命令;
- 配置、日志、接口、数据模型、权限、安全、部署和发布边界;
- 已完成、进行中、阻塞和待验收任务;
- 现有长期文档的事实来源、负责人和更新方式。
输出时区分:
1. 从代码、配置或现有系统确认的事实;
2. 项目负责人确认的业务规则;
3. 尚待确认的假设;
4. DevHarness 与现有规则的冲突;
5. 与接入无关、必须保留的工作区改动。
只读盘点不授权修改文件、创建 Wiki、迁移文档或改变工单状态。
## 已有内容保护原则
- 保留 Git 历史、分支、标签和当前任务状态。
- 保留已有 `AGENTS.md`、README、规则和项目专用红线;DevHarness 规则按冲突结果增量合并。
- 保留与接入无关的未提交改动,不重置、不覆盖、不混入提交。
- 不复制 DevHarness 的 `docs/task/`、任务归档映射和历史工单。
- 不因采用 Wiki-first 就立即删除原本地文档;先逐页确认事实来源和迁移状态。
- 不把模板占位值当成项目事实,不臆造技术栈、命令、业务规则、凭据或环境。
- 不把密码、令牌、Cookie、私钥、个人数据或生产数据带入工单、Wiki和镜像。
- 页面删除、重命名、历史清理和事实来源切换必须单独确认。
## 多应用单仓库判断
一个 Git 仓库可以包含多个技术栈不同、能够独立构建和发布的应用或终端。技术栈不同本身不是拆仓理由;先把每个子项目和交付单元记录到 Project-Profile,再根据实际协作边界判断。
### 适合继续单仓库
- 多个应用共同完成一条产品或业务链路;
- 由同一团队维护,仓库权限基本一致;
- 接口变更需要在一个工单中同步修改或验证多端;
- 共享契约、业务规则和任务归档放在一起更容易保持一致;
- 仓库体积、测试时间和工具性能尚未明显影响开发;
- 初级维护者和 Agent 能通过目录、子目录 `AGENTS.md` 和文档入口清楚定位。
### 可以考虑拆仓
- 长期由不同团队独立负责并需要不同访问权限;
- 发布周期、版本策略和验收负责人已经完全独立;
- 某个应用被多个产品复用或需要单独对外提供;
- 仓库体积、检出、索引或测试耗时已经持续影响效率;
- 共享接口已经版本化、兼容周期明确,并有跨仓契约测试;
- 跨应用任务很少,拆仓后的协调成本低于继续共仓。
不满足这些条件时,优先保持单仓库并完善边界,不为了目录整洁或技术栈不同而拆仓。
### 保持单仓库时的最小规则
- 根目录 `AGENTS.md` 只放共同流程、安全和跨项目规则,技术栈专用规则写入子目录 `AGENTS.md`。
- 每个交付单元拥有自己的构建、测试、版本和发布方式,不强制统一版本。
- 单元任务必须声明只影响哪个子项目、是否跨子项目、是否修改共享接口,以及各端需要执行的验证。
- 共享接口或契约只能指定一个事实来源;其他文档引用它,不复制一个“差不多”的版本。
- 跨子项目契约变更在同一工单中更新事实来源,并验证所有受影响端。
- 拆仓属于事实来源、任务和发布边界变化,必须另建工单、确认迁移和回退方案后实施。
## 增量接入顺序
### 1. 确认差异方案
根据盘点结果列出目标、非目标、复用项、改写项、冲突项、影响范围、风险、回退、验证和文档影响。方案得到用户明确确认前不实施。
### 2. 建立单元任务工单
使用目标项目的 Gitea 建立接入工单,记录原始需求、范围、依赖、方案和验收标准。目标项目没有可用 Gitea 时,先提交完整工单草稿并说明阻塞,不默认绕过。
### 3. 接入共同规则和工单流程
优先增量合并根规则、Claude 入口和单元任务模板。项目专用安全、业务和目录规则继续有效;冲突时由负责人决定最终表述。
### 4. 确定长期文档事实来源
为每份已有文档标记:
- 保留在 Git:与特定代码版本强绑定;
- 迁移到 Wiki:长期架构、业务规则、开发规范或操作说明;
- 合并:内容重复但各有有效事实;
- 暂不迁移:事实未确认或当前不影响接入;
- 停止维护:必须由负责人确认,不能由 Agent 自行删除。
切换到 Wiki-first 的页面必须先在线上创建或更新、读取确认,再建立 `wiki-docs.json` 映射并导出本地镜像。避免 Wiki 和手写本地文档长期形成双事实源。
### 5. 接入 Harness 工具
仅复制当前项目实际需要的 `dev_scripts/` 工具、配置和测试。业务脚本使用独立目录。根据目标项目调整核心页面、路径、命令和结构检查,不照搬 DevHarness 项目值。
### 6. 分阶段验证
先验证工单和规则入口,再验证 Wiki 映射,最后启用严格检查。每阶段采用“执行 → 首个真实错误 → 最小修复 → 继续”的闭环,不用一次接入全部旧文档。
### 7. 提交和验收
提交只包含当前接入工单相关文件。记录测试、未验证部分、Wiki revision 和提交哈希,创建任务归档并保持工单“待验收”,等待用户明确验收后再关闭。
## 后续升级
已接入的项目必须以 Project-Profile 中记录的 DevHarness 来源和当前基线为起点升级,不得重新复制整个模板,也不得用“最新版本”代替可复现的目标提交。
### 升级步骤
1. 读取目标项目的 Project-Profile,确认 DevHarness 来源仓库、当前基线完整提交、最后升级日期和项目适配说明;字段缺失时先补齐可验证事实,无法确认则停止。
2. 选择一个明确、已审阅的 DevHarness 目标提交,记录旧基线和新基线。先比较两个上游提交之间的变化,再判断这些变化如何作用于目标项目。
3. 只读比较与 Harness 有关的 `AGENTS.md`、`CLAUDE.md`、工单模板、`dev_scripts/`、Harness 测试和核心 Wiki 结构,把差异分为“直接采用、按项目改写、冲突待确认、不采用”。不得把 DevHarness 的项目事实、工单或任务归档带入目标项目。
4. 在目标项目建立单元任务工单,写明升级范围、差异分类、项目专用规则、风险、回退、验证和文档影响。会改变产品行为的内容必须拆成独立任务。
5. 按工单最小合并,保留目标项目更具体的业务、安全、权限和目录规则,以及 Git 历史和无关工作区修改。无法判断哪一方规则有效时停止并等待负责人确认。
6. 长期文档先更新目标项目 Wiki,读取确认后再同步目标项目的核心 `docs/` 镜像;不得用 DevHarness 的本地镜像覆盖目标项目文档。
7. 执行目标项目规定的必要检查和受影响测试,提交并回写证据。工单保持“待验收”。
8. 用户验收通过后,确认目标项目 Project-Profile 已记录新 DevHarness 基线完整提交和升级日期,再关闭工单。升级失败或回退时保留旧基线。
### 升级停止条件
除本页已有的冲突停止条件外,来源仓库与记录不一致、旧基线不存在、目标提交未明确、差异跨越过大而无法可靠分类,或升级需要覆盖项目专用安全规则时,都必须停止并请求确认。可以把升级拆成多个单元任务,但每个任务都要声明最终采用的同一目标基线。
### 可复制升级指令
```text
请把当前项目从 Project-Profile 记录的 DevHarness 基线升级到
<DevHarness 目标完整提交哈希>。
先只读比较来源仓库中“旧基线..目标基线”的 Harness 变化和当前项目
适配,列出直接采用、按项目改写、冲突待确认和不采用的内容,以及
风险、回退、验证和文档影响。不要覆盖项目专用规则、业务文档、Git
历史或无关改动,不复制 DevHarness 工单和任务归档。方案确认后在
当前项目建单并实施;长期文档先改当前项目 Wiki,再同步本地镜像。
工单保持待验收,验收通过后确认 Project-Profile 已记录新基线。
```
路径和目标完整提交哈希必须替换为真实值;目标提交未明确时只分析,不实施。
## 冲突处理和停止条件
出现以下情况时停止实施并请求负责人确认:
- 现有规则与 DevHarness 的安全、权限、事实来源或验收规则冲突;
- 无法判断某份文档应该保留、迁移、合并还是停止维护;
- 需要删除、重命名 Wiki 页面、覆盖已有文件或清理历史归档;
- 需要改变接口、数据库、权限、部署、发布或其他产品行为;
- 工作区存在可能与接入文件重叠的未知修改;
- Gitea、Wiki、凭据或远端权限不可用;
- 真实命令、环境或业务规则无法从证据或负责人确认。
相邻问题最多提示或另建工单,不混入接入任务。
## 可复制 Agent 指令
### 只分析
```text
请把 <DevHarness 路径> 的文档模板和开发流程接入当前已有项目。
先只分析,不修改文件、工单或 Wiki:
1. 阅读 DevHarness 的 AGENTS.md、README.md、项目档案、开发工作流、
新项目文档初始化和已有项目接入指南。
2. 阅读当前项目已有的 Agent 规则、README、docs、Gitea 工单模板、
Wiki 配置、代码入口、测试命令和目录结构。
3. 列出已有规则、文档、任务状态、Git 历史和未提交改动。
4. 对比后列出可复用项、必须改写项、冲突项、旧文档处理方式、
最小接入范围、风险、回退、验证和文档影响。
5. 不复制 DevHarness 任务归档,不覆盖、删除或重命名已有内容,
不把模板占位值当成项目事实。
6. 输出方案后停止,等待我确认。
```
### 方案确认后实施
```text
按照已确认方案建工单并做。
严格按工单范围增量接入 DevHarness,保留当前项目已有规则、历史、
任务状态和无关改动。长期文档先更新 Gitea Wiki,读取确认后再导出
本地 docs 镜像。执行必要测试,提交实现和任务归档,然后把工单保持
为“待验收”;未经我明确验收,不关闭工单。
```
路径、仓库地址和项目名称必须替换为当前环境的真实值。第二段指令只有在第一段方案已经明确确认后使用。
## 最小验收清单
- [ ] 已盘点规则、文档、任务、Git 历史和未提交改动。
- [ ] 已识别所有子项目和独立交付单元。
- [ ] 跨子项目共享契约已经指定唯一事实来源。
- [ ] 已明确复用、改写、冲突和暂不处理内容。
- [ ] 已保留项目专用规则、历史和无关改动。
- [ ] 未复制 DevHarness 历史归档或模板项目事实。
- [ ] 已为每类长期文档明确事实来源和迁移状态。
- [ ] Wiki-first 页面已经读取确认并具有显式镜像映射。
- [ ] Harness 检查已按目标项目调整并通过。
- [ ] 必要测试、未验证部分、提交和归档证据已记录。
- [ ] 工单处于待验收,未提前关闭。
## 回退原则
接入应拆成可回退的小提交。普通回退恢复本次新增或修改的规则、配置、检查和镜像映射,不触碰原有业务提交。Wiki 页面删除、重命名、历史清理或事实来源反向切换不是普通回退,必须另行建单并等待确认。
+92 -6
View File
@@ -1,8 +1,94 @@
# 文档索引 <!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Home
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Home
wiki_revision: 52e9a6f8fe4fe03db8c65792ef500cc9a5084384
synchronized_at: 2026-08-16T11:18:48Z
<!-- gitea-wiki-mirror:end -->
- [项目档案](00-project-profile.md):仓库、技术栈、命令和负责人等稳定信息。 # DevHarness 文档中心
- [开发工作流](01-workflow.md):从需求讨论到工单关闭的完整顺序。
- [任务归档模板](templates/task-archive.md):任务完成后的固定格式。
- `task/`:已经完成并与代码版本对应的任务记录。
临时进度、方案讨论和待办事项写入 Gitea 工单,不写进长期文档。 DevHarness 是一个以 Gitea 工单管理开发任务、以 Wiki 管理长期开发文档、以 Git 记录代码变更的 AI 辅助开发模板。目标是让初级程序员能够理解项目、运行验证,并在 Claude/Codex Agent 协助下处理简单 Bug 和需求。
## 第一次阅读
建议按以下顺序,用 10~20 分钟建立整体认识:
1. [项目档案](Project-Profile.-):项目目标、环境、命令和目录边界。
2. [架构与代码地图](Architecture-and-Code-Map.-):功能从哪里开始读、测试在哪里。
3. [业务规则与术语](Business-Rules-and-Glossary.-):重要名词、状态和不能破坏的规则。
4. [本地开发与验证](Local-Development-and-Verification.-):怎样运行、测试和排错。
5. [常见修改指南](Common-Changes.-):简单修改的步骤和停止条件。
6. [故障排查](Troubleshooting):遇到错误时按什么顺序检查。
7. [开发工作流](Development-Workflow.-):完整建单、实施、验收和归档流程。
从模板创建新项目时先阅读[新项目文档初始化](New-Project-Documentation-Setup.-);向已有项目增量接入本流程时阅读[已有项目接入 DevHarness 指南](Existing-Project-Adoption-Guide.-)。需要为客户或其他岗位准备说明时,阅读[交付文档指南](Delivery-Documentation-Guide.-),再按需使用[岗位文档模板](Audience-Document-Template.-)。
## 五分钟开始
在仓库根目录执行:
```powershell
git status --short --branch
python dev_scripts/check_harness.py --strict
python -m unittest discover -s tests -v
python dev_scripts/sync_wiki_docs.py --check
```
预期结果:
- 工作区没有不属于当前任务的修改;
- Harness 输出“DevHarness 检查通过”;
- 所有单元测试通过;
- 所有 Wiki 映射显示“一致”。
如果失败,先看[故障排查](Troubleshooting),不要直接重置工作区或覆盖本地文档。
## 简单修改从哪里开始
| 想做什么 | 先读哪里 | 主要验证 |
|---|---|---|
| 修改文档 | 对应 Wiki 页面、Common-Changes | Wiki 同步检查 |
| 接入已有项目 | Existing-Project-Adoption-Guide | 只读盘点、差异确认和分阶段验证 |
| 准备交付文档 | Delivery-Documentation-Guide、Audience-Document-Template | 目标岗位验证和 Wiki 同步检查 |
| 调整工单字段 | `.gitea/issue_template/`、Development-Workflow | Harness 严格检查 |
| 修改同步行为 | `dev_scripts/wiki_docs.py`、Architecture-and-Code-Map | 单元测试和真实 Wiki 检查 |
| 增加结构检查 | `dev_scripts/check_harness.py` | 成功与失败测试 |
| 排查运行错误 | Troubleshooting、项目档案 | 最小复现命令 |
权限、安全、并发、迁移、支付、删除数据或不可逆操作不属于简单修改,必须停止并交给 Agent 分析、等待人工确认。
## 事实来源
| 信息 | 事实来源 |
|---|---|
| 任务状态、讨论、阻塞、验收过程 | Gitea 工单 |
| 架构、业务规则、开发规范、操作手册、交付文档、任务归档 | Gitea Wiki |
| 源码和与特定代码版本强绑定的文档 | Git 仓库 |
| 核心长期文档的离线浏览副本 | Git 仓库中的 `docs/` Wiki 镜像 |
| 完整任务归档 | Gitea Wiki;`docs/task/` 仅是人工按需导出的快照 |
本地 `docs/` 不是编辑入口。长期文档必须先修改 Wiki,读取确认后再导出镜像。
## 项目入口
- [Gitea 工单](http://ilaer.eicp.net:8418/opc/dev_harness/issues)
- [代码仓库](http://ilaer.eicp.net:8418/opc/dev_harness)
- [已有项目接入 DevHarness 指南](Existing-Project-Adoption-Guide.-)
- [交付文档指南](Delivery-Documentation-Guide.-)
- [岗位文档模板](Audience-Document-Template.-)
- [任务归档模板](Task-Archive-Template.-)
## 同步原则
```text
修改 Wiki → 读取确认 → 导出 docs → 校验差异 → 提交镜像
```
- 核心页面和本地路径通过仓库中的 `wiki-docs.json` 显式映射;普通同步不处理任务归档。
- 任务归档默认只保存在 Wiki,只有用户明确提出时才增量或全量导出到 `docs/task/`。
- 镜像头记录来源页面、Wiki revision 和同步时间。
- 已映射镜像存在未提交修改时同步必须停止。
- 页面删除、重命名和映射变更必须人工确认。
- 核心 Wiki 或必要同步失败时,相关任务不能标记为完成;未请求任务归档导出不阻止任务完成。
- 凭据、个人数据和生产数据不得进入 Wiki 或镜像。
+80
View File
@@ -0,0 +1,80 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Delivery-Documentation-Guide
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Delivery-Documentation-Guide.-
wiki_revision: d9de4f8d8abc40630069c67948ca2a5086f90e70
synchronized_at: 2026-08-10T06:23:38Z
<!-- gitea-wiki-mirror:end -->
# 交付文档指南
## 本页用途
本页规定项目开发完成后,怎样为客户、最终用户和其他岗位选择、编写、验证及维护交付文档。交付文档面向实际使用产品的人,不替代开发工作流、架构说明和任务归档。
最小原则:先确认交付对象,只创建对方完成工作确实需要的文档,不预建空白手册。
## 什么时候需要交付文档
出现以下任一情况时,应在单元任务工单中评估并更新交付文档:
- 新增或改变用户可见功能、操作步骤、界面、权限或限制;
- 改变安装、配置、部署、备份、恢复、监控或升级方法;
- 改变外部 API、数据格式、集成条件或兼容范围;
- 改变常见故障的识别、处理或支持方式;
- 发布新版本或交付客户验收。
纯内部重构只有在外部行为、操作方式、配置和支持边界均未改变时,才可选择“无交付文档影响”并说明原因。
## 受众与文档选择
| 交付对象 | 典型文档 | 需要回答的问题 |
|---|---|---|
| 最终用户 | 用户使用说明 | 怎样完成日常操作,失败后怎么办 |
| 管理员 | 管理员指南 | 怎样配置用户、权限和系统参数 |
| 运维人员 | 部署与运维指南 | 怎样安装、启停、监控、备份和恢复 |
| 客服或一线支持 | 支持与排错指南 | 怎样识别问题、收集信息和升级处理 |
| 集成人员 | 接口与集成指南 | 怎样认证、调用接口和处理兼容性 |
| 验收或项目负责人 | 发布、升级与验收说明 | 本次交付了什么,怎样验证和回退 |
一个项目只选择实际存在的交付对象。多个岗位需要相同内容时可共享一份文档,但必须明确各自可以执行的操作和权限边界。
## 内部文档与交付文档边界
交付文档可以包含用户完成工作所需的产品地址、公开接口、配置项、操作步骤、结果、限制和支持渠道。
面向客户或公开的文档不得包含:
- 内部工单链接、聊天记录、内部决策过程或任务归档;
- 内部网络地址、仓库路径、无必要的源码模块名和调试细节;
- 密码、令牌、Cookie、私钥、个人数据或生产数据;
- 未经确认的安全实现、漏洞细节或仅供内部使用的恢复手段;
- 未承诺的路线图、期限和功能。
交付前必须检查模板中的“可见范围”。同一主题同时存在内部版和客户版时,应分别维护并明确名称,不能依靠读者自行忽略内部内容。
## 编写和维护流程
1. 在项目初始化或需求确认时识别交付对象、可见范围和所需文档。
2. 使用[岗位文档模板](Audience-Document-Template.-)按需创建文档,不创建没有明确读者的空页面。
3. 单元任务在工单“交付文档影响”中选择无影响并说明原因,或列出需要更新的页面和受众。
4. 功能、配置或流程改变时,代码与对应交付文档在同一任务中更新。
5. 由熟悉该岗位但未参与实现的人按文档执行关键步骤;不能验证的环境和步骤必须明确标注。
6. 发布或交付前确认适用版本、最后验证日期、负责人、已知限制和支持渠道。
7. 长期维护仍遵循 Wiki-first:先修改 Wiki、读取确认,再导出本地 `docs/` 镜像。
具体项目创建的岗位文档应增加到 `wiki-docs.json` 的显式映射中。本模板自身的指南和模板镜像位于 `docs/delivery/`。
## 最小验收清单
- [ ] 文档有明确受众、适用版本、可见范围和负责人。
- [ ] 前置条件、操作步骤和预期结果完整且可以对应。
- [ ] 常见失败、恢复方法、安全提示和已知限制已说明。
- [ ] 关键步骤由目标岗位视角验证,或明确记录未验证项。
- [ ] 外部版本不含内部链接、敏感数据和无关实现细节。
- [ ] 本次功能变化涉及的交付文档已更新并与版本一致。
- [ ] Wiki 已读取确认,本地镜像检查一致。
## 不在本页解决的内容
开发任务怎样建单、实施和归档见[开发工作流](Development-Workflow.-);代码结构和维护入口见[架构与代码地图](Architecture-and-Code-Map.-)。本页不规定市场宣传、合同、法务或商务承诺。
@@ -0,0 +1,96 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Audience-Document-Template
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Audience-Document-Template.-
wiki_revision: 7a8f38df566fe639094497c9692e0559c9c080e6
synchronized_at: 2026-08-10T06:23:41Z
<!-- gitea-wiki-mirror:end -->
# 岗位文档模板
> 使用说明:复制本模板创建具体岗位文档,删除说明性文字并填写真实内容。没有明确受众时不要创建文档;不得保留“待填写”后直接交付。
## 文档信息
| 字段 | 内容 |
|---|---|
| 文档名称 | |
| 适用对象 | 最终用户 / 管理员 / 运维 / 客服 / 集成人员 / 验收人员 / 其他 |
| 适用版本 | |
| 最后验证日期 | YYYY-MM-DD |
| 负责人 | |
| 可见范围 | 内部 / 指定客户 / 公开 |
| 相关产品或模块 | |
## 目的与适用范围
说明读者完成什么工作,以及本文包含和不包含什么。用岗位语言描述结果,不复制内部需求分析。
## 前置条件
- 所需权限:
- 所需环境或设备:
- 已完成的准备:
- 需要提前获得的信息:
不得在这里填写真实密码、令牌、个人数据或生产数据。
## 操作步骤
### 任务一:<明确的操作目标>
1. <执行动作>
2. <执行动作>
3. <执行动作>
**预期结果**:<读者可以观察到的成功结果>
**失败时**:<先检查什么;何时停止并联系支持>
每个独立任务重复以上结构。命令和界面名称应与适用版本一致;危险或不可逆操作必须在执行前给出醒目警告、影响和回退条件。
## 常见错误与恢复
| 现象或错误信息 | 可能原因 | 处理步骤 | 何时升级 |
|---|---|---|---|
| | | | |
只记录经过确认的原因和恢复方法。不要让外部读者执行内部调试、绕过权限或可能扩大损失的操作。
## 安全与权限
- 本岗位允许执行的操作:
- 明确禁止或需要审批的操作:
- 敏感信息处理规则:
- 数据、日志和截图脱敏要求:
- 删除、发布、迁移或其他高风险操作的确认要求:
## 已知限制
- 支持的环境和版本:
- 当前不支持的场景:
- 兼容性限制:
- 未验证的环境或步骤:
## 支持与升级处理
- 支持渠道:
- 服务时间或响应约定:
- 联系支持前需要收集的信息:
- 不得提交的信息:
- 需要升级到下一岗位或负责人的条件:
## 版本记录
| 日期 | 适用版本 | 变更内容 | 验证人 |
|---|---|---|---|
| | | | |
## 交付前检查
- [ ] 目标岗位能够理解术语和步骤。
- [ ] 前置条件、步骤与预期结果一一对应。
- [ ] 关键流程已按目标岗位视角验证。
- [ ] 常见错误、恢复方法和升级条件清楚。
- [ ] 没有内部工单、内部地址、敏感数据或无关源码细节。
- [ ] 适用版本、最后验证日期、负责人和可见范围已填写。
+82
View File
@@ -0,0 +1,82 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Task-1-Wiki-文档主源
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Task-1-Wiki-%E6%96%87%E6%A1%A3%E4%B8%BB%E6%BA%90.-
wiki_revision: dbbf9ddfb2e703486c7054f275538e8e03a570b3
synchronized_at: 2026-08-08T01:11:20Z
<!-- gitea-wiki-mirror:end -->
# 1 引入 Gitea Wiki 作为长期开发文档主源
- 类型:需求
- 所属 Epic:无
- 所属 MVP / 版本:无
- 状态:已完成
- 日期:2026-08-07
- Gitea 工单:[opc/dev_harness#1](http://ilaer.eicp.net:8418/opc/dev_harness/issues/1)
- Wiki 页面:Task-1-Wiki-文档主源
- Wiki revision:见本地镜像头
## 背景与目标
DevHarness 原来使用 Gitea 工单记录实施过程、使用 Git 记录代码,并把完成后的长期文档直接维护在本地 `docs/`。本任务引入 Gitea Wiki 作为长期开发文档的事实来源,本地 `docs/` 改为仅供离线浏览和代码审查的只读镜像。
## 最终方案
- Gitea 工单管理任务状态、讨论、阻塞、验证和验收过程。
- Gitea Wiki 管理架构说明、开发规范、操作手册和任务归档。
- Git 管理源码、版本绑定资料以及 Wiki 镜像。
- `wiki-docs.json` 显式登记 Wiki 页面与本地路径,映射仅允许写入 `docs/` 下的 Markdown。
- `scripts/sync_wiki_docs.py` 只实现 Wiki → docs;镜像记录页面、规范 URL、revision 和同步时间。
- 同步前检查所有映射镜像的 Git 状态,发现未提交改动立即停止。
- 页面缺失、删除或重命名时同步失败,不自动传播破坏性变化。
- `scripts/new_task_archive.py` 改为先创建 Wiki 归档,再登记映射并导出本地镜像。
- `scripts/check_harness.py --strict` 同时检查项目档案、映射完整性和镜像元数据。
Gitea 1.25 会为部分带连字符页面生成带 `.-` 的规范 `sub_url`;客户端先按标题查询页面列表,再使用服务端返回的 `sub_url` 读取内容和构造链接。
## 修改文件
- `AGENTS.md`:更新事实源、实施、归档与安全规则。
- `CLAUDE.md`:更新 Agent 文档读取和归档入口。
- `README.md`:更新工作闭环、目录和快速开始说明。
- `wiki-docs.json`:新增 Wiki 页面显式映射。
- `scripts/wiki_docs.py`:实现配置校验、Gitea API、镜像生成、脏文件保护和一致性检查。
- `scripts/sync_wiki_docs.py`:新增单向同步命令。
- `scripts/new_task_archive.py`:改为在线优先的任务归档流程。
- `scripts/check_harness.py`:增加映射和镜像元数据检查。
- `tests/test_wiki_docs.py`:覆盖映射、元数据、幂等、路径限制和脏文件保护。
- `docs/**`:由 Wiki 导出的只读镜像。
## 验收结果
| 验收标准 | 结果 |
|---|---|
| Wiki 是长期开发文档主源,docs 明确为镜像 | 通过 |
| 一条命令导出全部映射页面 | 通过 |
| 镜像包含页面、URL、revision 和同步时间 | 通过 |
| 未提交镜像改动会阻止覆盖 | 通过 |
| 删除或重命名不会自动传播 | 通过 |
| 严格检查和测试通过 | 通过 |
| Wiki、镜像和流程规则一致 | 通过 |
## 测试
- 执行命令:`python -m unittest discover -s tests -v`
- 结果:10 项测试通过。
- 执行命令:`python scripts/check_harness.py --strict`
- 结果:通过。
- 执行命令:`python scripts/sync_wiki_docs.py --check`
- 结果:映射页面的 revision 和正文均一致。
- 执行命令:`python -m py_compile scripts/check_harness.py scripts/wiki_docs.py scripts/sync_wiki_docs.py scripts/new_task_archive.py tests/test_wiki_docs.py`
- 结果:通过。
- 执行命令:`git diff --check`
- 结果:通过。
- **未验证部分**:未用独立测试 PAT 调用 `new_task_archive.py` 创建额外测试页面;正式 #1 归档通过同一 Gitea API 的 MCP 写入并导出验证。
## 相关提交
- `b08a919` feat: 引入 Wiki 文档镜像流程 (#1)
- `f1a7b09` docs: 同步 Wiki 文档镜像 (#1)
- `8c52ea2` test: 覆盖 Wiki 镜像脏文件保护 (#1)
- `1e3915b` fix: 支持中文 Wiki 页面路径 (#1)
@@ -0,0 +1,88 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Task-10-需求记录与流转规则
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Task-10-%E9%9C%80%E6%B1%82%E8%AE%B0%E5%BD%95%E4%B8%8E%E6%B5%81%E8%BD%AC%E8%A7%84%E5%88%99.-
wiki_revision: d1f9c3198f76216fe8013f9862c26b59f9c179dc
synchronized_at: 2026-08-10T04:00:18Z
<!-- gitea-wiki-mirror:end -->
# 10 需求记录与流转规则
- 类型:开发流程优化
- 所属 Epic:无
- 所属 MVP / 版本:无
- 状态:已完成
- 日期:2026-08-10
- Gitea 工单:http://ilaer.eicp.net:8418/opc/dev_harness/issues/10
- Wiki 页面:Task-10-需求记录与流转规则
- Wiki revision:见本地镜像头
## 背景与目标
工单原本能够记录目标、方案和验收标准,但没有固定字段保存需求来源、关键原始表达和实施期间的需求变化,未来可能难以追溯用户最初目的以及最终方案变化原因。
本任务以最小方式补强需求可追踪性,同时保持 Gitea 工单、Wiki 和本地镜像的事实来源边界。
## 最终方案
- 单元任务模板增加“原始需求”:来源、提出时间、关键原话或脱敏摘要。
- 只保存表达用户目的、场景和限制所需的少量原话,不臆造用户原话。
- 目标、非目标、已确认方案、验收标准和文档影响构成正式任务需求。
- 增加“需求变化记录”,记录影响范围、接口、数据、风险或验收的变化日期、内容、原因和用户确认。
- 会改变已确认结果的变化仍须先更新工单并等待再次确认。
- 不复制完整聊天,不保存 Agent 内部推理,不写入敏感信息;敏感原话必须脱敏。
- 关键原始需求、正式任务需求、讨论和变化保存在 Gitea 工单,不导出全文。
- 长期产品需求、业务规则和系统边界进入 Wiki 主题页并导出 `docs/`。
- 完成结果进入 Wiki 任务归档并导出 `docs/task/`。
- Harness 检查任务模板、Agent 规则和 Wiki 章节。
- 没有新增需求数据库、同步脚本、Skill 或聊天抓取功能。
实施结果与已确认方案一致。
## 修改文件
- `AGENTS.md`:增加需求记录和事实来源边界。
- `.gitea/issue_template/task.md`:增加原始需求和需求变化记录。
- `dev_scripts/check_harness.py`:检查需求追踪字段和规则。
- `tests/test_harness_docs.py`:验证缺失需求追踪字段会被报告。
- `docs/01-workflow.md`:Development-Workflow Wiki 的只读镜像。
- `docs/03-business-rules-and-glossary.md`:Business-Rules-and-Glossary Wiki 的只读镜像。
- `wiki-docs.json`:登记本任务 Wiki 归档镜像。
- `docs/task/10-需求记录与流转规则.md`:本页的自动导出镜像。
## 验收结果
用户于 2026-08-10 明确验收通过。
| 验收标准 | 结果 |
|---|---|
| 模板记录来源、时间和关键原话 | 通过 |
| 模板记录需求变化四项信息 | 通过 |
| 工单保存正式任务需求和变化 | 通过 |
| 长期结论进入 Wiki 和 docs 镜像 | 通过 |
| 不保存完整聊天、内部推理和敏感信息 | 通过 |
| 不导出 Gitea 工单全文 | 通过 |
| Harness 检测字段缺失 | 通过 |
| 未增加脚本或聊天抓取功能 | 通过 |
| Wiki 先更新、确认再导出 | 通过 |
| 测试和严格检查 | 通过 |
| 实现和归档提交分离 | 通过 |
## 测试
- 执行命令:`python -m unittest discover -s tests -v`
- 结果:21/21 通过。
- 执行命令:`python dev_scripts/check_harness.py --strict`
- 结果:通过。
- 执行命令:`python dev_scripts/sync_wiki_docs.py --check`
- 结果:归档前 19/19 映射一致;归档后重新检查 20/20。
- 执行命令:`python -m compileall -q dev_scripts tests`
- 结果:通过。
- 执行命令:`git diff --check`
- 结果:通过。
- **未验证部分**:未实现聊天自动抓取或 Gitea 工单全文导出,这些是已确认的非目标。
## 相关提交
- `353bd97` `feat: 增加需求记录规则 (#10)`
- 归档提交哈希在提交后回写 Gitea 工单,避免归档自引用。
@@ -0,0 +1,80 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Task-11-交付文档指南与岗位文档模板
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Task-11-%E4%BA%A4%E4%BB%98%E6%96%87%E6%A1%A3%E6%8C%87%E5%8D%97%E4%B8%8E%E5%B2%97%E4%BD%8D%E6%96%87%E6%A1%A3%E6%A8%A1%E6%9D%BF.-
wiki_revision: 4e5c30641d9bcbc69d4c235f33ff89dd47259f63
synchronized_at: 2026-08-10T06:29:01Z
<!-- gitea-wiki-mirror:end -->
# 11 交付文档指南与岗位文档模板
- 类型:开发流程优化
- 所属 Epic:无
- 所属 MVP / 版本:无
- 状态:待验收
- 日期:2026-08-10
- Gitea 工单:http://ilaer.eicp.net:8418/opc/dev_harness/issues/11
- Wiki 页面:Task-11-交付文档指南与岗位文档模板
- Wiki revision:见本地镜像头
## 背景与目标
DevHarness 已覆盖开发维护文档,但缺少面向客户、最终用户及其他岗位的交付文档选择、内外边界、编写和验收规则,也没有可以复用的岗位文档模板。
本任务以最小范围建立交付文档闭环:只增加一份指南和一份通用模板,并把交付文档影响接入工单和新项目初始化流程,不预建没有明确读者的空白手册。
## 最终方案
- 新增“交付文档指南”,规定适用时机、受众与文档选择、内部与外部信息边界、编写维护流程和最小验收清单。
- 新增“岗位文档模板”,覆盖文档受众、适用版本、最后验证日期、负责人、可见范围、目的、前置条件、操作步骤、预期结果、常见错误与恢复、安全、限制、支持渠道和版本记录。
- 明确根据实际受众按需创建用户、管理员、运维、支持、集成或验收文档,不创建没有明确读者的空页面。
- 单元任务模板增加“交付文档影响”,要求选择无影响并说明原因,或列出新增、更新页面及目标岗位验证方式。
- 新项目初始化流程增加识别交付对象、可见范围、所需文档和外部信息边界的步骤。
- Home 增加交付指南和岗位模板入口,并将交付文档纳入 Wiki 长期文档事实来源。
- 两个新增页面显式映射到 `docs/delivery/`,Harness 将其纳入核心映射和章节检查。
- 未生成具体岗位手册、PDF、HTML 或发布包,未增加新脚本、框架和第三方依赖。
实施结果与已确认方案一致。
## 修改文件
- `.gitea/issue_template/task.md`:增加交付文档影响字段。
- `dev_scripts/check_harness.py`:检查新增核心页面、章节和工单字段。
- `tests/test_harness_docs.py`:验证缺少交付文档影响字段时能够报告错误。
- `wiki-docs.json`:登记交付指南、岗位模板和本任务归档的镜像映射。
- `docs/README.md`:Home Wiki 的只读镜像,增加交付文档入口。
- `docs/07-new-project-documentation-setup.md`:新项目初始化 Wiki 的只读镜像。
- `docs/delivery/README.md`:交付文档指南的只读镜像。
- `docs/delivery/audience-document-template.md`:岗位文档模板的只读镜像。
- `docs/task/11-交付文档指南与岗位文档模板.md`:本页的自动导出镜像。
## 验收结果
| 验收标准 | 结果 |
|---|---|
| 指南覆盖选择规则、内外边界、流程和验收 | 通过 |
| 岗位模板字段完整且可复用 | 通过 |
| 工单模板要求评估交付文档影响 | 通过 |
| 新项目初始化识别交付对象并按需建文档 | 通过 |
| Home 可进入新增页面 | 通过 |
| 两页镜像到 `docs/delivery/` 并可追踪 revision | 通过 |
| Harness 严格检查和单元测试 | 通过 |
| 实现提交已推送且归档单独提交 | 进行中:归档提交后回写工单 |
| 用户明确验收 | 待验收 |
## 测试
- 执行命令:`python dev_scripts/check_harness.py --strict`
- 结果:通过。
- 执行命令:`python -m unittest discover -s tests -v`
- 结果:22/22 通过。
- 执行命令:`python dev_scripts/sync_wiki_docs.py --check`
- 结果:归档前 22/22 映射一致;归档完成后重新检查。
- 执行命令:`git diff --check`
- 结果:通过。
- **未验证部分**:未创建具体产品的岗位文档,因此没有真实目标岗位或客户环境可执行验证;这属于已确认非目标,具体项目创建实际文档时必须补充目标岗位验证。
## 相关提交
- `3474870` `docs: 建立最小交付文档体系 (#11)`
- 归档提交哈希在提交后回写 Gitea 工单,避免归档自引用。
@@ -0,0 +1,87 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Task-12-已有项目接入DevHarness指南
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Task-12-%E5%B7%B2%E6%9C%89%E9%A1%B9%E7%9B%AE%E6%8E%A5%E5%85%A5DevHarness%E6%8C%87%E5%8D%97.-
wiki_revision: e4ed7d5fd6a9ac5d4bf97c93f509182f2a8a8a4f
synchronized_at: 2026-08-10T10:00:07Z
<!-- gitea-wiki-mirror:end -->
# 12 已有项目接入 DevHarness 指南
- 类型:开发流程优化
- 所属 Epic:无
- 所属 MVP / 版本:无
- 状态:已完成
- 日期:2026-08-10
- Gitea 工单:http://ilaer.eicp.net:8418/opc/dev_harness/issues/12
- Wiki 页面:Task-12-已有项目接入DevHarness指南
- Wiki revision:见本地镜像头
## 背景与目标
现有“新项目文档初始化”面向从模板创建的新项目,没有完整覆盖已有项目中现存规则、文档、工单、Wiki、Git 历史、未完成任务和工作区改动的保护与增量接入方式。
本任务新增独立的已有项目接入指南,让 Claude、Codex 等 Agent 能先只读盘点、识别冲突、等待方案确认,再通过单元任务安全地分阶段接入 DevHarness。
## 最终方案
- 新增 `Existing-Project-Adoption-Guide`,明确与新项目初始化的适用边界。
- 接入前盘点 Agent 规则、README、文档、Wiki、工单、Git 历史、未提交改动、技术栈、命令、权限、安全及任务状态。
- 区分代码或系统事实、负责人确认规则、待确认假设、规则冲突和必须保留的无关改动。
- 明确保留 Git 历史、项目专用规则、现有任务状态和无关工作区改动。
- 禁止复制 DevHarness 历史归档、覆盖已有规则、把模板占位值当成项目事实或自动清理旧文档。
- 采用“确认差异方案 → 建立单元工单 → 接入规则和工单流程 → 确定长期文档事实来源 → 接入必要 Harness 工具 → 分阶段验证 → 提交和验收”的顺序。
- 为已有文档提供保留在 Git、迁移到 Wiki、合并、暂不迁移和停止维护五种明确状态。
- 提供可直接发送给 Agent 的“只分析”和“方案确认后实施”两段指令。
- 规定冲突、删除重命名、产品行为变化、重叠修改、权限不可用和事实不明时的停止条件。
- Home 增加指南入口和已有项目接入导航。
- 页面映射到 `docs/08-existing-project-adoption.md`,并纳入 Harness 核心映射和章节检查。
- 未增加自动迁移脚本,未修改任何实际业务项目,未改变事实来源边界。
实施结果与已确认方案一致。
## 修改文件
- `dev_scripts/check_harness.py`:增加已有项目接入指南的核心映射和章节检查。
- `tests/test_harness_docs.py`:验证指南属于必需核心页面。
- `wiki-docs.json`:登记指南和本任务归档镜像。
- `docs/README.md`:Home Wiki 只读镜像,增加已有项目接入入口。
- `docs/08-existing-project-adoption.md`:已有项目接入指南只读镜像。
- `docs/task/12-已有项目接入DevHarness指南.md`:本页的自动导出镜像。
## 验收结果
用户于 2026-08-10 明确验收通过。
| 验收标准 | 结果 |
|---|---|
| Wiki 存在独立的已有项目接入指南 | 通过 |
| 明确区分已有项目接入与新项目初始化 | 通过 |
| 覆盖只读盘点、内容保护、冲突处理和停止条件 | 通过 |
| 给出分阶段增量接入顺序 | 通过 |
| 包含可直接复制的分析和实施指令 | 通过 |
| 禁止复制历史归档和覆盖已有项目内容 | 通过 |
| Home 可进入指南 | 通过 |
| 页面具有显式映射和有效 revision | 通过 |
| Harness 严格检查和单元测试 | 通过 |
| 实现和归档提交已推送 | 通过 |
| 用户明确验收 | 通过 |
## 测试
- 执行命令:`python dev_scripts/check_harness.py --strict`
- 结果:通过。
- 执行命令:`python -m unittest discover -s tests -v`
- 结果:23/23 通过。
- 执行命令:`python dev_scripts/sync_wiki_docs.py --check`
- 结果:25/25 映射一致。
- 执行命令:`git diff --check`
- 结果:通过。
- 人工检查:两段 Agent 指令未授权覆盖、删除、重命名或自动迁移已有项目内容。
- **未验证部分**:未在真实业务仓库执行完整接入,因为修改实际项目属于本任务非目标;首次实际接入时应按指南建立独立工单并记录项目专用验证。
## 相关提交
- `0271a3e` `docs: 新增已有项目接入指南 (#12)`
- `eba8515` `docs: archive task #12`
- 验收状态提交哈希回写 Gitea 工单,避免归档自引用。
@@ -0,0 +1,68 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Task-13-多子项目与独立交付单元
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Task-13-%E5%A4%9A%E5%AD%90%E9%A1%B9%E7%9B%AE%E4%B8%8E%E7%8B%AC%E7%AB%8B%E4%BA%A4%E4%BB%98%E5%8D%95%E5%85%83.-
wiki_revision: 92b493258075739341d1591069f6c79b143965e7
synchronized_at: 2026-08-11T02:22:51Z
<!-- gitea-wiki-mirror:end -->
# 13 多子项目与独立交付单元
- 类型:需求
- 所属 Epic:无
- 所属 MVP / 版本:无
- 状态:已完成
- 日期:2026-08-10
- 验收日期:2026-08-11
- 验收结果:用户明确验收通过
- Gitea 工单:http://ilaer.eicp.net:8418/opc/dev_harness/issues/13
- Wiki 页面:Task-13-多子项目与独立交付单元
- Wiki revision:见本地镜像头
## 背景与目标
DevHarness 需要兼容一个 Git 仓库包含多个终端、服务或其他独立交付单元的项目。目标是在不引入 monorepo 框架、不自动拆仓的前提下,让项目档案、任务范围和接入指南明确每个子项目的职责、构建测试、版本发布、规则入口及共享契约边界。
## 最终方案
- Project-Profile 增加“子项目与交付单元”表格,记录职责、技术栈、构建测试、版本发布、规则入口和共享边界。
- 新项目初始化流程先识别全部子项目与独立交付单元,并指定共享契约的唯一事实来源。
- 已有项目接入指南增加多应用单仓库判断:技术栈不同本身不要求拆仓;按团队、权限、发布周期、仓库效率、复用关系和契约稳定性决定是否拆分。
- 单元任务模板增加“子项目影响”,强制记录单端或跨端范围、共享契约变化及各端验证。
- Harness 严格检查上述核心章节与模板字段,并增加对应单元测试。
- 未增加 monorepo 工具、自动拆仓或迁移、统一版本机制、CI 生成器,也未修改实际业务项目。
## 修改文件
- `.gitea/issue_template/task.md`:增加子项目影响字段。
- `dev_scripts/check_harness.py`:检查多子项目核心文档结构和任务模板字段。
- `tests/test_harness_docs.py`:增加任务模板子项目影响测试。
- `docs/00-project-profile.md`:镜像项目档案的子项目与交付单元定义。
- `docs/07-new-project-documentation-setup.md`:镜像新项目识别交付单元的初始化步骤。
- `docs/08-existing-project-adoption.md`:镜像多应用单仓库判断与最小规则。
## 验收结果
| 验收标准 | 结果 |
|---|---|
| 项目档案可记录每个子项目和独立交付单元 | 通过 |
| 新项目和已有项目指南说明多应用单仓库的判断规则 | 通过 |
| 单元任务模板明确子项目、跨项目和共享契约影响 | 通过 |
| 共享契约要求指定唯一事实来源和各端验证 | 通过 |
| 严格检查、单元测试和 Wiki 镜像检查通过 | 通过 |
| 不引入拆仓自动化或修改实际业务项目 | 通过 |
## 测试
- 执行命令:`python dev_scripts/check_harness.py --strict`
- 结果:通过。
- 执行命令:`python -m unittest discover -s tests -v`
- 结果:24 项测试全部通过。
- 执行命令:`python dev_scripts/sync_wiki_docs.py --check`
- 结果:归档前 25 份 Wiki 镜像全部一致。
- 人工示例检查:以 Client 与 Admin 两个交付单元填写职责、独立构建测试、版本发布和共享接口时,新增字段能够表达单端与跨端影响。
- **未验证部分**:未在真实多应用业务仓库执行接入或发布验证;该项不属于本工单范围。
## 相关提交
- `4e1db45` docs: support multi-project delivery units (#13)
@@ -0,0 +1,81 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Task-2-Junior-Maintainer-Docs
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Task-2-Junior-Maintainer-Docs.-
wiki_revision: 9bf07c395c959501d995cfe7250623438c53dba7
synchronized_at: 2026-08-08T01:11:23Z
<!-- gitea-wiki-mirror:end -->
# 2 建立面向初级维护者的轻量项目文档体系
- 类型:需求
- 所属 Epic:无
- 所属 MVP / 版本:无
- 状态:已完成
- 日期:2026-08-08
- Gitea 工单:[opc/dev_harness#2](http://ilaer.eicp.net:8418/opc/dev_harness/issues/2)
- Wiki 页面:Task-2-Junior-Maintainer-Docs
- Wiki revision:见本地镜像头
## 背景与目标
DevHarness 已具备工单、Wiki 主源和本地镜像流程,但缺少面向初级维护者的代码入口、业务规则、运行验证、简单修改和故障排查。目标是让初级程序员理解项目文档,并在 Claude/Codex Agent 协助下处理简单 Bug 和需求;大部分代码仍由 Agent 实现。
## 最终方案
- Home 提供 10~20 分钟阅读路径、五分钟验证命令、修改入口和停止条件。
- 新增架构与代码地图、业务规则与术语、本地开发与验证、常见修改、故障排查、新项目文档初始化六个稳定主题页。
- 项目档案补充使用者、环境、适用版本、日志、测试数据和阅读入口。
- 开发工作流和 Agent 规则采用低/中/高风险分级,不以代码行数判断风险。
- 单元任务模板增加“文档影响”,入口、命令、配置、数据、业务和排错变化必须更新 Wiki。
- Harness 检查核心页面映射、固定章节及任务模板字段;不把结构检查误认为语义质量判断。
- 新项目使用明确初始化说明,不增加额外 Wiki 生成框架。
## 修改文件
- `AGENTS.md`:增加风险边界、核心文档和更新条件。
- `CLAUDE.md`:增加代码地图、业务规则阅读顺序和文档影响要求。
- `.gitea/issue_template/task.md`:增加文档影响检查项。
- `README.md`:链接新项目初始化和新增镜像目录。
- `wiki-docs.json`:登记六个新增核心页面。
- `scripts/check_harness.py`:检查核心映射、章节和工单模板。
- `tests/test_harness_docs.py`:覆盖成功、缺失章节和错误映射。
- `docs/README.md`、`docs/00-project-profile.md`、`docs/01-workflow.md`:更新后的 Wiki 镜像。
- `docs/02-architecture-and-code-map.md` 至 `docs/07-new-project-documentation-setup.md`:新增 Wiki 镜像。
## 验收结果
| 验收标准 | 结果 |
|---|---|
| Home 提供阅读顺序、启动、测试和代码入口 | 通过 |
| 核心页面职责明确且保持轻量结构 | 通过 |
| 简单修改和高风险停止边界清楚 | 通过 |
| 单元任务必须声明文档影响 | 通过 |
| 严格检查能发现页面、映射和章节问题 | 通过 |
| Wiki 与本地镜像一致 | 通过 |
| 自动化测试和严格检查通过 | 通过 |
## 测试
- 执行命令:`python -m unittest discover -s tests -v`
- 结果:16/16 通过。
- 执行命令:`python scripts/check_harness.py --strict`
- 结果:通过。
- 执行命令:`python scripts/sync_wiki_docs.py --check`
- 结果:11/11 映射一致。
- 执行命令:`python -m compileall -q scripts tests`
- 结果:通过。
- 执行命令:`git diff --check`
- 结果:通过。
- 人工验证:六个新增 Wiki 页面均可访问,Home 阅读路径、风险分级和初始化顺序完整。
- **未验证部分**:尚未在全新业务仓库完整执行初始化,也未由真实初级程序员完成可用性测试。
## 遗留问题
首个采用该模板的业务项目应把“新人能否根据 Home 找到入口、运行测试并理解一次简单修改”纳入实际验收,再根据反馈精简或补充主题页。
## 相关提交
- `250b055` feat: 建立初级维护者文档体系 (#2)
- `b09658e` docs: 修正文档命令与初始化边界 (#2)
- `c8ccb62` test: 覆盖核心文档映射错误 (#2)
+72
View File
@@ -0,0 +1,72 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Task-3-Dev-Scripts-Rename
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Task-3-Dev-Scripts-Rename.-
wiki_revision: 15e71e5f540039f3947754a4a17971e12604dff7
synchronized_at: 2026-08-08T01:24:35Z
<!-- gitea-wiki-mirror:end -->
# 3 将 DevHarness 工具目录重命名为 dev_scripts
- 类型:重构
- 所属 Epic:无
- 所属 MVP / 版本:无
- 状态:已完成
- 日期:2026-08-08
- Gitea 工单:[opc/dev_harness#3](http://ilaer.eicp.net:8418/opc/dev_harness/issues/3)
- Wiki 页面:Task-3-Dev-Scripts-Rename
- Wiki revision:见本地镜像头
## 背景与目标
DevHarness 自身工具原来放在根目录 `scripts/`,容易和后续业务项目的脚本混淆。本任务把 Harness 检查、Wiki 同步和任务归档工具统一迁移到 `dev_scripts/`,不保留旧路径兼容入口。
## 最终方案
- `scripts/check_harness.py` → `dev_scripts/check_harness.py`。
- `scripts/wiki_docs.py` → `dev_scripts/wiki_docs.py`。
- `scripts/sync_wiki_docs.py` → `dev_scripts/sync_wiki_docs.py`。
- `scripts/new_task_archive.py` → `dev_scripts/new_task_archive.py`。
- 更新测试导入路径、必需文件检查、AGENTS、README 和稳定 Wiki 页面。
- Wiki 先修改并读取确认,再导出本地镜像。
- 历史任务 #1、#2 保留旧路径,避免改写历史事实。
- 旧 `scripts/` 目录不保留包装器,未来业务脚本必须与 `dev_scripts/` 分开。
## 修改文件
- `scripts/**` → `dev_scripts/**`:整体目录重命名。
- `dev_scripts/check_harness.py`:必需工具路径改为新目录。
- `tests/test_wiki_docs.py`、`tests/test_harness_docs.py`:导入新目录。
- `AGENTS.md`:更新同步、归档命令和目录边界。
- `README.md`:更新快速开始和目录说明。
- `docs/README.md`、项目档案、工作流、代码地图、开发验证、常见修改和新项目初始化:线上 Wiki 的更新镜像。
## 验收结果
| 验收标准 | 结果 |
|---|---|
| 四个工具位于 dev_scripts,旧目录不存在 | 通过 |
| 新命令可运行且不保留旧兼容入口 | 通过 |
| 测试导入和必需文件检查使用新路径 | 通过 |
| 稳定 Wiki 与本地镜像使用新路径 | 通过 |
| 非历史内容没有旧路径引用 | 通过 |
| 测试、严格检查、同步和编译通过 | 通过 |
## 测试
- 执行命令:`python -m unittest discover -s tests -v`
- 结果:16/16 通过。
- 执行命令:`python dev_scripts/check_harness.py --strict`
- 结果:通过。
- 执行命令:`python dev_scripts/sync_wiki_docs.py --check`
- 结果:12/12 映射一致。
- 执行命令:`python -m compileall -q dev_scripts tests`
- 结果:通过。
- 执行命令:旧目录和非历史旧路径搜索。
- 结果:旧目录不存在;非历史旧路径无残留。
- **未验证部分**:仓库外部未纳入版本管理的自动化如果仍使用旧命令,需要使用方自行更新。
## 相关提交
- `1c99867` refactor: 将 Harness 工具移至 dev_scripts (#3)
- `51278fa` docs: 修正 dev_scripts 编译检查命令 (#3)
+70
View File
@@ -0,0 +1,70 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Task-4-Agent-Efficiency-Scope
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Task-4-Agent-Efficiency-Scope.-
wiki_revision: 3c2d8b9e5d82a0d3da5b71ef9d889e11d813d27d
synchronized_at: 2026-08-08T01:59:47Z
<!-- gitea-wiki-mirror:end -->
# 4 增加 Agent 效率与范围控制规则
- 类型:需求
- 所属 Epic:无
- 所属 MVP / 版本:无
- 状态:已完成
- 日期:2026-08-08
- Gitea 工单:[opc/dev_harness#4](http://ilaer.eicp.net:8418/opc/dev_harness/issues/4)
- Wiki 页面:Task-4-Agent-Efficiency-Scope
- Wiki revision:见本地镜像头
## 背景与目标
DevHarness 已有完整安全和交付闭环,但缺少对无关扩展、重复检查、预防性验证和完成后继续优化的限制。本任务在不削弱安全、测试和归档的前提下增加效率规则。
## 最终方案
- 严格控制范围:默认按用户确认目标和工单执行,只限制无关产物、无关验证和相邻问题。
- 渐进执行和修复:完成必要前置检查后,用最小命令获得真实反馈,一次处理首个可行动错误。
- 复用已验证事实:只在同一任务和同一环境状态下复用,关键前提变化时重检。
- 明确停止条件:完成用户验收标准和仓库必要闭环后停止,首次运行成功不等于完成。
- 安全、高风险前置检查、代码修改后的测试、提交前状态、推送前远端检查和 Skill 规则不允许省略。
- Harness 检查开发工作流镜像和 AGENTS 中四组章节是否存在。
## 修改文件
- `AGENTS.md`:增加效率与范围控制的完整规则。
- `CLAUDE.md`:增加简明执行入口。
- `Development-Workflow` Wiki:增加长期工作流说明。
- `docs/01-workflow.md`:Wiki 更新后的只读镜像。
- `dev_scripts/check_harness.py`:检查四组核心规则章节。
- `tests/test_harness_docs.py`:验证当前 Agent 规则结构。
## 验收结果
| 验收标准 | 结果 |
|---|---|
| 范围限制不禁止任务必需或仓库强制产物 | 通过 |
| 渐进执行保留安全和高风险前置检查 | 通过 |
| 事实复用包含失效条件和重检例外 | 通过 |
| 最小验收包含完整项目闭环 | 通过 |
| 相邻问题不会自动混入 | 通过 |
| Harness 检查四组章节 | 通过 |
| Wiki、测试和严格检查通过 | 通过 |
## 测试
- 执行命令:`python -m unittest discover -s tests -v`
- 结果:17/17 通过。
- 执行命令:`python dev_scripts/check_harness.py --strict`
- 结果:通过。
- 执行命令:`python dev_scripts/sync_wiki_docs.py --check`
- 结果:13/13 映射一致。
- 执行命令:`python -m compileall -q dev_scripts tests`
- 结果:通过。
- 执行命令:`git diff --check`
- 结果:通过。
- **未验证部分**:不同新项目中的实际效率提升需要通过后续任务数据验证。
## 相关提交
- `d3df733` docs: 增加 Agent 效率与范围控制 (#4)
+70
View File
@@ -0,0 +1,70 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Task-5-Simplify-Agents
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Task-5-Simplify-Agents.-
wiki_revision: 2096d9eee938c7680c1188dd6a5fbe490d1fd69c
synchronized_at: 2026-08-08T03:26:17Z
<!-- gitea-wiki-mirror:end -->
# 5 精简 AGENTS 中重复的解释性内容
- 类型:重构
- 所属 Epic:无
- 所属 MVP / 版本:无
- 状态:已完成
- 日期:2026-08-08
- Gitea 工单:[opc/dev_harness#5](http://ilaer.eicp.net:8418/opc/dev_harness/issues/5)
- Wiki 页面:Task-5-Simplify-Agents
- Wiki revision:见本地镜像头
## 背景与目标
AGENTS 中部分工单层级、风险示例和核心文档说明已经由 Wiki 完整维护。本任务把 AGENTS 精简为强制规则入口,降低重复维护,同时保证 Agent 只读入口规则时不会遗漏安全和交付要求。
## 最终方案
- 工单层级保留单元任务唯一实施、父工单索引和同步要求,详细定义链接开发工作流与业务术语。
- 归档步骤压缩但保留待验收、Wiki 优先、镜像检查、独立提交、证据回写和验收关闭。
- 风险示例表改为强制结论,详细示例链接常见修改指南。
- 核心文档清单改为更新触发条件,详细清单链接新项目初始化指南。
- dev_scripts 不与业务脚本混放的项目红线保留。
- Harness 检查关键强制规则文本,防止后续精简误删。
- 六个现有 Wiki 页面已经承接详细内容,本任务未修改稳定 Wiki 正文。
## 修改文件
- `AGENTS.md`:精简重复解释并增加现有镜像入口。
- `dev_scripts/check_harness.py`:检查关键强制规则仍然存在。
## 验收结果
用户于 2026-08-08 明确验收通过。
| 验收标准 | 结果 |
|---|---|
| 所有安全和交付强制规则保留 | 通过 |
| 详细层级、风险和文档清单不再重复 | 通过 |
| 被精简内容均有现有文档入口 | 通过 |
| 单元任务仍是唯一实施单位 | 通过 |
| 高风险修改仍需停止和确认 | 通过 |
| 用户验收前不得关闭工单 | 通过 |
| Harness 和测试通过 | 通过 |
## 测试
- AGENTS 非空规则行:115 降至 102。
- 执行命令:`python -m unittest discover -s tests -v`
- 结果:17/17 通过。
- 执行命令:`python dev_scripts/check_harness.py --strict`
- 结果:通过。
- 执行命令:`python dev_scripts/sync_wiki_docs.py --check`
- 结果:14/14 映射一致。
- 执行命令:`python -m compileall -q dev_scripts tests`
- 结果:通过。
- 执行命令:`git diff --check`
- 结果:通过。
- **未验证部分**:无。
## 相关提交
- `c9f1bc8` refactor: 精简 AGENTS 重复说明 (#5)
@@ -0,0 +1,78 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Task-6-优化ClaudeCode规则入口
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Task-6-%E4%BC%98%E5%8C%96ClaudeCode%E8%A7%84%E5%88%99%E5%85%A5%E5%8F%A3.-
wiki_revision: 321e7016b2fcdf39f5bc6453310b233d00e81f07
synchronized_at: 2026-08-08T03:26:20Z
<!-- gitea-wiki-mirror:end -->
# 6 优化 Claude Code 规则入口
- 类型:重构
- 所属 Epic:无
- 所属 MVP / 版本:无
- 状态:已完成
- 日期:2026-08-08
- Gitea 工单:http://ilaer.eicp.net:8418/opc/dev_harness/issues/6
- Wiki 页面:Task-6-优化ClaudeCode规则入口
- Wiki revision:见本地镜像头
## 背景与目标
仓库原有 `CLAUDE.md` 已声明 `AGENTS.md` 是共同规则来源,但仍手工复制了阅读顺序、安全、工单、Wiki、Git 和验收等多条共同规则。两处同时维护容易产生内容漂移。
本任务把 `CLAUDE.md` 优化为 Claude Code 的轻量入口:使用官方支持的 `@AGENTS.md` 语法直接导入共同规则,仅在本文件记录 Claude Code 专用差异。
## 最终方案
- 在根目录 `CLAUDE.md` 中使用独立的 `@AGENTS.md` 导入行。
- 删除原先重复的共同流程清单,仅保留三条入口说明。
- 明确 `AGENTS.md` 是共同规则事实来源,共同规则只在其中维护。
- 当前没有 Claude Code 特有的项目规则;以后只有平台或工具差异才写入 `CLAUDE.md`。
- 将 `CLAUDE.md` 加入 Harness 必需文件。
- 新增入口检查,要求精确的独立导入行和事实来源说明。
- 新增自动化测试,覆盖正常入口和缺失导入两种情况。
- 稳定流程语义未变化,因此没有修改稳定 Wiki 主题页。
实施结果与建单方案一致。
## 修改文件
- `CLAUDE.md`:改为导入式 Claude Code 入口,删除重复共同规则。
- `dev_scripts/check_harness.py`:增加必需文件和 Claude Code 入口检查。
- `tests/test_harness_docs.py`:增加入口正向测试和缺失导入失败测试。
- `wiki-docs.json`:登记本任务 Wiki 归档镜像。
- `docs/task/6-优化ClaudeCode规则入口.md`:本页的自动导出镜像。
## 验收结果
用户于 2026-08-08 明确验收通过。
| 验收标准 | 结果 |
|---|---|
| 根目录文件名为 `CLAUDE.md` | 通过 |
| 使用 `@AGENTS.md` 导入共同规则 | 通过 |
| 不再复制大段共同流程 | 通过,由 24 行精简为 9 行 |
| 共同规则只维护在 `AGENTS.md` | 通过 |
| Harness 检测缺失入口或导入 | 通过 |
| 测试、严格检查和 Wiki 镜像检查 | 通过 |
| 实现和归档提交分离 | 通过 |
## 测试
- 执行命令:`python -m unittest discover -s tests -v`
- 结果:19/19 通过。
- 执行命令:`python dev_scripts/check_harness.py --strict`
- 结果:通过。
- 执行命令:`python dev_scripts/sync_wiki_docs.py --check`
- 结果:归档前 15/15 映射一致;归档后将重新检查 16/16。
- 执行命令:`python -m compileall -q dev_scripts tests`
- 结果:通过。
- 执行命令:`git diff --check`
- 结果:通过。
- **未验证部分**:未启动 Claude Code 交互会话执行 `/memory`;导入语法依据 Claude Code 官方文档,并由仓库检查保证格式。
## 相关提交
- `2adb30a` `refactor: 优化 Claude Code 规则入口 (#6)`
- 归档提交哈希在提交后回写 Gitea 工单,避免归档自引用。
+81
View File
@@ -0,0 +1,81 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Task-7-ClaudeCode模型路由
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Task-7-ClaudeCode%E6%A8%A1%E5%9E%8B%E8%B7%AF%E7%94%B1.-
wiki_revision: cfd37f7beab92133e1528104df9553e3e7835aff
synchronized_at: 2026-08-08T03:26:21Z
<!-- gitea-wiki-mirror:end -->
# 7 Claude Code 模型路由
- 类型:开发规则优化
- 所属 Epic:无
- 所属 MVP / 版本:无
- 状态:已完成
- 日期:2026-08-08
- Gitea 工单:http://ilaer.eicp.net:8418/opc/dev_harness/issues/7
- Wiki 页面:Task-7-ClaudeCode模型路由
- Wiki revision:见本地镜像头
## 背景与目标
Claude Code 已通过 `CLAUDE.md` 导入共同开发规则,但没有说明 Opus、Sonnet 和 Haiku 应如何按任务复杂度分工。若每个任务固定依次调用三个模型,会增加重复上下文、Token 和等待时间;如果只用提示语要求 Haiku 只读,又不能真正限制其工具权限。
本任务在 Claude Code 专用入口中增加按需模型路由、结构化交接和 Haiku 只读边界。
## 最终方案
- 目标、范围和修改位置明确的任务默认由 Sonnet 分析、建单和实施。
- 模糊需求、复杂根因、跨模块架构或高风险任务由 Opus/`opusplan` 制定方案。
- Opus 方案必须先由用户确认,再交给 Sonnet 创建工单和实施。
- Haiku 仅用于范围明确的文档、代码入口、日志事实和调用关系检索。
- Haiku 不承担最终根因、技术方案、风险等级和验收结论。
- 当前模型足够时不升级,不固定依次调用三个模型。
- Agent 只交接结构化事实、假设、范围、风险、证据位置和未知项,减少大段重复上下文。
- Haiku 只读必须由工具权限实现;本任务未创建子 Agent 配置,因此未配置只读权限时不得调用 Haiku 处理项目内容。
- Harness 检查三个新增章节及关键边界,防止后续误删。
- 未写死具体模型版本,继续使用模型别名。
实施结果与已确认方案一致。
## 修改文件
- `CLAUDE.md`:增加模型路由、Agent 交接和 Haiku 只读约束。
- `dev_scripts/check_harness.py`:增加 Claude Code 路由规则的结构检查。
- `wiki-docs.json`:登记本任务 Wiki 归档镜像。
- `docs/task/7-ClaudeCode模型路由.md`:本页的自动导出镜像。
## 验收结果
用户于 2026-08-08 明确验收通过。
| 验收标准 | 结果 |
|---|---|
| 保留 `@AGENTS.md` 导入和共同规则来源说明 | 通过 |
| 包含模型路由、Agent 交接和 Haiku 只读约束 | 通过 |
| 不要求每个任务依次调用三个模型 | 通过 |
| Opus 方案经用户确认后由 Sonnet 实施 | 通过 |
| Haiku 不做最终决策 | 通过 |
| Haiku 只读要求工具权限实施 | 通过 |
| 不写死具体模型版本 | 通过 |
| Harness、测试和镜像检查 | 通过 |
| 实现和归档提交分离 | 通过 |
## 测试
- 执行命令:`python -m unittest discover -s tests -v`
- 结果:19/19 通过。
- 执行命令:`python dev_scripts/check_harness.py --strict`
- 结果:通过。
- 执行命令:`python dev_scripts/sync_wiki_docs.py --check`
- 结果:归档前 16/16 映射一致;归档后重新检查 17/17。
- 执行命令:`python -m compileall -q dev_scripts tests`
- 结果:通过。
- 执行命令:`git diff --check`
- 结果:通过。
- **未验证部分**:未创建或运行 Haiku 只读子 Agent,未执行多模型 Token/耗时基准;这些均不在本任务范围内。
## 相关提交
- `3bfe752` `docs: 增加 Claude Code 模型路由 (#7)`
- 归档提交哈希在提交后回写 Gitea 工单,避免归档自引用。
+85
View File
@@ -0,0 +1,85 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Task-8-最小工单依赖规则
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Task-8-%E6%9C%80%E5%B0%8F%E5%B7%A5%E5%8D%95%E4%BE%9D%E8%B5%96%E8%A7%84%E5%88%99.-
wiki_revision: e9a890b18380bfbc02b7a292b8bed95cba8468a0
synchronized_at: 2026-08-10T04:00:14Z
<!-- gitea-wiki-mirror:end -->
# 8 最小工单依赖规则
- 类型:开发流程优化
- 所属 Epic:无
- 所属 MVP / 版本:无
- 状态:已完成
- 日期:2026-08-10
- Gitea 工单:http://ilaer.eicp.net:8418/opc/dev_harness/issues/8
- Wiki 页面:Task-8-最小工单依赖规则
- Wiki revision:见本地镜像头
## 背景与目标
项目已经允许建立后续工单,并定义了“待实施”和“阻塞”,但单元任务模板没有前置依赖和并行信息,Agent 实施前也没有明确的依赖检查步骤。
本任务采用最小实现:工单显式声明依赖,Agent 实施前人工检查,Harness 只检查模板字段存在,不把 DevHarness 扩展为自动项目管理系统。
## 最终方案
- 单元任务模板增加“依赖与并行”章节,记录前置工单、是否允许并行和原因。
- 建立新工单不要求其他工单完成,也不按工单编号限制实施顺序。
- Agent 开始修改前检查已声明的前置工单。
- 没有前置工单、前置已完成或明确允许并行时,可以进入“进行中”。
- 前置未完成且存在真实依赖时不得实施,工单保持“待实施”。
- 允许并行必须说明不存在实施冲突的原因。
- 已进入实施后遇到计划外、当前无法解除的问题时才使用“阻塞”。
- 依赖满足后,任务仍须拥有独立范围、提交、测试和回退方式。
- Harness 检查模板章节及三个字段,并增加缺失字段测试。
- 没有增加自动依赖图、联网查询、自动状态门禁或新脚本。
实施结果与已确认方案一致。
## 修改文件
- `AGENTS.md`:增加实施前依赖检查及状态边界。
- `.gitea/issue_template/task.md`:增加“依赖与并行”字段。
- `dev_scripts/check_harness.py`:检查模板依赖字段。
- `tests/test_harness_docs.py`:验证缺失依赖字段会被报告。
- `docs/01-workflow.md`:Development-Workflow Wiki 的只读镜像。
- `docs/03-business-rules-and-glossary.md`:Business-Rules-and-Glossary Wiki 的只读镜像。
- `wiki-docs.json`:登记本任务 Wiki 归档镜像。
- `docs/task/8-最小工单依赖规则.md`:本页的自动导出镜像。
## 验收结果
用户于 2026-08-10 明确验收通过。
| 验收标准 | 结果 |
|---|---|
| 模板包含依赖与并行三个字段 | 通过 |
| 建单不受其他未完成工单限制 | 通过 |
| 实施前检查声明的前置工单 | 通过 |
| 待实施和阻塞边界明确 | 通过 |
| 允许并行必须写明原因 | 通过 |
| Harness 检测字段缺失 | 通过 |
| 未增加自动门禁或新脚本 | 通过 |
| Wiki 先更新、确认再导出 | 通过 |
| 测试和严格检查 | 通过 |
## 测试
- 执行命令:`python -m unittest discover -s tests -v`
- 结果:20/20 通过。
- 执行命令:`python dev_scripts/check_harness.py --strict`
- 结果:通过。
- 执行命令:`python dev_scripts/sync_wiki_docs.py --check`
- 结果:归档前 17/17 映射一致;归档后重新检查 18/18。
- 执行命令:`python -m compileall -q dev_scripts tests`
- 结果:通过。
- 执行命令:`git diff --check`
- 结果:通过。
- **未验证部分**:没有自动查询 Gitea 前置工单状态;这是已确认的非目标,依赖由 Agent 在实施前人工检查。
## 相关提交
- `a59e3b5` `feat: 增加最小工单依赖规则 (#8)`
- 归档提交哈希在提交后回写 Gitea 工单,避免归档自引用。
@@ -0,0 +1,94 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Task-9-Agent自然语言快捷指令
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Task-9-Agent%E8%87%AA%E7%84%B6%E8%AF%AD%E8%A8%80%E5%BF%AB%E6%8D%B7%E6%8C%87%E4%BB%A4.-
wiki_revision: 6aedbcf63ad86892681c5f9226c4679dbbe16646
synchronized_at: 2026-08-10T04:00:17Z
<!-- gitea-wiki-mirror:end -->
# 9 Agent 自然语言快捷指令
- 类型:开发流程优化
- 所属 Epic:无
- 所属 MVP / 版本:无
- 状态:已完成
- 日期:2026-08-10
- Gitea 工单:http://ilaer.eicp.net:8418/opc/dev_harness/issues/9
- Wiki 页面:Task-9-Agent自然语言快捷指令
- Wiki revision:见本地镜像头
## 背景与目标
Agent 过去能够结合上下文理解“建工单,做”,但仓库没有正式定义快捷指令的名称、允许操作和停止位置,不同平台或会话可能产生不同理解。
本任务定义一组共享的自然语言快捷指令,让 Claude Code、Codex 和维护者使用同一套工作流语义,同时保持方案确认、依赖、安全、Wiki 主源和人工验收门禁。
## 最终方案
正式定义以下 8 个快捷指令:
- `只分析`:只读检查并给出方案,停在等待确认。
- `建工单`:根据已确认方案创建工单,建单后停止。
- `执行工单 #N`:实施现有工单并完成提交、归档和证据,停在“待验收”。
- `建工单并做`:依次建单和执行;`建工单,做`、`建工单,做` 是等价表达,停在“待验收”。
- `继续工单 #N`:从首个未完成步骤继续,不重复仍然有效的检查。
- `检查工单 #N`:只读核对范围、验收、测试和证据,不自动修复。
- `同步文档`:Wiki → `docs/` 同步并检查,不修改 Wiki、不自动提交。
- `#N 验收通过`:仅在用户明确验收后完成验收归档、推送、父工单同步并关闭任务。
补充边界:
- 快捷指令只是自然语言别名,不绕过任何强制规则。
- 方案未确认时,建单或实施类指令停在方案确认。
- 前置依赖未满足且不允许并行时,实施类指令停在“待实施”。
- 除 `#N 验收通过` 外,不关闭待验收工单。
- Gitea 工单保留讨论过程,本地只保存 Wiki 最终任务归档镜像。
- Harness 检查 Wiki 章节和 8 个指令名称。
- 没有增加脚本、Skill、Slash Command 或自动编排器。
实施结果与已确认方案一致。
## 修改文件
- `AGENTS.md`:增加 8 个共享自然语言快捷指令和强制边界。
- `dev_scripts/check_harness.py`:检查 Wiki 章节和快捷指令名称。
- `tests/test_harness_docs.py`:调整 Agent 规则测试名称以覆盖全部必需规则。
- `docs/01-workflow.md`:Development-Workflow Wiki 的只读镜像。
- `wiki-docs.json`:登记本任务 Wiki 归档镜像。
- `docs/task/9-Agent自然语言快捷指令.md`:本页的自动导出镜像。
## 验收结果
用户于 2026-08-10 明确验收通过。
| 验收标准 | 结果 |
|---|---|
| 8 个指令有明确语义和停止位置 | 通过 |
| 建工单并做及逗号变体停在待验收 | 通过 |
| 只有明确验收指令会关闭工单 | 通过 |
| 只分析和检查工单默认只读 | 通过 |
| 同步文档不修改 Wiki、不自动提交 | 通过 |
| 不绕过确认、依赖、安全和验收 | 通过 |
| 未新增平台专用命令或脚本 | 通过 |
| Wiki 先更新并确认再导出 | 通过 |
| Harness、测试和镜像检查 | 通过 |
| 实现和归档提交分离 | 通过 |
## 测试
- 执行命令:`python -m unittest discover -s tests -v`
- 结果:20/20 通过。
- 执行命令:`python dev_scripts/check_harness.py --strict`
- 结果:通过。
- 执行命令:`python dev_scripts/sync_wiki_docs.py --check`
- 结果:归档前 18/18 映射一致;归档后重新检查 19/19。
- 执行命令:`python -m compileall -q dev_scripts tests`
- 结果:通过。
- 执行命令:`git diff --check`
- 结果:通过。
- **未验证部分**:没有实现或运行平台专用 Slash Command、Skill 或自动编排器,这些是已确认的非目标。
## 相关提交
- `10e82d6` `feat: 定义 Agent 快捷指令 (#9)`
- 归档提交哈希在提交后回写 Gitea 工单,避免归档自引用。
+10
View File
@@ -1,3 +1,11 @@
<!-- gitea-wiki-mirror:start -->
generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件)
wiki_page: Task-Archive-Template
wiki_url: http://ilaer.eicp.net:8418/opc/dev_harness/wiki/Task-Archive-Template.-
wiki_revision: 8f8d1bb9c596a79d37631d670aaf95818d74e7c8
synchronized_at: 2026-08-07T15:54:05Z
<!-- gitea-wiki-mirror:end -->
# <工单号> <标题> # <工单号> <标题>
- 类型:需求 / 缺陷 / 重构 - 类型:需求 / 缺陷 / 重构
@@ -6,6 +14,8 @@
- 状态:待验收 / 已完成 - 状态:待验收 / 已完成
- 日期:YYYY-MM-DD - 日期:YYYY-MM-DD
- Gitea 工单:<链接> - Gitea 工单:<链接>
- Wiki 页面:<页面名>
- Wiki revision:见本地镜像头
## 背景与目标 ## 背景与目标
-87
View File
@@ -1,87 +0,0 @@
"""检查 DevHarness 必需文件和任务归档的基本结构。"""
from __future__ import annotations
import argparse
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
REQUIRED_FILES = (
"AGENTS.md",
"README.md",
"docs/00-project-profile.md",
"docs/01-workflow.md",
"docs/templates/task-archive.md",
".gitea/issue_template/epic.md",
".gitea/issue_template/mvp.md",
".gitea/issue_template/task.md",
)
ARCHIVE_HEADINGS = (
"## 背景与目标",
"## 最终方案",
"## 修改文件",
"## 验收结果",
"## 测试",
"## 相关提交",
)
def check_required_files(errors: list[str]) -> None:
for relative_path in REQUIRED_FILES:
if not (ROOT / relative_path).is_file():
errors.append(f"缺少必需文件:{relative_path}")
def check_project_profile(errors: list[str], warnings: list[str], strict: bool) -> None:
profile = ROOT / "docs" / "00-project-profile.md"
if not profile.is_file():
return
if "<填写" in profile.read_text(encoding="utf-8"):
message = "项目档案仍有未填写内容"
(errors if strict else warnings).append(message)
def check_archives(errors: list[str]) -> None:
task_dir = ROOT / "docs" / "task"
for path in task_dir.glob("*.md"):
if not re.match(r"^\d+-.+\.md$", path.name):
errors.append(f"归档文件名不符合 <编号>-<标题>.md:{path.name}")
content = path.read_text(encoding="utf-8")
for heading in ARCHIVE_HEADINGS:
if heading not in content:
errors.append(f"{path.name} 缺少章节:{heading}")
if "**未验证部分**:" not in content:
errors.append(f"{path.name} 没有记录未验证部分")
def main() -> int:
parser = argparse.ArgumentParser(description="检查 DevHarness 项目结构")
parser.add_argument(
"--strict",
action="store_true",
help="项目档案有占位内容时返回失败",
)
args = parser.parse_args()
errors: list[str] = []
warnings: list[str] = []
check_required_files(errors)
check_project_profile(errors, warnings, args.strict)
check_archives(errors)
for warning in warnings:
print(f"警告:{warning}")
for error in errors:
print(f"错误:{error}")
if errors:
print(f"检查失败:{len(errors)} 个问题")
return 1
print("DevHarness 检查通过")
return 0
if __name__ == "__main__":
raise SystemExit(main())
-56
View File
@@ -1,56 +0,0 @@
"""根据模板创建任务归档草稿。"""
from __future__ import annotations
import argparse
import re
from datetime import date
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
TEMPLATE = ROOT / "docs" / "templates" / "task-archive.md"
TASK_DIR = ROOT / "docs" / "task"
def safe_title(title: str) -> str:
"""把标题转换为适合文件名的短文本。"""
cleaned = re.sub(r'[<>:"/\\|?*]', "-", title.strip())
cleaned = re.sub(r"\s+", "-", cleaned)
return cleaned.strip(".-")
def create_archive(issue_number: str, title: str) -> Path:
"""创建归档草稿;目标文件存在时拒绝覆盖。"""
short_title = safe_title(title)
if not issue_number.isdigit():
raise ValueError("工单号必须是数字")
if not short_title:
raise ValueError("标题不能为空")
target = TASK_DIR / f"{issue_number}-{short_title}.md"
if target.exists():
raise FileExistsError(f"文件已存在:{target}")
content = TEMPLATE.read_text(encoding="utf-8")
content = content.replace("<工单号>", issue_number, 1)
content = content.replace("<标题>", title.strip(), 1)
content = content.replace("YYYY-MM-DD", date.today().isoformat(), 1)
target.write_text(content, encoding="utf-8")
return target
def main() -> None:
parser = argparse.ArgumentParser(description="创建 docs/task 任务归档草稿")
parser.add_argument("issue_number", help="Gitea 工单号,例如 123")
parser.add_argument("title", help="简短任务标题")
args = parser.parse_args()
target = create_archive(args.issue_number, args.title)
print(f"已创建:{target.relative_to(ROOT)}")
if __name__ == "__main__":
main()
+210
View File
@@ -0,0 +1,210 @@
from __future__ import annotations
import sys
import tempfile
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "dev_scripts"))
from check_harness import ( # noqa: E402
CORE_DOCUMENT_REQUIREMENTS,
CORE_PAGE_PATHS,
REQUIRED_FILES,
check_claude_code_entry,
check_core_documents,
check_agent_efficiency_rules,
check_task_template,
core_mapping_errors,
missing_sections,
)
from wiki_docs import load_config # noqa: E402
class CoreDocumentTests(unittest.TestCase):
def test_current_core_documents_have_required_sections(self) -> None:
errors: list[str] = []
check_core_documents(errors)
self.assertEqual(errors, [])
def test_missing_sections_reports_each_heading(self) -> None:
missing = missing_sections("# 页面\n## 已有\n", ("## 已有", "## 缺少"))
self.assertEqual(missing, ["## 缺少"])
def test_every_core_document_is_required(self) -> None:
for path in CORE_DOCUMENT_REQUIREMENTS:
self.assertIn(path, REQUIRED_FILES)
def test_every_core_page_has_exact_mapping(self) -> None:
config = load_config()
mappings = {mapping.page: mapping.path for mapping in config.mappings}
for page, path in CORE_PAGE_PATHS.items():
self.assertEqual(mappings.get(page), path)
def test_task_archives_are_not_core_mappings(self) -> None:
config = load_config()
self.assertFalse(
any(mapping.path.startswith("docs/task/") for mapping in config.mappings)
)
def test_existing_project_adoption_guide_is_core_document(self) -> None:
path = "docs/08-existing-project-adoption.md"
self.assertEqual(
CORE_PAGE_PATHS.get("Existing-Project-Adoption-Guide"),
path,
)
self.assertIn(path, CORE_DOCUMENT_REQUIREMENTS)
self.assertIn("## 可复制 Agent 指令", CORE_DOCUMENT_REQUIREMENTS[path])
def test_existing_project_adoption_requires_upgrade_process(self) -> None:
required = CORE_DOCUMENT_REQUIREMENTS[
"docs/08-existing-project-adoption.md"
]
self.assertIn("## 后续升级", required)
self.assertIn("### 升级步骤", required)
self.assertIn("### 可复制升级指令", required)
def test_project_profile_requires_dev_harness_baseline(self) -> None:
required = CORE_DOCUMENT_REQUIREMENTS["docs/00-project-profile.md"]
self.assertIn("## DevHarness 来源与基线", required)
def test_new_project_setup_requires_baseline_selection(self) -> None:
required = CORE_DOCUMENT_REQUIREMENTS[
"docs/07-new-project-documentation-setup.md"
]
self.assertIn("### 2. 选择建设基线", required)
self.assertIn("#### 判断案例", required)
self.assertIn("### 3. 识别子项目与交付单元", required)
def test_missing_or_wrong_core_mapping_is_reported(self) -> None:
errors = core_mapping_errors({"Home": "docs/wrong.md"})
self.assertTrue(any("Home -> docs/README.md" in error for error in errors))
self.assertTrue(
any("Architecture-and-Code-Map" in error for error in errors)
)
class TaskTemplateTests(unittest.TestCase):
def test_task_template_requires_document_impact(self) -> None:
errors: list[str] = []
check_task_template(errors)
self.assertEqual(errors, [])
def test_task_template_requires_delivery_document_impact(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
template = root / ".gitea" / "issue_template" / "task.md"
template.parent.mkdir(parents=True)
template.write_text(
"## 依赖与并行\n"
"- 前置工单:无 / #编号\n"
"- 是否允许与前置工单并行:是 / 否\n"
"- 原因:\n"
"## 原始需求\n"
"- 来源:用户对话 / Gitea / 其他\n"
"- 提出时间:\n"
"- 关键原话或脱敏摘要:\n"
"## 需求变化记录\n"
"| 日期 | 变化内容 | 原因 | 用户确认 |\n"
"## 文档影响\n"
"- [ ] 不影响长期文档,原因:\n"
"- [ ] 更新架构与代码地图\n"
"- [ ] 更新业务规则与术语\n"
"- [ ] 更新常见修改或故障排查\n",
encoding="utf-8",
)
errors: list[str] = []
check_task_template(errors, root)
self.assertIn("单元任务模板缺少:## 交付文档影响", errors)
self.assertIn(
"单元任务模板缺少:- [ ] 无交付文档影响,原因:",
errors,
)
def test_task_template_requires_subproject_impact(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
template = root / ".gitea" / "issue_template" / "task.md"
template.parent.mkdir(parents=True)
template.write_text("## 基本信息\n", encoding="utf-8")
errors: list[str] = []
check_task_template(errors, root)
self.assertIn("单元任务模板缺少:## 子项目影响", errors)
self.assertIn(
"单元任务模板缺少:- 是否跨子项目:是 / 否",
errors,
)
self.assertIn(
"单元任务模板缺少:- 是否修改共享接口或契约:是 / 否;唯一事实来源:",
errors,
)
def test_task_template_requires_dependency_fields(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
template = root / ".gitea" / "issue_template" / "task.md"
template.parent.mkdir(parents=True)
template.write_text(
"## 文档影响\n"
"- [ ] 不影响长期文档,原因:\n"
"- [ ] 更新架构与代码地图\n"
"- [ ] 更新业务规则与术语\n"
"- [ ] 更新常见修改或故障排查\n",
encoding="utf-8",
)
errors: list[str] = []
check_task_template(errors, root)
self.assertIn("单元任务模板缺少:## 依赖与并行", errors)
self.assertIn("单元任务模板缺少:- 前置工单:无 / #编号", errors)
def test_task_template_requires_requirement_traceability(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
template = root / ".gitea" / "issue_template" / "task.md"
template.parent.mkdir(parents=True)
template.write_text(
"## 依赖与并行\n"
"- 前置工单:无 / #编号\n"
"- 是否允许与前置工单并行:是 / 否\n"
"- 原因:\n"
"## 文档影响\n"
"- [ ] 不影响长期文档,原因:\n"
"- [ ] 更新架构与代码地图\n"
"- [ ] 更新业务规则与术语\n"
"- [ ] 更新常见修改或故障排查\n",
encoding="utf-8",
)
errors: list[str] = []
check_task_template(errors, root)
self.assertIn("单元任务模板缺少:## 原始需求", errors)
self.assertIn("单元任务模板缺少:## 需求变化记录", errors)
class AgentRuleTests(unittest.TestCase):
def test_required_agent_rules_are_present(self) -> None:
errors: list[str] = []
check_agent_efficiency_rules(errors)
self.assertEqual(errors, [])
def test_claude_code_entry_imports_shared_rules(self) -> None:
errors: list[str] = []
check_claude_code_entry(errors)
self.assertEqual(errors, [])
def test_claude_code_entry_requires_exact_import_line(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
(root / "CLAUDE.md").write_text(
"共同规则事实来源\n只记录 Claude Code 特有\n"
"共同规则只修改 `AGENTS.md`\n",
encoding="utf-8",
)
errors: list[str] = []
check_claude_code_entry(errors, root)
self.assertIn("CLAUDE.md 缺少独立的 @AGENTS.md 导入", errors)
if __name__ == "__main__":
unittest.main()
+319
View File
@@ -0,0 +1,319 @@
from __future__ import annotations
import json
import os
import sys
import tempfile
import unittest
from pathlib import Path
from unittest.mock import Mock, patch
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "dev_scripts"))
from new_task_archive import ( # noqa: E402
build_archive,
main as new_archive_main,
safe_title,
)
from export_task_archives import ( # noqa: E402
existing_task_mirrors,
export_task_archives,
task_target,
)
from wiki_docs import ( # noqa: E402
Config,
Mapping,
WikiClient,
WikiDocsError,
WikiPage,
dirty_mirror_paths,
load_config,
parse_mirror,
render_mirror,
sync_all,
validate_mappings,
)
class MappingTests(unittest.TestCase):
def test_rejects_path_outside_docs(self) -> None:
with self.assertRaisesRegex(WikiDocsError, "docs/"):
validate_mappings([{"page": "Home", "path": "README.md"}])
def test_rejects_duplicate_page(self) -> None:
with self.assertRaisesRegex(WikiDocsError, "重复映射"):
validate_mappings(
[
{"page": "Home", "path": "docs/README.md"},
{"page": "Home", "path": "docs/other.md"},
]
)
def test_normalizes_api_suffix_from_environment(self) -> None:
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / "wiki-docs.json"
path.write_text(
json.dumps(
{
"schema_version": 1,
"gitea_url": "http://configured.example",
"owner": "owner",
"repository": "repo",
"mappings": [
{"page": "Home", "path": "docs/README.md"}
],
}
),
encoding="utf-8",
)
with patch.dict(
os.environ, {"GITEA_URL": "http://gitea.example/api/v1"}, clear=False
):
config = load_config(path)
self.assertEqual(config.gitea_url, "http://gitea.example")
class MirrorTests(unittest.TestCase):
def setUp(self) -> None:
self.page = WikiPage(
title="Home",
sub_url="Home",
text="# 首页\n",
revision="a" * 40,
html_url="http://gitea.example/o/r/wiki/Home",
)
def test_render_includes_traceable_metadata(self) -> None:
rendered = render_mirror(self.page)
metadata, body = parse_mirror(rendered)
self.assertEqual(metadata["wiki_page"], "Home")
self.assertEqual(metadata["wiki_revision"], "a" * 40)
self.assertTrue(metadata["synchronized_at"].endswith("Z"))
self.assertEqual(body, "# 首页\n")
def test_unchanged_revision_preserves_sync_time(self) -> None:
first = render_mirror(self.page)
second = render_mirror(self.page, first)
self.assertEqual(first, second)
@patch("wiki_docs.subprocess.run")
def test_dirty_mirror_paths_are_reported(self, run) -> None:
run.return_value.stdout = " M docs/README.md\n"
config = Config(
path=Path("wiki-docs.json"),
gitea_url="http://gitea.example",
owner="o",
repository="r",
mappings=(Mapping("Home", "docs/README.md"),),
)
self.assertEqual(dirty_mirror_paths(config), [" M docs/README.md"])
@patch("wiki_docs.dirty_mirror_paths", return_value=[" M docs/README.md"])
def test_sync_stops_before_reading_wiki_when_mirror_is_dirty(self, _dirty) -> None:
config = Config(
path=Path("wiki-docs.json"),
gitea_url="http://gitea.example",
owner="o",
repository="r",
mappings=(Mapping("Home", "docs/README.md"),),
)
client = Mock()
with self.assertRaisesRegex(WikiDocsError, "未提交改动"):
sync_all(config, client)
client.get_page.assert_not_called()
class WikiClientTests(unittest.TestCase):
def test_encoded_unicode_sub_url_is_not_double_encoded(self) -> None:
config = Config(
path=Path("wiki-docs.json"),
gitea_url="http://gitea.example",
owner="o",
repository="r",
mappings=(Mapping("中文", "docs/chinese.md"),),
)
client = WikiClient(config, token="")
client.list_pages = Mock(
return_value=[{"title": "中文", "sub_url": "%E4%B8%AD%E6%96%87.-"}]
)
encoded = __import__("base64").b64encode("# 中文\n".encode()).decode()
with patch.object(
client,
"_request",
return_value={
"title": "中文",
"content_base64": encoded,
"last_commit": {"sha": "b" * 40},
},
) as request:
page = client.get_page("中文")
api_path = request.call_args.args[1]
self.assertIn("%E4%B8%AD%E6%96%87.-", api_path)
self.assertNotIn("%25E4", api_path)
self.assertTrue(page.html_url.endswith("/%E4%B8%AD%E6%96%87.-"))
class ArchiveTests(unittest.TestCase):
def test_safe_title_handles_windows_characters(self) -> None:
self.assertEqual(safe_title(' 修复:"登录" / 超时 '), "修复-登录-超时")
def test_build_archive_replaces_known_fields(self) -> None:
template = "# <工单号> <标题>\nYYYY-MM-DD\n<链接>\n<页面名>\n"
result = build_archive(template, "12", "修复登录", "Task-12-login", "http://i/12")
self.assertIn("# 12 修复登录", result)
self.assertIn("http://i/12", result)
self.assertIn("Task-12-login", result)
self.assertNotIn("YYYY-MM-DD", result)
@patch("new_task_archive.WikiClient")
def test_create_archive_does_not_change_core_mapping(self, client_class) -> None:
with tempfile.TemporaryDirectory() as directory:
config_path = Path(directory) / "wiki-docs.json"
original = json.dumps(
{
"schema_version": 1,
"gitea_url": "http://gitea.example",
"owner": "o",
"repository": "r",
"mappings": [
{"page": "Home", "path": "docs/README.md"}
],
}
)
config_path.write_text(original, encoding="utf-8")
client = client_class.return_value
client.list_pages.return_value = []
client.get_page.return_value = WikiPage(
title="Task-Archive-Template",
sub_url="Task-Archive-Template.-",
text="# <工单号> <标题>\nYYYY-MM-DD\n<链接>\n<页面名>\n",
revision="a" * 40,
html_url="http://gitea.example/wiki/template",
)
client.create_page.return_value = WikiPage(
title="Task-14-按需导出",
sub_url="Task-14.-",
text="# 14 按需导出\n",
revision="b" * 40,
html_url="http://gitea.example/wiki/task-14",
)
with patch.object(
sys,
"argv",
[
"new_task_archive.py",
"14",
"按需导出",
"--config",
str(config_path),
],
):
result = new_archive_main()
self.assertEqual(config_path.read_text(encoding="utf-8"), original)
self.assertEqual(result, 0)
client.create_page.assert_called_once()
def test_task_target_uses_stable_safe_name(self) -> None:
with tempfile.TemporaryDirectory() as directory:
target = task_target("Task-14-修复:导出", Path(directory))
self.assertEqual(target.name, "14-修复-导出.md")
def test_existing_mirror_keeps_historical_custom_filename(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
path = root / "docs" / "task" / "2-初级维护者文档体系.md"
path.parent.mkdir(parents=True)
page = WikiPage(
title="Task-2-Junior-Maintainer-Docs",
sub_url="Task-2-Junior-Maintainer-Docs.-",
text="# 2 文档\n",
revision="c" * 40,
html_url="http://gitea.example/wiki/task-2",
)
path.write_text(render_mirror(page), encoding="utf-8")
mirrors = existing_task_mirrors(root)
self.assertEqual(
mirrors["Task-2-Junior-Maintainer-Docs"].name,
"2-初级维护者文档体系.md",
)
@patch("export_task_archives.dirty_paths", return_value=[])
def test_incremental_export_skips_same_revision(self, _dirty) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
path = root / "docs" / "task" / "14-按需导出.md"
path.parent.mkdir(parents=True)
page = WikiPage(
title="Task-14-按需导出",
sub_url="Task-14.-",
text="# 14 按需导出\n",
revision="d" * 40,
html_url="http://gitea.example/wiki/task-14",
)
path.write_text(render_mirror(page), encoding="utf-8")
client = Mock()
client.list_pages.return_value = [
{
"title": page.title,
"sub_url": page.sub_url,
"last_commit": {"sha": page.revision},
}
]
messages = export_task_archives(client, root=root)
self.assertTrue(messages[0].startswith("跳过:"))
client.get_page_from_metadata.assert_not_called()
@patch(
"export_task_archives.dirty_paths",
return_value=[" M docs/task/14-按需导出.md"],
)
def test_export_stops_before_wiki_read_when_task_mirror_is_dirty(
self, _dirty
) -> None:
client = Mock()
with self.assertRaisesRegex(WikiDocsError, "未提交改动"):
export_task_archives(client)
client.list_pages.assert_not_called()
@patch("export_task_archives.dirty_paths", return_value=[])
def test_full_export_reads_all_and_never_deletes_extra_file(self, _dirty) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
task_dir = root / "docs" / "task"
task_dir.mkdir(parents=True)
extra = task_dir / "99-历史快照.md"
extra_page = WikiPage(
title="Task-99-历史快照",
sub_url="Task-99.-",
text="# 99 历史快照\n",
revision="e" * 40,
html_url="http://gitea.example/wiki/task-99",
)
extra.write_text(render_mirror(extra_page), encoding="utf-8")
page = WikiPage(
title="Task-14-按需导出",
sub_url="Task-14.-",
text="# 14 按需导出\n",
revision="f" * 40,
html_url="http://gitea.example/wiki/task-14",
)
client = Mock()
metadata = {
"title": page.title,
"sub_url": page.sub_url,
"last_commit": {"sha": page.revision},
}
client.list_pages.return_value = [metadata]
client.get_page_from_metadata.return_value = page
messages = export_task_archives(client, export_all=True, root=root)
exported = root / "docs" / "task" / "14-按需导出.md"
self.assertTrue(exported.is_file())
self.assertTrue(extra.is_file())
self.assertTrue(messages[0].startswith("已导出:"))
client.get_page_from_metadata.assert_called_once_with(metadata, page.title)
if __name__ == "__main__":
unittest.main()
+60
View File
@@ -0,0 +1,60 @@
{
"schema_version": 1,
"gitea_url": "http://ilaer.eicp.net:8418",
"owner": "opc",
"repository": "dev_harness",
"mappings": [
{
"page": "Home",
"path": "docs/README.md"
},
{
"page": "Project-Profile",
"path": "docs/00-project-profile.md"
},
{
"page": "Development-Workflow",
"path": "docs/01-workflow.md"
},
{
"page": "Architecture-and-Code-Map",
"path": "docs/02-architecture-and-code-map.md"
},
{
"page": "Business-Rules-and-Glossary",
"path": "docs/03-business-rules-and-glossary.md"
},
{
"page": "Local-Development-and-Verification",
"path": "docs/04-local-development-and-verification.md"
},
{
"page": "Common-Changes",
"path": "docs/05-common-changes.md"
},
{
"page": "Troubleshooting",
"path": "docs/06-troubleshooting.md"
},
{
"page": "New-Project-Documentation-Setup",
"path": "docs/07-new-project-documentation-setup.md"
},
{
"page": "Existing-Project-Adoption-Guide",
"path": "docs/08-existing-project-adoption.md"
},
{
"page": "Delivery-Documentation-Guide",
"path": "docs/delivery/README.md"
},
{
"page": "Audience-Document-Template",
"path": "docs/delivery/audience-document-template.md"
},
{
"page": "Task-Archive-Template",
"path": "docs/templates/task-archive.md"
}
]
}