用途
Slopwatch 用于检测 .NET 代码变更中的 LLM 奖励黑客行为。当 LLM(包括 Claude)修改 C# 源文件(.cs)、项目文件(.csproj)、Props 文件(Directory.Build.props、Directory.Packages.props)或测试文件后,应运行 slopwatch 来验证变更没有引入“slop”。
“Slop”指 LLM 为了让测试通过或构建成功而采取的捷径,这些捷径并未真正解决底层问题。这是奖励黑客行为——LLM 优化的是表面成功,而非真正的修复。
主要能力
Slopwatch 能检测以下常见 slop 模式:
- 禁用的测试:例如
[Fact(Skip="flaky")],隐藏失败而非修复。 - 警告抑制:例如
#pragma warning disable CS8618,静默编译器而不解决问题。 - 空 catch 块:例如
catch (Exception) { },吞掉错误、隐藏缺陷。 - 任意延迟:例如
await Task.Delay(1000);,掩盖竞态条件、拖慢测试。 - 项目级抑制:例如
<NoWarn>CS1591</NoWarn>,在整个项目范围内禁用警告。 - CPM 绕过:例如内联
Version="1.0.0",破坏中央包管理。
检测规则及严重级别:
| 规则 | 严重级别 | 检测内容 |
|---|---|---|
| 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 属性) |
使用方式
安装
作为本地工具(推荐):
在 .config/dotnet-tools.json 中添加:
{
"version": 1,
"isRoot": true,
"tools": {
"slopwatch.cmd": {
"version": "0.4.2",
"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 "Add slopwatch baseline"
基线的作用:遗留代码可能已有问题。基线确保 slopwatch 只捕获新引入的 slop,而非预先存在的技术债务。
在 LLM 会话中使用
每次代码变更后运行:
# 分析新问题(使用基线)
slopwatch analyze
# 严格模式——警告也失败
slopwatch analyze --fail-on warning
当 slopwatch 标记问题时,不要忽略。应:
- 理解 LLM 为何采取捷径。
- 要求正确的修复——具体说明问题所在。
- 验证修复没有引入其他 slop。
示例:
# 示例:LLM 禁用了测试
❌ SW001 [Error]: Disabled test detected
File: tests/MyApp.Tests/OrderTests.cs:45
Pattern: [Fact(Skip="Test is flaky")]
# 正确响应:要求实际修复
"This test was disabled instead of fixed. Please investigate why
it is flaky and fix the underlying timing/race condition issue."
更新基线(罕见)
仅当 slop 确实合理且有文档记录时才更新基线:
# 将当前检测结果加入基线(谨慎使用!)
slopwatch analyze --update-baseline
合理理由示例:
- 第三方库强制某种模式(例如必须抑制特定警告)。
- 有意为速率限制添加延迟(非测试不稳定)。
- 无法修改的生成代码。
更新基线时,在代码注释中说明原因。
Claude Code 钩子集成
将 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: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Install Slopwatch
run: dotnet tool install --global Slopwatch.Cmd
- name: Run Slopwatch
run: slopwatch analyze -d . --fail-on warning
Azure Pipelines:
- task: DotNetCoreCLI@2
displayName: 'Install Slopwatch'
inputs:
command: 'custom'
custom: 'tool'
arguments: 'install --global Slopwatch.Cmd'
- script: slopwatch analyze -d . --fail-on warning
displayName: 'Slopwatch Analysis'
配置
创建 .slopwatch/config.json 为特定路径抑制规则,并附上理由:
{
"suppressions": [
{
"ruleId": "SW002",
"pattern": "**/Generated/**",
"justification": "Generated code from tooling cannot be manually changed"
}
],
"globalSuppressions": []
}
使用 -c 或 --config 指向其他位置的配置文件,使用 --exclude 将路径完全排除在分析之外:
slopwatch analyze -d . --config path/to/config.json
slopwatch analyze -d . --exclude "**/Generated/**,**/obj/**,**/bin/**"
严格模式(LLM 会话推荐)
在 LLM 编码会话中,为获得最大保护,警告和错误都失败:
slopwatch analyze -d . --fail-on warning
适用场景
- LLM 修改 .NET 代码后,验证变更未引入 slop。
- 作为 Claude Code 钩子,自动阻止引入 slop 的编辑。
- 集成到 CI/CD 流水线,作为质量门禁。
- 在团队中统一人类和 AI 生成代码的质量标准。
限制
- 基线机制需要首次设置,遗留问题被隔离但不修复。
- 更新基线或禁用规则需要合理理由和文档记录。
- 无效理由包括:“测试不稳定”(应修复不稳定性)、“警告烦人”(应修复代码)、“在我机器上能跑”(应修复竞态条件)、“以后修”(应现在修)。
- 关于收费、价格、平台支持等具体信息,资料中暂未提供。