这是不可跳过的强制协议。在你的每一轮回复中,你必须执行以下操作之一:
- 完成用户请求后 → 立即调用
ask_user工具,提出与当前上下文相关的后续问题 - 存在任何不确定性时 → 不要猜测执行,立即调用
ask_user工具进行澄清
- 禁止在不调用
ask_user的情况下结束回复 - 禁止使用终结性表达(如“希望对你有帮助”、“如有问题随时提问”等)
- 禁止猜测用户意图 — 不确定就用
ask_user询问
- 问题必须与当前任务上下文直接相关
- 问题必须具体、可操作,不要问泛泛的"还需要什么帮助"
- 可以提供选项供用户选择,降低用户输入成本
- 必须先编译+提交后再调用
ask_user— 禁止在未编译验证和提交的情况下询问用户后续工作
- 先记录剩下的任务到 {任务名} 文档
- 每个任务都要:红测试 → 任务 → 编译 → 绿测试 → 文档 → (没有单元测试就不得)git 提交 → 差评大师 → 修复
- 禁止 subAgent 进行全量测试,只能编译+快速冒烟,由mainAgent进行全量测试
- 无后向兼容 — 项目不需要任何后向兼容,遇到相关字样直接删除,大修大改
- JSON 宽容 — 已实现 JSON 宽容解析(RelaxedJsonSerializer),无需重复实现
- JSON DTO 双向转换 — 所有 JSON 处理必须用 DTO + JsonContext 双向转换(
JsonSerializer.Serialize/Deserialize),禁止手写 JSON 字符串拼接(StringBuilder拼 JSON、$"{{...}}"内插、EscapeJsonString手写转义)。GraphQL 用BuildGraphQL分层(外层 DTO + 内层$"..."查询字符串)。保留JsonDocument.Parse仅限动态字段过滤(> ADR: 0133) - Rust 风格报错 — 已实现 Rust 编译器风格报错(带行列指示+代码片段+箭头),面向参数错误必须用此风格
- BitMask 位掩码工具类 — 已实现
BitMask静态工具类(modules/abstractions/abstractions/abs_core/core_utils/core/misc/BitMask.cs),类似 BitArray/Bitmap,减少 hash 查找、降低内存使用、提高性能。枚举集合优先用BitMask.Of()+BitMask.Contains(),替代FrozenSet<Enum> - 字符串处理优先级 — 首选用
Span<char>(0-GC)、SIMD、mmap、AsParallel()链式编程风格 - 纵深防御按层按名称 — 安全拦截按层(前缀树→结构化解析→专项预处理→五色灯→确认→执行→审计)按名称(每个守卫有明确
Name属性和Priority)组织,统一整齐。新增拦截层必须声明层名和优先级,禁止散落 ad-hoc 检查 - 代码风格:链式+有名函数 — 主逻辑用 LINQ 链式语法控制流程,不同部分提取为有名函数,调用处直观可见一切。或用 Actor 模型 / 管道中间件模型组织。禁止内联长 lambda 淹没主流程。死锁处理用 Actor 邮箱模型(消息传递替代共享锁),管道用中间件洋葱模型
- 错误提示必须有诱导方式 — 安全拒绝/错误提示必须解释:① 为什么拒绝 ② 是什么参数/子命令触发的 ③ 到哪个守卫/层拦截住了 ④ 接下来应该怎么做(正确写法)。把通过+错误都告诉 AI,禁止纯拒绝无引导,否则 AI 不知道错误含义会换命令尝试(禁止处发散,比安全策略更危险)。AI 调用时疑惑会导致它换用其他命令绕过,比直接拒绝更危险
- 提取重复为 LINQ 链式 — 发现代码重复部分必须提取为公共组件,并用 LINQ 链式语法组织。禁止散落重复逻辑,禁止复制粘贴。万物皆 node,万物皆插件 — 写代码前先查看当前有多少 node 和插件,复用现有组件,禁止重复造轮子
- 硬编码变委托 — 硬编码字典/映射表/集合优先改为委托给统一数据源(如
DangerousCommandCatalog、RetainedDeviceNames),消除多套不一致的定义。唯一数据源 + 委托消费,禁止双向维护 - 目录层次化 — 文件夹按层次化组织:底层可看见上层,上层无法看见下层,平级之间可互相看见。通过构造
private/目录包裹自己私有对象,提取公共部分放在上层文件夹。禁止跨层引用、禁止私有对象暴露到上层。目录命名必须全小写+下划线,禁止连字符(-)。禁止core/core/嵌套(对 move 不友好,导致工具调用错误),遇到时用 py 脚本全局检测修复
- 执行 bash 工具前,先思考先后果而非结果,避免一切不可撤回的动作
- 用移动代替删除:有非要删除的情况必须调用
ask_user工具让用户接管(详见下文"🔴 绝对禁止"第1条) - Agent 层已帮你把工具分类权限:用户明确睡觉等无人值守环节下,不调用
ask_user工具
- 先思考最坏代码方式,要有反例才能知道如何写好(详见下文"
⚠️ 反例清单") - 不要用简单方式为导向,而是满足架构最优,方便日后不断扩展
- 使用 ADR 保证全局决策,每个任务用 spec 来保存执行(详见下文"ADR 工作流")
先充分理解当前项目和用户需求,防止架构疲劳,推荐使用开心路径处理项目:
- 主路径清晰暴露实现路径:例如 main 仅做配置工作,每个命令是一个主路径
- 主路径上面先实现开心路径,对于边缘场景一律采用断言屏蔽。任务结束后推荐用户用新窗口做之后的任务,需要根据路径进行创立新的 spec 或者 plan
开心路径设计理由:
- a. 边缘场景没有覆盖可以在上层正确就终止,而不是在底层过程游离
- b. 防止非核心路径过分补充,先做第一版代码给用户验证方向
- c. 即使有工作计划、TODO 表,也会因为上下文压缩保留 commit 信息条目过多,造成 AI 信心疲劳,后续 AI 会注意力涣散和偷懒
- d. 断言要具备栈帧信息,AI 调试 exe 时候可以立即整改
- e. 外部环境传入参数是异常,内部屏蔽就是断言。发布期间会自动清理
- f. 如果当前已经进入了处理断言阶段,就逐个断言展开决策和修复方案,不要同时处理多个
为了未来使用脚本替换函数方便, 你应该把函数消费写得相同和类似,这样替换才能更便利.
- 多态,在这个要求场景非常优美.
- 函数重载要慎用,因为这个目的消除消费耦合,不要难以替换.
- 实在不行就封装多一个函数.
替换时必须为未来做工作:当当前生产代码的写法/格式与替换目标不一致时,必须先转为统一写法和格式,再执行替换(幂等可重复)。提示词来源:
ReplacementMethodologySection(系统提示词 Section,关键词"替换/批量替换"触发注入)
大量采用和改造为:AOT 元编程 + 关注点分离 + 约定大于配置。
- 通过源码生成器获取特性(非 AOT 就反射)
- 通过非耦合方式进行扩展,特性写在函数上面,构造各种字典,例如 cmdMap
- 减少硬编码,大量采用
nameof(xxType)防止改了类型忘记改字符串 - 详见下文"枚举 + [EnumValue] 使用规范"和"封装要求"
你是 AI 助手写代码非常迅速,可以把人类的工作计划压缩到数分钟,因此你的 spec、adr、plan 等文档不要预估任何时间,除非用户明确要求。
- 方案选择的时候需要面向未来,选择插件式,好扩展,好替换的方案,架构最美设计,明确向用户说明好坏。
- **每次只问用户一个问题,**不要一次多个问题,多轮对话总能更好让用户选择方案,使用
ask_user工具。 - 让用户做选择题而不是问答题,让你自主决策的时候,则按照第一条架构最美设计。
- 详见上文"核心规则:每次回复结束前必须调用 ask_user 工具"
用户明确睡觉等无人值守环境,说明他期望你可以持续推进一个长任务. 用户希望醒来之后看见所有任务都已经完成,并且没有遗漏任何边缘场景. 你此时需要实现
-
主路径任务(开心路径) a. 文档 b. 代码 c. 单元测试 d. 手动验证,通过设置启动参数,通过bash调用来实际运行,真实暴露运行,而不是mock.
-
边缘场景 实现流程跟主路径任务一样,但是必须要主路径任务完成之后才能实现,禁止过早处理.
-
当发生上下文压缩 回来看看AGENTS.md,它能帮助你回忆这些必要要求.
- 时序bug一旦出现,拆分成
确定性验证和时序验证两部分. - 如果确定性不满足,就拆分函数,用internal+内联特性,进行补充测试.
- 如果还是太复杂了,进行重构,拆分单一职责,每个职责单独测试,补充更多边界条件防御和守卫.
- 涉及时序的 flaky 偶发性测试问题,必须要构造重试16次(项目已经有),延长时间,等等策略.
- 遇到bug不隐瞒,必须报告给用户.
ADR: 0080 — 详见 ADR 文档(含验收标准、推荐配置[架构选型/效率/编译])用户说了
测试等等关键字眼,必须要提示用户是否执行这个操作.
-
⛔ 禁止删除文件(不可协商的安全红线)
这是神圣不可侵犯的规则。违反将导致任务立即失败。
-
🚫 绝对禁用的工具和命令:
DeleteFile工具 — 永远不要调用此工具Remove-Item/del/rm命令- 任何形式的文件删除操作
-
为什么?
- 删除 = 无法回滚 = 灾难性后果
- 丢失审计追踪,无法追溯历史
- 违反渐进式安全原则
-
✅ 唯一正确做法:移动到项目根目录
.xxx/目录- 目标位置:统一移到
{RepoRoot}\.xxx\(如D:\project\w3\.xxx\),禁止移到子目录下的.xxx/ - 命令:用
Move-Item,禁止用git mv(git mv 会 staged 移动记录,且子目录 .xxx 不在 gitignore 中会污染编译) - 格式:
.xxx/{原文件名}.{原后缀}.{时间戳}.del(如ICommandRewriter.cs.20260824.del) - .xxx 在 .gitignore 中:归档文件不被 git 跟踪,原文件显示为
D(deleted),commit 记录删除 - 移走后必须修复引用(否则编译失败 CS1574/CS0246):
- XML 注释中
<see cref="旧类名"/>→ 改为文字描述(如迁移自旧 XxxRewriter) GlobalUsings.cs中旧命名空间 → 删除该global using行- 旧测试文件也一并移走(引用旧类的测试同样归档)
- XML 注释中
- 完整示例:
New-Item -ItemType Directory -Force -Path "D:\project\w3\.xxx" | Out-Null Move-Item "lib/guard/hooks/execution/ICommandRewriter.cs" "D:\project\w3\.xxx\ICommandRewriter.cs.20260824.del"
- 目标位置:统一移到
-
-
✅ 批量替换源码文件 — 推荐用 Python 脚本
- 推荐: 用 Python 脚本做批量替换,替换前先备份(git stash 或 cp 备份)
- 禁止用 PowerShell 替换: PowerShell 读写文件会出现编码问题(BOM/UTF-8/UTF-16 混乱)
- Python 脚本必须: ① 用
encoding='utf-8'打开文件 ② 先单文件验证再推广 ③ 替换后编译验证
-
❌ 禁止删除函数注释(XML 文档注释)
- 🚫 禁止: 删除或清空函数/方法上的
/// <summary>等 XML 文档注释 - 原因: 函数注释是代码契约的一部分,删除会导致 IntelliSense 信息丢失、调用方无法理解意图
- 原因: AI 生成代码时容易“顺手”删掉注释,这是不可逆的信息损失
- ✅ 正确做法:
- 函数签名变更时,同步更新注释内容,而非删除
- 注释内容过时时,更新为正确描述,而非清空
- 新增函数时,必须编写 XML 文档注释
- 🚫 禁止: 删除或清空函数/方法上的
-
❌ 禁止使用会卡住交互的命令
- 禁止:
more,less等分页命令 - 禁止:
git commit不带-m参数(会打开编辑器) - 禁止:
npm init等交互式命令(使用-y跳过)
- 禁止:
4.1 ⛔ 禁止 AI 自动执行 git pull / git pull --rebase
- 🚫 禁止: AI 自行执行
git pull、git pull --rebase、git fetch + git merge/rebase等拉取合并操作 - 根因: 用户可能已自行 rebase main 或重组历史,远程与本地差异是用户有意为之。AI 自动 pull 会产生不伦不类的历史记录,破坏用户的工作
- ✅ 正确做法: 遇到
git push被拒绝(non-fast-forward)时,停止操作,用ask_user工具告知用户"push 被拒绝,远程有新提交,请用户手动处理 pull/rebase",让用户接管 - 例外: 用户明确要求 AI 执行 pull 时才允许执行
-
❌ 禁止猜测用户意图/背景/业务场景(规划任务期间)
- 信息模糊时,基于行业最佳实践自主选择技术方案,优先保守安全.
- 一旦过程中有对架构进行丰富调整的,推荐使用
ask_user工具让用户确认. - 记录决策依据到工作文件末尾(遵循第3条对话偏好)
-
⚠️ 分级交互控制(执行任务期间)- Level 1-2 禁止交互: 自主决策技术方案,基于上下文选择最合理的实现方式
- Level 3 允许交互: 穷尽所有手段(MCP记忆 → 项目代码 → 可用技能 → 互联网搜索)后仍无法解决时,使用
ask_user工具请求用户决策
-
⛔ 禁止并行子智能体期间提交 Git
- 每次指派子智能体时,必须告知子智能体当前处于并行期间,禁止执行
git commit/git push - 原因: 并行子智能体操作同一仓库,提交会导致冲突或覆盖他人工作
- ✅ 正确做法: 并行任务全部完成后,由主智能体统一提交
- 每次指派子智能体时,必须告知子智能体当前处于并行期间,禁止执行
-
⛔ 禁止因时间/长度关系中断任务
- 禁止因为输出过长、执行时间过长而中途停止
- 禁止主动询问用户“是否继续”——用户可以随时中断,不需要AI提醒
- 长任务应持续执行直到完成或穷尽所有手段后请求用户决策
📖 规范详见 docs/README.md
- ⛔ 禁止把 bug 修复报告写成 ADR — ADR 只用于统筹"为什么选 A 放弃 B"的架构决策。bug 修复/功能开发/排错过程不是架构决策,用 commit message(根因+修复+验证)+ 测试复现。判断:没有真正的"替代方案(考虑过但放弃)"就不是 ADR
- 新架构决策必须先写 ADR:涉及跨模块、影响全局、或选择 A 放弃 B 的决策,先在
docs/proposed/adr/写 ADR(状态:proposed)再实现 - 实现后改状态:决策落地并验证后,ADR 状态改为
accepted - 决策被取代:旧 ADR 状态改为
superseded by NNNN,新 ADR 引用旧 ADR - ADR 不可删除:内容不可变,只改状态字段(见 ADR 0008)
- 粒度:架构级 + 组件策略级,函数级决策留在代码注释
- AGENTS.md 反向引用:AGENTS.md 中对应规则处标注
> ADR: NNNN
- 脚本替换规范 必须要先在一个文件或者一个项目上面验证成功,才可以用脚本推广到全部位置.
- ✅ 必须采用渐进式开发方法
- 渐进式定义:渐进式 ≠ 停下来问用户。渐进式 = 执行任务期间,每完成一个关键步骤就自动执行
编译 → 测试 → commit,持续推进任务,不中断、不等待、不询问。核心目的是:① 每步都有可验证的产物(编译产物/测试结果/git提交)② 任何时刻中断都能从最近的commit恢复 ③ 错误在萌芽阶段就被捕获,而非堆积到末尾 - 每次只完成一个功能,编译,单元测试,git提交
- 主工程编译成功后,测试用例也需编译成功
- 一旦有疑问或发现错误,立即停止,先修复再继续
- ⛔ 禁止中途暂停询问用户"是否继续" — 渐进式要求持续推进,用户可随时中断,不需要AI提醒
- 渐进式定义:渐进式 ≠ 停下来问用户。渐进式 = 执行任务期间,每完成一个关键步骤就自动执行
- *
⚠️ 任务失败处理机制- 核心原则: 必须先评估错误级别,禁止遇到小错误就立即回滚
- 总上限: 3次重试机会(含自行修复和回滚重试)
⚠️ 降级策略: 错误累积过多时,禁止反复重试整个任务触发git回滚- 正确做法: 将当前任务拆成更小的子步骤,每步独立编译验证
- 拆到多小?拆到单文件单函数级别,确保每步可独立编译
- 禁止: 看到大量错误就整体回滚再重来(会陷入回滚-重试死循环)
- 🔴 TDD 铁律1(双层测试驱动开发,强制)
- 循环: 🔴E2E红 → 🔴单元红 → 🟢单元绿 → 🔵重构 → 🟢E2E绿
- E2E 层(仅对外接口变更时): 先写会失败的E2E测试 → 定义契约 → 独立exe进程级交互模拟,验证跨进程协议
- 单元层: E2E红灯下开始红绿循环 → 先写行为测试后写输入测试 → 逐步让代码通过使E2E变绿
- 铁律: 先E2E红灯→才允许单元测试→才允许生产代码;写完组件要聚合到主项目编译验证
- ⛔ 禁止补测试: 发现无测试的生产代码 → 先移到
.xxx/→ 再从E2E失败测试开始 - 例外: 纯DTO/枚举无需TDD;纯内部重构可仅走 🔴单元红→🟢单元绿→🔵重构
- 🔴 TDD 铁律2(缺陷驱动测试,修复 bug 时强制)
- 循环: bug → 🔴E2E红(复现) → 🔴单元红(定位根因) → 🟢单元绿(修复) → 🟢E2E绿(验证集成)
- 铁律: 禁止直接改代码修bug → 先写E2E复现 → 确认失败 → 写单元定位根因 → 确认失败 → 才允许修复
- 例外: 纯内部bug可仅走单元测试复现→修复循环
- ✅ Git 规范(强制)
| 规则 | 说明 |
|---|---|
| 环境准备 | 开始任务前先备份一次 |
| 无分页模式 | git --no-pager log/diff/status,git merge --no-edit |
| 提交前验证 | 改动的 csproj 在 Debug 模式下编译通过即可提交,Release 全量编译由 CI 执行 |
| 禁止跳过 | 即使只改了一个注释,也必须走完整个流水线 |
| 禁止单元测试不通过 | 单元测试不通过 = 不允许提交 |
| 允许 push(非 main/master) | LLM 可 git commit + git push 到功能分支;禁止 force push 到 main/master |
| HEREDOC 禁令 | PowerShell 不支持 HEREDOC。git commit 用多个 -m 参数;gh pr create --body 用双引号多行字符串 --body "line1\nline2";Shell命令中HEREDOC由 HeredocRewriter 自动检测并转换为双引号字符串(优先级200,无需手动处理) |
| 特殊字符禁令 | commit 消息禁止 $、反引号、三引号 |
dotnet build 默认增量编译,会缓存生成器输出。新增/修改 [Register] 类后必须用 --no-incremental 全量重建,否则生成器不会重新扫描新类型 |
|
| PR 两段式验证 | PR 通过 CI 后自动合并到 main → main 自动触发自身 CI 实现二次验证。创建 PR 时必须启用 auto-merge(squash 方式)。PR 目标分支统一为 main,无 dev 中间层 |
| gh 工具 | 操作 PR/Issue/Release/CI 等 GitHub 资源时,优先用 jcc gh / jcc mcp_call gh_*;jcc 不可用时直接用系统 gh CLI > ADR: 0073、0089 |
| rg 工具 | 日常代码/文本搜索优先用 Agent 内置 grep 工具(Grep tool);内置工具不可用时直接用系统 rg > ADR: 0070 |
Git commit 消息格式:
- 标准:
类型: 描述 - 含决策:
类型: 描述 | 决策: [做了什么选择,为什么] - 类型:feat / fix / refactor / docs / test / chore
- 示例:
git commit -m "feat: 添加工具搜索功能 | 决策: 优先查MCP记忆再查互联网" - ⛔ 禁止包含分支名:commit 消息中禁止出现 W1/W2/feature-xxx 等分支标识,描述必须说明"做了什么"而非"在哪个分支"
- ⛔ 禁止包含无意义标记:commit 消息禁止包含 PR/Issue 编号引用(会被 GitHub 自动关联)、纯序号、临时标记等
-
✅ 文档需求落地四列验收表(强制)
- 写
docs/implemented/task/、docs/implemented/design/文档时,针对用户提出的每条要求,必须列出验收表:
基建实现 消费点 已实现 已验收 底层能力实现位置(类/模块/文件) 上层调用/使用位置 ✅/❌ ✅/❌ + ADR 0080 手动 exe 验收记录 - 后两列为强制钩子:
已实现防止"写了文档但没实现",已验收防止"实现了但没验收" 已验收列必须走 ADR 0080 手动 exe 验收方式(真实运行非 mock)- 禁止只列前两列就交付文档
- 写
-
查记忆(开始任务前必做)
- 搜同类问题、失败记录、解决方案(可联网)
- 知识图谱:技术栈 → 问题 → 方案
- 不要重复造轮子,避免重蹈覆辙
- 搜索记忆等工具,执行记忆查询命令
-
写记忆(解决问题后必做)
- 记录:问题场景、原因、方案、验证结果
- 标记:【成功经验】/【避坑指南】
- 要有对应错误原因(什么位置遇到,做过什么尝试不行)
- 即使失败的经验也是可贵的
-
注意事项
- 不要写项目名到记忆(记忆会越来越大,要保持通用性)
- 先去检索有什么工具可以读写记忆
原则: 越靠前的手段成本越低、上下文越精准,禁止跳级查询
- 🔍 查 MCP 工具(尤其是记忆 MemoryCli)→ 搜同类问题、失败记录、解决方案
- 📂 查项目代码 → .ps1 脚本、SearchCodebase、Agent 内置
grep工具搜索现有实现模式(内置工具不可用时用系统rg) - 🛠️ 查可用技能 → 检查 Skill 工具是否有相关能力(如性能优化、代码组织等)
- 🌐 查互联网 → WebSearch/WebFetch(最后手段,成本最高、上下文最泛)
- ❓ 穷尽以上仍无法解决 → 使用
ask_user请求用户决策 - 没有测试出来就不允许修复,你要复现用户问题,先定位到问题,避免日后出现重复错误,也避免你自以为修复了.
| 优先级 | 原则 | 说明 |
|---|---|---|
| 🟢 | 可运行 > 完美 | 先让核心路径跑通,再优化边缘场景 |
| 🟡 | 质量底线不可妥协 | 编译通过、无运行时崩溃、核心测试通过 |
| 🔵 | 后续可优化项 | 性能优化、边缘场景覆盖、代码美化 |
| 与 TDD 的协调 | TDD 循环仍需执行,但允许先覆盖核心路径测试,边缘测试后续补充 |
- 保证git环境干净,备份一次
- 每次移动一个功能模块
- 移动后立即编译验证
- 编译成功后提交git
- 禁止一次性大规模重构
-
涉及文件更改时要先列目录树
-
架构不合理要提出来,不要直接生成代码
-
✅ 渐进式成功后必须记录自主决策
-
时机: 每完成一个功能点并编译成功后,立即记录
-
位置: 写到当前工作文件的末尾(不是CLAUDE.md)
-
格式: 使用
<!-- 🤖 Auto Decision: [决策内容] -->注释格式 -
内容: 说明做了什么决策、为什么这样选择、替代方案是什么
-
示例:
<!-- 🤖 Auto Decision: 2026-04-30 --> <!-- 决策: 使用 FrozenDictionary 替代 switch-case --> <!-- 原因: 性能更优,符合NativeAOT要求,避免硬编码 --> <!-- 替代方案: 特性标记(复杂度较高,暂不采用)--> <!-- 验证: 编译通过,测试用例全部通过 ✅ -->
-
⚠️ 重要: 未编译成功的决策不得记录,必须先修复错误
-
-
结束对话时有未完成的工作或缺陷,一定要
⚠️ emoji表情提醒 -
根据用户对话,自行决定是否采用脚本检测法,否则 Agent 内置
grep逐个检查太慢,尤其是大型重构- 脚本检测法:遍历用户项目代码,生成报告
- 例如,用户需要你判断全部锁是否范围,重构锁,死锁问题
| 规则 | 说明 |
|---|---|
| API 粒度 | 尽可能少暴露公开接口,测试用 internal 类 |
| 字符串性能 | 用 Span 消除性能差异,用只读类型消除多线程不安全 |
| 类拆分 | 字段太多时拆成多个类,封装层次更清晰 |
| 枚举扩展 | 用 [EnumValue] + 源码生成器遍历特性收集函数,实现扩展 |
ADR: 0085 — 详见 ADR 文档(含容器性能对比、禁止行为、正确模式)
- 源码参考:
D:\project\claude-code-rust\claude-code-rev-main\src\
| 步骤 | 内容 | 状态 |
|---|---|---|
| a1 | 遗留实现补充:组件 → 链路 → 链路测试 | ✅ 核心完成(TUI渲染链路因迁移WPF跳过) |
| a2 | 每个功能与 ts 文件对比,深度细节 | ✅ 完成(见剩余任务清单.md) |
| a3 | 先构造修复的单个功能计划 md | ✅ 完成(历史/子目录下各分类文档) |
| a4 | 再修复具体代码 | ✅ P0-P2核心功能已对齐,剩余P3 |
原则:ts 和 cs 两边功能完美同步,链路断裂不是删除而是修复;过渡方案、临时组件、过时的都删掉,消除两套实现;逐步消除冗余类和方法。
- 有限集合的字符串常量必须枚举化 — 凡是有限个可选值的字符串标识(模型名、角色名、状态名等),必须定义枚举 +
[EnumValue],利用源码生成器自动生成XxxEnumConstants+XxxExtensions - 禁止手动维护 KV 完全相同的映射字典 — 当 Key == Value 时(如
"gpt-4o" → "gpt-4o"),直接用EnumType[]+ToValue()遍历匹配,不要写(string Key, string Value)[]冗余元组 - 枚举是唯一数据源 — 字符串值由
[EnumValue]定义一次,所有消费方通过ToValue()/FromValue()/XxxEnumConstants获取,禁止在消费方重复硬编码相同字符串 - Contains 匹配场景 — 对需要模糊匹配(如
modelId.Contains("gpt-4o"))的场景,用EnumType[]按优先级排列,遍历时model.ToValue()获取匹配串,无需额外字典 - 一个枚举可以多个特性注释,手动实现字典很蠢啊
- MCP 工具参数描述用 WellKnownParam 枚举 — 公共参数(working_dir/repo/verbosity/limit 等)必须用
[McpToolParameter(WellKnownParam.X)]替代手写字符串描述,由param_metadata.generator编译期自动填充。> ADR: 0135
ADR: 0093 — 详见 ADR 文档(含替代方案、DisposeSafe 扩展方法实现、验证清单)
任何 IDisposable/IAsyncDisposable 对象,在当前作用域内创建且不逃逸,必须用 using var / await using var 声明。禁止裸 new 后手动 Dispose 或 try-finally 释放。
✅ 正确:
using var stream = new FileStream(path, FileMode.Open, FileAccess.Read);
using var reader = new StreamReader(stream, encoding);
return await reader.ReadToEndAsync(ct).ConfigureAwait(false);❌ 错误(裸 new + try-finally):
var reader = new StreamReader(new FileStream(path, FileMode.Open, FileAccess.Read));
try { return await reader.ReadToEndAsync(); }
finally { reader.Dispose(); } // FileStream 未释放,且样板冗余例外(允许手动释放,需注释说明):
- 字段持有的长生命周期资源 → 在
Dispose(bool)中释放 - 工厂方法返回可释放对象(如
OpenRead()返回Stream)→ 调用方负责 - 故意不释放底层流(
leaveOpen: true或Console.OpenStandardInput())→ 注释说明
一个方法内尽量只保留一个 try-catch。化解手段(按优先级):
using var消除finally— 资源释放交给 using,去掉 try-finally- 提取辅助方法 — 每个资源操作独立成方法各自 using,主方法只编排
- 合并相邻 try-catch — 异常处理相同时合并,用
when子句区分类型 - 嵌套 try-catch 提取 — try 内套 try(补偿/回退逻辑)提取为独立方法,外层 catch 调用
❌ 错误(Dispose 里 3 个 try-catch):
public void Dispose() {
try { _cts.Cancel(); } catch (ObjectDisposedException ex) { _logger?.LogWarning(ex, "x"); }
try { _cts.Dispose(); } catch (ObjectDisposedException ex) { _logger?.LogWarning(ex, "y"); }
try { _sem.Dispose(); } catch (ObjectDisposedException ex) { _logger?.LogWarning(ex, "z"); }
}✅ 正确(用 DisposeSafe 扩展方法):
public void Dispose() {
_cts.CancelAndDisposeSafe(_logger);
_sem.DisposeSafe(_logger);
}modules/abstractions/abstractions/abs_core/core_utils/core/misc 提供 DisposeSafeExtensions:
obj.DisposeSafe(logger)— 吞ObjectDisposedException(幂等),其他异常可选日志cts.CancelAndDisposeSafe(logger)— Cancel + Dispose 合并
所有 Dispose() 方法禁止再写 try { x.Dispose(); } catch (ObjectDisposedException) 样板,统一调 x.DisposeSafe(_logger)。
所有释放函数(Dispose/DisposeAsync/Close/StopAsync/ShutdownAsync)体内禁止任何超时/阻塞等待。释放是必须完成的操作,超时无意义——无论如何都要释放。
🚫 禁止模式:
.GetAwaiter().GetResult()— 同步阻塞.Wait(TimeSpan)/.Wait()— 阻塞等待.WaitAsync(TimeSpan)— 超时等待new CancellationTokenSource(timeout)— 超时令牌Task.WhenAny(task, Task.Delay(timeout))— 超时竞赛Thread.Join(TimeSpan)— 超时等待线程
✅ 正确做法:
- 直接
await无超时:await task.ConfigureAwait(false) - 用
CancellationToken.None:await _process.WaitForExitAsync(CancellationToken.None) - fire-and-forget 后台释放:
_ = DisposeAsync().AsTask()(Dispose 内不能 await 自己) - 遇到同步函数无法 await → 全部异步化改造,关闭即释放,统一到
DisposeAsync - 用
await using var自动释放,不手动调 Dispose
处理手法:
- 移除超时参数,直接等待:
StopAsync(TimeSpan? timeout)→StopAsync() - 移除 CTS 超时:
using var cts = new CancellationTokenSource(timeout); await x.WaitAsync(cts.Token)→await x.WaitAsync(CancellationToken.None) - 移除 Task.WhenAny 竞赛:
await Task.WhenAny(task, Task.Delay(timeout))→await task - 同步释放改异步:
Close()→ 逻辑内联到DisposeAsync(),调用方改await using - 弃用方法直接删除,不要任何后向兼容
| 场景 | ✅ 推荐 | ❌ 禁止 |
|---|---|---|
| 作用域内资源 | using var / await using var |
裸 new + 手动 Dispose |
| 异步资源 | await using var |
async 方法里 .Dispose() |
| Dispose 多资源 | x.DisposeSafe(_logger) |
每资源一个 try-catch |
| 取消令牌链接 | using var cts = CancellationTokenSource.CreateLinkedTokenSource(...) |
手动链接 + try-finally |
| 只读集合 | FrozenSet<T> / FrozenDictionary<K,V> |
HashSet + AsReadOnly() |
| 枚举集合 | BitMask.Of() + BitMask.Contains() |
FrozenSet<Enum> |
| 字符串切片 | Span<char> / ReadOnlySpan<char> |
Substring 链式分配 |
| JSON 请求/响应 | DTO + JsonSerializer.Serialize/Deserialize |
StringBuilder 拼 JSON / $"{{...}}" 内插(> ADR: 0133) |
| JSON 解析 | using var doc = JsonDocument.Parse(...) |
不 using 的 JsonDocument |
| 路径拼接 | Path.Combine |
字符串 + 拼接路径 |
| 空检查 | ArgumentNullException.ThrowIfNull |
if (x == null) throw new... |
| 配置字典 | 枚举 + [EnumValue] 源码生成 |
手动 (string,string)[] 元组 |
| 临时目录 | await using var tmp = TempDirScope.Create(fs) |
手写 CreateDirectory + try-finally DeleteDirectory |
| 环境变量临时设置 | using var env = EnvVarScope.Set("K","v").Add("K2","v2") |
手写 var prev=Get;Set;try{}finally{Set(prev)} |
| 工作目录切换 | using var cwd = CwdScope.Enter(fs, newPath) |
手写 var prev=GetCwd;SetCwd;try{}finally{SetCwd(prev)} |
| 事件订阅 | await using var sub = bus.SubscribeAsync(handler) |
Subscribe + 手动 Unsubscribe try-finally |
| 后台 PeriodicTimer 循环 Dispose | volatile bool _stopping + _timer?.Dispose() |
CancellationTokenSource 字段 + _cts.Dispose() 竞态 |
| 后台循环退出检查 | while (!_stopping && await _timer.WaitForNextTickAsync(CancellationToken.None)) |
await _timer.WaitForNextTickAsync(_cts.Token) + CTS Dispose 后 ObjectDisposedException |
| 释放函数内等待 | await task.ConfigureAwait(false) / CancellationToken.None |
.GetAwaiter().GetResult() / .Wait(TimeSpan) / Task.WhenAny+Task.Delay / CTS超时 |
ADR: 0084 — 详见 ADR 文档(含 PowerShell 禁令、路径格式、命令分隔、脚本语言优先级[AST CLI/Python/PowerShell/jcc gh/Agent grep])
- 🚫 禁止:在 bash 命令中使用
2>nul、>nul、2>&1>nul等任何含nul的重定向 - 根因:
nul在 Windows cmd/PowerShell 中是空设备,但在 git bash 中是普通文件名,会创建名为nul的垃圾文件 - ✅ 正确做法:bash 中用
2>/dev/null(/dev/null在 git bash 中是真正的空设备) - 自动拦截:主仓库
D:/project/JoinCode/.git/hooks/pre-commit已配置,检测到nul文件拒绝提交并提示 - 误创建后:立即
rm -f nul删除(不适用 AGENTS.md 禁删规则,这是 AI 自己造成的垃圾)
ADR: 0089 — 详见 ADR 文档(含实测证据、边缘错误提示清单)
jcc.exe 启动后已自带大量工具(实测:jcc mcp_list = 400 个工具 / 49 个分类)。
优先用 jcc/内置工具;jcc 不可用时直接回退到系统 gh / rg:
- 优先在 Bash/PowerShell 里用
jcc gh/ Agent 内置grep工具 - jcc 报错或未安装时,直接用系统
gh/rg可执行文件,不要卡住
1. jcc.exe gh 工具 → 处理 GitHub 的 PR 和 CI 问题(github 分类 31 个 gh_*,HttpClient 直调 REST API,无需系统 gh CLI)
CLI 形态(推荐,ADR: 0090):
jcc gh pr list --limit 3 # 列 PR
jcc gh pr checks 123 # CI 检查状态
jcc gh pr view 123 # PR 详情
jcc gh run view 123 --log --filter error # CI 日志(只留 error)
jcc gh run rerun 123 # 重跑失败的 job
jcc gh issue comment 12 "正文" # 评论 Issue
jcc gh api repos/o/r/issues # 通用 REST 调用
jcc gh --help # 完整用法MCP 工具直调形态(脚本/结构化场景,ADR: 0073):
jcc mcp_list --category github # 列出全部 gh_* 工具
jcc mcp_schema <tool> # 查参数 schema,不必记忆
jcc mcp_call gh_pr_checks '{"pr_number":"123"}' # pr_number 必填
jcc mcp_call gh_run_view '{"run_id":"123"}' # run_id 必填两种入口等价:jcc gh <group> <action> 按约定拼成 gh_{group}_{action},位置参数按工具 schema 的 required 顺序绑定(pr_number / run_id / tag / issue_number…),选项支持 --key value、--key=value,连字符自动归一化为下划线(--max-lines → max_lines)。
2. Agent 内置 grep 工具 → 处理日常搜索(Grep tool,基于 ripgrep 引擎):
Grep tool: pattern=正则表达式, path=搜索目录, include=文件过滤
- 优先用 Agent 内置
grep工具(Greptool)进行代码/文本搜索 - 内置工具不可用时直接用系统
rg - 搜索结果按修改时间排序,返回文件路径+行号+匹配行
ADR: 0075 — 详见 ADR 文档(含坑1-5:jq引号/超时/Sandbox拦截/checks格式/管道死锁 + CI排错完整流程)
[Console]::OutputEncoding = [System.Text.Encoding]UTF8
chcp 65001ADR: 0076 — 详见 ADR 文档(含 Out-File/Select-String/Select-Object 禁令、CLI运行时测试[常用参数/扁平元动词/Agent grep]、FileMode.Append 陷阱)
详细架构索引见 README.md,包含:组件依赖图、组件详情表、内部结构、源码生成器、中间件管道清单、测试结构、构建命令速查、组件名→路径映射
✅ 架构迁移完成 — ADR 0137 已落地:16 功能域(
modules/<domain>/)就近隔离,每域自包含(代码+测试+docs+module.slnx)。详见 DSG040。下方路径引用已更新为 modules/ 结构。
按 16 功能域 + 跨层测试中心组织(就近隔离,ADR 0137):
| 域 | 位置 |
|---|---|
| generators | modules/generators/*.tests/ |
| collections | modules/collections/*.tests/ |
| persistence | modules/persistence/*.tests/ |
| async_primitives | modules/async_primitives/*.tests/ |
| abstractions | modules/abstractions/*.tests/ |
| guard | modules/guard/*.tests/ |
| storage | modules/storage/*.tests/ |
| plugins | modules/plugins/*.tests/ |
| mcp | modules/mcp/*.tests/ |
| llm | modules/llm/*.tests/ |
| cognition | modules/cognition/*.tests/ |
| perception | modules/perception/*.tests/ |
| services | modules/services/*.tests/ |
| composition | modules/composition/*.tests/ |
| apps | modules/apps/*.tests/ |
| tools | modules/tools/*.tests/ |
| 跨层 | test/unit/*.tests/, test/integration/, test/mock/ |
CI 拆分为可复用 workflow(PR → main 触发):
| Workflow | 说明 |
|---|---|
ci.yml |
主入口,调用子 workflow |
ci-build.yml |
16 功能域有序编译 + 组件测试项目 + 卫星项目 |
ci-unit-tests.yml |
每个 csproj 独立 matrix job,--filter "Category!=Integration&Category!=Benchmark" |
ci-integration.yml |
Integration.Tests + App.slnx filter 分组 |
ci-e2e.yml |
MockServer.E2E + Sync.Integration + CodeIndex.E2E + smoke tests |
mutation-testing.yml |
Stryker.NET,每日定时 |
注:
tests/Unit/Mcp.Tests(ToolInterventionManagerTest)和tests/Unit/McpToolDispatch.Tests(ToolHealthMonitor/Scorer/Template/ScoreDebug)有独特测试类,已加入 JoinCode.slnx + CI unit-tests matrix
nuget包: 拒绝全部微软的AI包,因为大部分不支持NativeAOT。 复杂任务: 网络上面查询有没有nuget包,并且支持AOT编译,需要单独项目做测试,避免工程冗余,可以制作卫星项目.
| 约束 | 说明 |
|---|---|
| 目标框架 | net10.0 |
| NativeAOT | 强制,Release 模式自动启用 PublishAot + TrimMode=full |
| AOT 兼容 | 禁止 dynamic、反射 emit、直接解析 JSON;必须用 JsonContext + 源码生成器;写文件 JSON 统一用 RelaxedJsonSerializer(> ADR: 0042);网络/内存 JSON 用 DTO 双向转换禁止手写拼接(> ADR: 0133) |
| GlobalUsings | .cs 文件内禁止写 using,统一放 GlobalUsings.cs |
| TreatWarningsAsErrors | 已启用,零警告容忍 |
| InvariantGlobalization | true,Release 模式 Exe 项目强制 |
| 全球化策略 | 渐进式双语(中英文),遇到全球化问题时逐步实现,不必一次性处理完 |
| IsAotCompatible | 所有源码项目已标记 |
| MCP 协议版本 | 2025-11-25(Streamable HTTP)— 客户端 HttpTransport + 服务端 McpHttpServer(HttpListener,无状态/有状态双模式);MCP-Protocol-Version 头握手协商,MCP-Session-Id 不分配=无状态;旧 2024-11-05 + SseAgentTransport 仍在 lib/transport.impl/sse/ 供兼容使用 |
| Workflow 断点续跑 | DAG 模式每层完成后原子保存快照 workflow_{id}.state.json,启动时加载跳过已完成步骤;IWorkflowStateStore 可选注入(> ADR: 0097) |
ADR: 0086 — 详见 ADR 文档(含 MiddlewarePipeline/StreamMiddlewarePipeline/McpHttpServer/上下文压缩/AsyncLock + 锁设计与死锁防护[Actor模型/状态机])
要求分类,通常一个文件夹内直接暴露的文件少于40个,可以多层文件夹。 强迫症就是每个文件夹内文件和文件夹不应该同时存在,而是纯文件夹或者内纯文件,不得混淆。
AI 产生的编译日志/调试日志/探针输出(dotnet build 重定向、测试输出捕获、计时探针、selftest 输出等)必须统一写入项目根 .xxx/ 目录(如 .xxx/logs/),禁止散落在仓库根目录(污染 git status、混入误提交)。一次性分析产物用完即删;需跨步骤比对的留在 .xxx/ 下命名(如 .xxx/logs/build_red.txt)。
本地测试构建走 Debug 配置,不做 AOT 发布(PublishAot/Release 全量编译耗时长,测试迭代优先速度);AOT 验证留给 CI/Release。
ADR: 0137 — 详见 ADR 文档(含 16 功能域依赖链、CI编译命令、修改不同域时的编译策略、开发编译策略[Debug+增量+单csproj]、编译注意事项)
ADR: 0080 — 详见 ADR 文档(含手动测试规范、全局测试命令、卡死排查优先级[testhost/管道锁/逻辑死锁]、throw 探针法定位副作用测试)
ADR: 0082 — 详见 ADR 文档(含 AsyncRelayCommand 死锁解法、测试标配模板、Avalonia XAML 专属坑、GUI崩溃诊断、启动 exe 测试)
ADR: 0077 — 详见 ADR 文档(含 MockServer 参数表/端点、启动方式、踩坑记录、jcc 环境变量参数表、诊断方法)
ADR: 0087 — 详见 ADR 文档(含 Out-File/Set-Content/regex/WriteAllText 禁令与正确导向)
ADR: 0083 — 详见 ADR 文档(含 Mode 计算属性、推断规则、运行时不变量断言[GEN036/GEN037]、新增 E2E 脚本检查清单、定位卡死方法)
ADR: 0078 — 详见 ADR 文档(含 rebase vs merge、reset --hard 生死线、两阶段流水线、PR创建/同步流程、auto-merge BLOCKED 排查、E2E定义)
以瞎猜接口为耻,以认真查询为荣; 以模糊执行为耻,以寻求确认为荣; 以臆想业务为耻,以人类确认为荣; 以创造接口为耻,以复用现有为荣; 以跳过验证为耻,以主动测试为荣; 以破坏架构为耻,以遵循规范为荣; 以假装理解为耻,以诚实无知为荣; 以盲目修改为耻,以谨慎重构为荣;
📖 各规则已收编为 ADR,详见 docs/README.md 索引。下方每条规则标注对应 ADR 编号,可二次打开查看完整决策上下文与替代方案。
ADR: 0079 — 详见 ADR 文档(含反例1-6:不查AGENTS.md/改共享配置/治标修复链/加法思维/模型ID推断模态/多余ToList拷贝)