name: audit description: “检测和修复代码级漂移。在YOLO冲刺后、发布前或3:1合并比例到期时使用。”
对ctx代码库运行代码级合并检查。这通过检查源代码是否符合项目规范,来补充ctx drift(检查上下文级漂移)。
合并前准备
- 检查比例:自上次合并以来是否已有3+次会话?如果没有,可能为时过早
- 清理工作树:
git status应显示无未提交更改;合并会触及许多文件,你需要一个干净的差异基线 - 先运行测试:
make audit应在开始前通过;不要在损坏的构建基础上进行合并
使用时机
- 在3+次快速会话后(3:1比例)
- 标记发布版本前
- 当会话触及许多文件时
- 当你怀疑规范漂移时
不应使用的时机
- 功能开发中途,代码故意不完整时
- 上次合并后立即,中间没有新工作
- 当用户专注于交付并明确推迟清理时 n
使用示例
/consolidate
/consolidate (在v0.3.0发布前)
/consolidate (本周YOLO冲刺后)
检查项
在机械运行检查之前,根据最近的更改推理哪些区域最有可能发生漂移。这有助于将注意力集中在最重要的地方。
按顺序运行每个检查。按检查报告发现,然后进行总结。
1. 谓词命名
规范:导出的返回布尔值的方法不应使用Is/Has/Can前缀。
rg '^\s*func\s+\([^)]+\)\s+(Is|Has|Can)[A-Z]\w*\(' --type go -l
接受的例外(不要标记这些):
Message上的IsUser()、IsAssistant():去掉Is会使这些看起来像getter(msg.User()读作“获取用户?”)。前缀有其价值。
修复:重命名以删除前缀。IsPending() → Pending()。
标记任何未列为上述例外的新的Is/Has/Can方法。
2. 魔法字符串
规范:在3+个文件中使用的字面量需要定义为常量。
# 查找跨文件重复的字符串字面量
rg '"[A-Z][A-Z_]+\.md"' --type go -c | sort -t: -k2 -rn
rg '"\.context/' --type go -c | sort -t: -k2 -rn
检查internal/config/中现有的常量。如果一个字面量已在那里定义但未在所有地方使用,那就是漂移。
修复:用internal/config/中的常量替换字面量。
3. 硬编码权限
规范:文件权限应使用命名常量,而不是字面量。
rg '0[67][0-7][0-5]' --type go -l
修复:如果缺失,在internal/config/中定义常量,然后引用它们。
4. 文件大小
规范:源文件>300行应评估是否拆分。
find . -name '*.go' -not -name '*_test.go' -exec wc -l {} + | sort -rn | head -20
超过300行的文件:检查它们是否混合了公共API和私有助手(规范要求拆分它们)。
5. 源代码中的TODO/FIXME
规定:主分支中不应有TODO注释(移至TASKS.md)。
rg 'TODO|FIXME|HACK|XXX' --type go -n
修复:将项目移至.context/TASKS.md,删除注释。
6. 路径构建
规定:路径构建使用标准库,不使用字符串连接。
rg '"\.\./|"/"|"/" \+|+ "/"' --type go -l
修复:用filepath.Join()替换。
7. 行宽
强烈建议:保持行数在~80个字符左右。这不是硬性限制——有些行(长字符串字面量、结构体标签、URL)会超过,这没关系。但漂移会悄悄发生,尤其是在测试代码中,长的断言消息和深度嵌套的结构体会使行变宽而无人察觉。
# 超过100个字符的行(标记最严重的违规者,而不是每个81-100字符的行)
rg '.{101,}' --type go -c | sort -t: -k2 -rn | head -20
修复:在自然点处断行:函数参数、结构体字段、链式调用。对于测试代码,将重复的长值提取到局部变量或常量中。
8. 重复代码块
漂移模式:当代理专注于完成任务而不是保持代码整洁时,复制粘贴的代码块会累积。这在测试文件中尤其常见,但也出现在非测试代码中。
在测试代码中,一些重复是可以接受的;测试可读性很重要。但当相同的设置/断言块出现3+次时,考虑使用测试助手(testutil或_test.go中的未导出助手)。
在非测试代码中,应用下面的合并决策矩阵。
# 启发式方法:在同一包中查找签名非常相似的函数
# 手动审查在这里更有效;寻找:
# - 相同的错误处理块
# - 重复的结构体构造
# - 复制粘贴的命令设置模式
修复(测试):在同一_test.go文件中提取助手函数。使用t.Helper(),以便失败消息指向调用者。
修复(非测试):将共享逻辑提取到包级别的未导出函数中,或者如果跨包,则提取到共享的内部包中。
9. 架构图漂移
在结构更改(新包、移动文件、更改依赖关系)后,验证.context/ARCHITECTURE.md图表与实际代码匹配:
# 比较ARCHITECTURE.md中列出的包与实际包
ls internal/
# 比较依赖关系图声明与实际导入
grep -r '"github.com/ActiveMemory/ctx/internal/' internal/ | \
sed 's|.*ctx/internal/|internal/|' | sort -u
修复:更新.context/ARCHITECTURE.md中的组件映射表、依赖关系图和文件布局部分。运行ctx drift以验证没有残留的死路径引用。
10. 死导出
检查导出的函数/类型在其包外没有调用者。
# 快速启发式:导出的函数已定义但仅在其自己的包中使用
使用go vet和golangci-lint run --enable=unused进行更彻底的检查。
11. 包文档漂移
规范:具有doc.go的包必须在两个方面保持准确:
a) 文件组织列表——必须列出包中的每个.go文件(不包括_test.go)。缺失或多余的条目意味着文件已添加/删除而未更新文档。
make lint-docs
b) 包描述——开篇段落描述了包的功能。当行为发生变化(新的子命令、新的职责、重命名的概念)时,描述会发生漂移。
手动审查每个doc.go:描述是否仍然与包今天实际的功能匹配?检查导出的符号、命令Use/Short/Long字符串以及文件组织列表,以获取范围已扩展或转移的线索。
修复(a):添加缺失的文件,删除过时的条目。 修复(b):重写描述以匹配当前行为。
12. 死文档链接
当页面被重命名、移动或删除时,文档链接会发生漂移。
调用/check-links技能来扫描所有docs/ markdown文件中的:
- 内部链接指向不存在的文件
- 外部链接返回错误(报告为警告,而非失败)
- 图像引用指向缺失的文件
内部损坏的链接算作需要修复的发现。外部失败是信息性的——网络分区会发生。
合并决策矩阵
使用此矩阵来优先处理修复:
| 相似性 | 实例数 | 操作 |
|---|---|---|
| 完全重复 | 2+ | 立即合并 |
| 相同模式,不同参数 | 3+ | 使用参数提取 |
| 相似形状 | 5+ | 考虑抽象 |
| < 3 个实例 | 任何 | 保留;重复比错误的抽象更便宜 |
安全迁移模式
当合并会更改公共API时:
- 在旧函数旁边创建新函数
- 使用
// Deprecated:godoc注释弃用旧函数 - 逐步迁移调用者
- 当没有调用者时删除旧函数
如果调用者跨包,切勿在单个提交中进行批量重命名。
输出格式
运行检查后,报告:
## 合并报告
### 发现
- [检查名称]: N个问题(列出文件)
- [检查名称]: 干净
### 优先级
1. [影响最大的发现]: [原因]
2. [下一个]: [原因]
### 建议的修复
- [文件:行号]: [更改内容]
与其他技能的关系
| 技能 | 范围 |
|---|---|
/qa |
构建/测试/lint;此技能检查规范 |
/verify |
确认声明;修复发现后使用 |
/update-docs |
同步文档与代码;更改后运行 |
ctx drift |
检查.context/文件;此技能检查.go |
/check-links |
死文档链接;作为检查#12调用 |
质量检查清单
在报告合并结果之前:
- [ ] 所有12项检查都已运行(未跳过)
- [ ] 接受的例外情况已得到尊重(例如,
IsUser()) - [ ] 发现已按优先级排序(影响最大的优先)
- [ ] 每个发现都有具体的修复建议和文件路径
- [ ] 应用修复后
make audit仍然通过