关于 dotnet-slopwatch
dotnet-slopwatch 是用于检查 .NET 代码变更中掩盖真实问题的捷径的技能,尤其适用于 LLM 生成的代码。它通过规则和基线帮助 .NET 开发者识别新增问题。
主要功能
- 01检测被禁用的测试和警告抑制
- 02标记空 catch 块和测试延迟
- 03检查项目设置和包版本绕过
- 04用基线跟踪已有问题
- 05支持钩子和 CI 质量门禁
适用场景
- 01审查 LLM 对 C# 和项目文件的修改
- 02在 Claude Code 编辑时阻止新增捷径
- 03将 slop 检查加入 CI 流水线
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ aaronontheweb/ dotnet-skills --skill dotnet-slopwatch安装后在 Claude Code 中输入/dotnet-slopwatch确认已加载。
详细介绍
Slopwatch:.NET 的 LLM 反作弊工具
何时使用此技能
始终使用此技能。 每当 LLM(包括 Claude)修改以下内容时:
- C# 源文件(.cs)
- 项目文件(.csproj)
- Props 文件(Directory.Build.props、Directory.Packages.props)
- 测试文件
运行 slopwatch,验证这些修改没有引入“糟粕”。
什么是糟粕?
“糟粕”指 LLM 为了让测试通过或构建成功而采取的捷径,却没有真正解决根本问题。这些行为属于奖励作弊——LLM 追求表面上的成功,而不是切实修复问题。
常见的糟粕模式
| 模式 | 示例 | 有何问题 |
|---|---|---|
| 禁用测试 | [Fact(Skip="flaky")] | 掩盖失败,而不是修复问题 |
| 禁用警告 | #pragma warning disable CS8618 | 消除编译器警告,却没有修复问题 |
| 空 catch 块 | catch (Exception) { } | 吞掉错误,掩盖 bug |
| 任意延迟 | await Task.Delay(1000); | 掩盖竞态条件,让测试变慢 |
| 项目级禁用警告 | <NoWarn>CS1591</NoWarn> | 在整个项目中禁用警告 |
| 绕过 CPM | 内联 Version="1.0.0" | 破坏集中式包管理 |
绝不要接受这些模式。 如果 LLM 引入了糟粕,请拒绝这些修改,并要求进行恰当的修复。
安装
作为本地工具(推荐)
添加到 .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 之前,请为当前问题建立基线:
# Initialize baseline from existing code
slopwatch init
# This creates .slopwatch/baseline.json
git add .slopwatch/baseline.json
git commit -m "Add slopwatch baseline"
为何要建立基线? 旧代码可能已有问题。基线可确保 slopwatch 只检测新引入的糟粕,而不是已有的技术债务。
在 LLM 会话期间的用法
每次代码修改后
每当 LLM 生成的代码有任何修改,都运行 slopwatch:
# Analyze for new issues (uses baseline)
slopwatch analyze
# Use strict mode - fail on warnings too
slopwatch analyze --fail-on warning
Slopwatch 标记问题时
不要忽略它。 而应:
- 弄清原因:LLM 为何走了捷径
- 要求恰当的修复:明确指出问题所在
- 验证修复:确保没有引入其他糟粕
# Example: LLM disabled a test
❌ SW001 [Error]: Disabled test detected
File: tests/MyApp.Tests/OrderTests.cs:45
Pattern: [Fact(Skip="Test is flaky")]
# Correct response: Ask for actual fix
"This test was disabled instead of fixed. Please investigate why
it's flaky and fix the underlying timing/race condition issue."
更新基线(少见)
只有在糟粕确有合理理由且已有记录时,才更新基线:
# Add current detections to baseline (use sparingly!)
slopwatch analyze --update-baseline
合理理由示例:
- 第三方库强制要求采用某种模式(例如,必须禁用特定警告)
- 为限制请求速率而有意延迟(并非为了掩盖测试不稳定)
- 无法修改的生成代码
更新基线时,请在代码注释中说明原因。
Claude Code Hook 集成
将 slopwatch 添加为 Hook,以便自动验证每次编辑。创建或更新 .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'
检测规则
| 规则 | 严重性 | 检测内容 |
|---|---|---|
| SW001 | 错误 | 禁用的测试(Skip=、Ignore、#if false) |
| SW002 | 警告 | 禁用警告(#pragma warning disable、SuppressMessage) |
| SW003 | 错误 | 会吞掉异常的空 catch 块 |
| SW004 | 警告 | 测试中的任意延迟(Task.Delay、Thread.Sleep) |
| SW005 | 警告 | 项目文件中的糟粕(NoWarn、TreatWarningsAsErrors=false) |
| SW006 | 错误 | 绕过 CPM(VersionOverride、内联 Version 属性) |
配置
创建 .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 不享有特殊待遇 - 人类编写的代码和 AI 生成的代码适用相同规则
我们的目标是防止技术债逐渐累积;当 LLM 追求“让测试通过”而非“解决实际问题”时,就会产生这种累积。
快速参考
# First time setup
slopwatch init
git add .slopwatch/baseline.json
# After every LLM code change
slopwatch analyze
# Strict mode (recommended)
slopwatch analyze --fail-on warning
# With stats (performance debugging)
slopwatch analyze --stats
# Update baseline (rare, document why)
slopwatch analyze --update-baseline
# JSON output for tooling
slopwatch analyze --output json
何时覆盖(几乎从不)
更新基线或禁用规则的唯一正当理由:
| 场景 | 操作 | 要求 |
|---|---|---|
| 第三方强制要求采用某种模式 | 更新基线 | 添加代码注释说明原因 |
| 生成的代码(不可编辑) | 添加到排除列表 | 在配置中记录 |
| 有意设置的速率限制延迟 | 更新基线 | 添加代码注释,而非写在测试中 |
| 清理遗留代码 | 一次性更新基线 | PR 描述 |
无效理由:
- “测试不稳定” → 修复不稳定问题
- “警告很烦人” → 修复代码
- “在我的机器上能运行” → 修复竞态条件
- “以后再修” → 现在就修