名称: dotnet-slopwatch 描述: 使用Slopwatch检测.NET代码变更中的LLM奖励黑客行为。每次代码修改后运行,捕获被禁用的测试、被抑制的警告、空的catch块以及其他掩盖真实问题的快捷方式。 可调用: true
Slopwatch: .NET的LLM反作弊工具
何时使用此技能
持续使用此技能。 每次LLM(包括Claude)对以下内容进行更改时:
- C#源文件(.cs)
- 项目文件(.csproj)
- Props文件(Directory.Build.props, Directory.Packages.props)
- 测试文件
运行slopwatch以验证更改不会引入“slop”。
什么是Slop?
“Slop”指的是LLM为让测试通过或构建成功而采取的快捷方式,但实际上并未解决根本问题。这些是奖励黑客行为——LLM优化的是表面成功而非真正的修复。
常见的Slop模式
| 模式 | 示例 | 为什么不好 |
|---|---|---|
| 禁用的测试 | [Fact(Skip="flaky")] |
隐藏失败而不是修复它们 |
| 警告抑制 | #pragma warning disable CS8618 |
静音编译器而不修复问题 |
| 空的catch块 | catch (Exception) { } |
吞掉错误,隐藏bug |
| 任意延迟 | await Task.Delay(1000); |
掩盖竞态条件,使测试变慢 |
| 项目级抑制 | <NoWarn>CS1591</NoWarn> |
在整个项目中禁用警告 |
| CPM绕过 | Version="1.0.0" 内联 |
破坏中央包管理 |
永远不要接受这些模式。 如果LLM引入了slop,拒绝该更改并要求进行适当的修复。
安装
作为本地工具(推荐)
添加到 .config/dotnet-tools.json:
{
"version": 1,
"isRoot": true,
"tools": {
"slopwatch.cmd": {
"version": "0.2.0",
"commands": ["slopwatch"],
"rollForward": false
}
}
}
然后恢复:
dotnet tool restore
作为全局工具
dotnet tool install --global Slopwatch.Cmd
首次设置:建立基线
在现有项目上使用slopwatch之前,创建当前问题的基线:
# 从现有代码初始化基线
slopwatch init
# 这会创建 .slopwatch/baseline.json
git add .slopwatch/baseline.json
git commit -m "添加slopwatch基线"
为什么需要基线? 遗留代码可能存在现有问题。基线确保slopwatch只捕获新引入的slop,而不是预先存在的技术债务。
在LLM会话期间的使用
每次代码更改后
在任何LLM生成的代码修改后运行slopwatch:
# 分析新问题(使用基线)
slopwatch analyze
# 使用严格模式 - 警告也导致失败
slopwatch analyze --fail-on warning
当Slopwatch标记问题时
不要忽略它。 相反:
- 理解为什么 LLM采取了快捷方式
- 请求适当的修复 - 明确指出问题所在
- 验证修复 没有引入不同的slop
# 示例:LLM禁用了测试
❌ SW001 [错误]: 检测到被禁用的测试
文件: tests/MyApp.Tests/OrderTests.cs:45
模式: [Fact(Skip="Test is flaky")]
# 正确回应:要求实际修复
"这个测试被禁用了而不是修复。请调查为什么
它是flaky的,并修复底层的时序/竞态条件问题。"
更新基线(罕见)
仅在slop确实合理且有文档记录时更新基线:
# 将当前检测添加到基线(谨慎使用!)
slopwatch analyze --update-baseline
合理理由示例:
- 第三方库强制使用某种模式(例如,必须抑制特定警告)
- 用于速率限制的故意延迟(不在测试中)
- 无法修改的生成代码
更新基线时在代码注释中记录原因。
Claude代码钩子集成
将slopwatch添加为钩子以自动验证每次编辑。创建或更新 .claude/settings.json:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "slopwatch analyze -d . --hook",
"timeout": 60000
}
]
}
]
}
}
--hook 标志:
- 仅分析git脏文件(快速,即使在大型仓库中)
- 以可读格式将错误输出到stderr
- 在警告/错误时阻止编辑(退出代码2)
- Claude看到错误并可以立即修复
CI/CD集成
将slopwatch添加到您的CI管道作为质量门:
GitHub Actions
jobs:
slopwatch:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 设置 .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: 安装 Slopwatch
run: dotnet tool install --global Slopwatch.Cmd
- name: 运行 Slopwatch
run: slopwatch analyze -d . --fail-on warning
Azure Pipelines
- task: DotNetCoreCLI@2
displayName: '安装 Slopwatch'
inputs:
command: 'custom'
custom: 'tool'
arguments: 'install --global Slopwatch.Cmd'
- script: slopwatch analyze -d . --fail-on warning
displayName: 'Slopwatch 分析'
检测规则
| 规则 | 严重性 | 捕获内容 |
|---|---|---|
| SW001 | 错误 | 禁用的测试(Skip=, Ignore, #if false) |
| SW002 | 警告 | 警告抑制(#pragma warning disable, SuppressMessage) |
| SW003 | 错误 | 吞掉异常的空的catch块 |
| SW004 | 警告 | 测试中的任意延迟(Task.Delay, Thread.Sleep) |
| SW005 | 警告 | 项目文件slop(NoWarn, TreatWarningsAsErrors=false) |
| SW006 | 警告 | CPM绕过(VersionOverride, 内联 Version 属性) |
配置
创建 .slopwatch/slopwatch.json 进行自定义:
{
"minSeverity": "warning",
"rules": {
"SW001": { "enabled": true, "severity": "error" },
"SW002": { "enabled": true, "severity": "warning" },
"SW003": { "enabled": true, "severity": "error" },
"SW004": { "enabled": true, "severity": "warning" },
"SW005": { "enabled": true, "severity": "warning" },
"SW006": { "enabled": true, "severity": "warning" }
},
"exclude": [
"**/Generated/**",
"**/obj/**",
"**/bin/**"
]
}
严格模式(推荐用于LLM会话)
为了在LLM编码会话期间提供最大保护,将所有规则提升为错误:
{
"minSeverity": "warning",
"rules": {
"SW001": { "enabled": true, "severity": "error" },
"SW002": { "enabled": true, "severity": "error" },
"SW003": { "enabled": true, "severity": "error" },
"SW004": { "enabled": true, "severity": "error" },
"SW005": { "enabled": true, "severity": "error" },
"SW006": { "enabled": true, "severity": "error" }
}
}
理念:对新Slop零容忍
- 基线捕获遗留问题 - 现有问题被承认但隔离
- 新slop被阻止 - 任何新的快捷方式都会导致构建/编辑失败
- 例外需要理由 - 如果必须更新基线,请记录原因
- LLM没有特权 - 相同的规则适用于人类和AI生成的代码
目标是防止当LLM优化“让测试通过”而不是“解决实际问题”时逐渐积累的技术债务。
快速参考
# 首次设置
slopwatch init
git add .slopwatch/baseline.json
# 每次LLM代码更改后
slopwatch analyze
# 严格模式(推荐)
slopwatch analyze --fail-on warning
# 带统计信息(性能调试)
slopwatch analyze --stats
# 更新基线(罕见,记录原因)
slopwatch analyze --update-baseline
# JSON输出用于工具化
slopwatch analyze --output json
何时覆盖(几乎从不)
更新基线或禁用规则的唯一有效理由:
| 场景 | 操作 | 要求 |
|---|---|---|
| 第三方强制模式 | 更新基线 | 代码注释解释原因 |
| 生成代码(不可编辑) | 添加到排除列表 | 在配置中记录 |
| 故意的速率限制延迟 | 更新基线 | 代码注释,不在测试中 |
| 遗留代码清理 | 一次性基线更新 | PR描述 |
无效理由:
- “测试是flaky的” → 修复flakiness
- “警告很烦人” → 修复代码
- “在我机器上能工作” → 修复竞态条件
- “我们稍后会修复” → 现在就修复