feat(debugging-network-issues): 补上「一切正常只是慢」这个失败家族 (v1.6.0) - #292
Conversation
这个 skill 原本会主动把降速类故障劝退:Step 0 写着「'Slow' is not a symptom」, 第 107 行把「用户说慢」列为不合格前提,全仓 throughput / MB/s / goodput / iperf 命中 0 个文件。但存在一整类故障没有任何 error string 可抓——每个请求都返回 200, 每项健康检查都绿,系统却因为字节以应有速率的几分之一到达而不可用。 新增内容,全部来自一次实测过的真实事故: Step 0.7 —— 决定性实验是「两条共享网络路径、但不共享任何应用代码的通道,同方向、 同时间预算测速率」。两条一致地慢 → 瓶颈在链路,别再调应用;A 慢 B 快 → 在应用。 两条命令都是时间预算制而非固定字节:固定 8MB 在健康链路上不到一秒、在降级链路上 约 90 秒,而更深的降级会无界放大等待,恰好在最耗不起的时候。 原则 3 加容量变体(同一技术,换的是「栈」不是「跳数」);原则 5 加量级轴—— 探针在链路成为限制因素之前就结束,就测不到链路。案例里健康端点全天 40ms 不变, 横跨链路容量 100× 的摆动。 陷阱 16-18: - 把「路径类型」字段读成「路径容量」。同一个 direct、同一个字段,在 0.09 和 14 MB/s 两种状态下读数完全一样;RTT 更是反向指标(慢的那次 12ms,快的 4ms)。 - 把自己探针的锅算到远端头上。损坏的 key 走的是更慢的路径(索引未命中 → 全表扫描 → 报错),完美伪造出「对端吃力」的 signature。判据是载荷大小:21 秒送 17 字节 不是带宽症状。 - 错误的容量假设会静默产出损坏产物并通过成功检查。真实的检查是 `HTTP 200 && bytes > 1000`,听起来像个正经完整性检查,却放过了 35 个下载里的 14 个截断文件——被砍断的多兆文件轻松超过任何你想得到的阈值。没有任何字节下限 能区分「完整」和「大部分」,那是文件格式的属性。 新 case study 按本 skill 自己的体例写:四个错误转弯逐个列出,每个都是合理的推断 + 真实的测量 + 量错了东西。 --- 两轮独立审阅,共 15 条 findings 全部处置(档案在私有知识库,非本仓)。其中: - 轮一给的一条修复建议被拒绝。它让我用 dd 自报速率作为 channel B 的读数,实测那 量的是「写进管道」不是网络——同一次传输 dd 自报 54.9 MB/s、端到端 44.26 MB/s。 改用「固定时间预算 + 接收端计数」,两条命令均已实验验证(含 curl --max-time 中途被砍时仍打印 -w 速率,exit 28)。 - 轮二 7 条里有 4 条是轮一修复自己引入的,含两个我编的数字(「8MB 要十分钟」实为 73-89 秒;「慢到一分钟挪不动 4MB」实为 36-44 秒),以及一条 blocking:判据表从 2 行扩到 4 行后,前面那句为 2 行写的压缩公式「agreement implicates the path」 就成了矛盾,且矛盾方向恰好是鼓励过早结案。 - 判据表补上「探针根本没连上」的自检:SSH 免密没配好会静默读到 0,被表读成「慢」, 直接推向「是链路」——这正是本 skill 陷阱 17 在配方内部的复现。 顺带修正一处既存缺陷:「详见 cognitive-traps.md」的指针在基线里就已部分失效 (该文件只有 Trap 1-12,而 SKILL.md 基线已有 13-15)。改为如实指路,未擅自迁移。 description 达 1020/1024,新增触发词是零和交易:删掉结尾一句纯方法论摘要 (不承担触发职能,内容在正文原则 2/3 里更完整),并放弃一条候选触发词,压回 1013。 闸门:quick_validate 通过;security_scan 无 secret(1 条 MEDIUM 为内网服务占位符 的误报,基线本有同类);回归审计 829 项精确保留、4 候选全分类;脱敏后真实 IP/主机名/ 内网段零命中,另经逐字语义复核抓出一处无关键词的指纹并改为中性示例。 Tier 3 的配对 A/B 评测经用户明确豁免,记为 user-waived 而非冒充通过;触发词有效性 未测。两项均在审阅档案的「未能核查」节列出。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019oKLM3zqoWUFAH6wY5W199
|
Small cross-reference drift worth folding into this PR while it's open — not a blocker, and not worth its own PR.
That phrasing was retired in tunnel-doctor by #294 (merged). The reason it went: under TUN every hostname resolves to a fake IP — on success as well as on failure — so "DNS hijack" describes the normal operating mode, not a diagnosis, and Low severity: a reader who follows the routing lands on the corrected Step 2H, so nothing here misleads them for long. It's just that the router still uses the retired vocabulary. Suggested edit to that row, if you're touching the file anyway — drop the label and keep the observable symptom:
Flagging rather than editing because this file has an open PR (this one) and uncommitted local changes, so it has an owner already. |
为什么
这个 skill 原本会主动把降速类故障劝退:Step 0 写着「'Slow' is not a symptom」,把「用户说慢」列为不合格前提,全仓
throughput/MB/s/goodput/iperf命中 0 个文件。但存在一整类故障没有任何 error string 可抓——每个请求返回 200,每项健康检查都绿,系统却因为字节以应有速率的几分之一到达而不可用。整套 error-driven 工具链在它面前无处下嘴。
加了什么
Step 0.7 — 决定性实验:两条共享网络路径、但不共享任何应用代码的通道,同方向、同时间预算测速率。
命令是时间预算制而非固定字节:固定 8MB 在健康链路上不到一秒、在降级链路上约 90 秒,更深的降级会无界放大等待。判据前有强制自检——「探针根本没连上」会读出低/零速率,被表读成「慢」,直接推向错误结论。
原则 3 加容量变体(换的是「栈」不是「跳数」);原则 5 加量级轴:探针在链路成为限制因素之前就结束,就测不到链路(案例里健康端点全天 40ms 不变,横跨容量 100× 的摆动)。
陷阱 16–18 — 把路径类型读成路径容量(同一个
direct、同一个字段,0.09 和 14 MB/s 读数一样;RTT 反向);把自己探针的锅算到远端头上;错误的容量假设静默产出损坏产物并通过成功检查。新 case study 按本 skill 体例:四个错误转弯逐个列出,每个都是合理的推断 + 真实的测量 + 量错了东西。
质量证据
两轮独立审阅(fresh-context,非 fork),共 15 条 findings 全部处置,档案在私有知识库。
dd自报速率,实测那量的是「写进管道」不是网络(同一次传输 dd 自报 54.9 MB/s、端到端 44.26 MB/s)。findings 是假设不是判决。顺带修正一处既存缺陷:「详见 cognitive-traps.md」的指针在基线里就已部分失效(该文件只有 Trap 1–12,而 SKILL.md 基线已有 13–15)。改为如实指路,未擅自迁移 6 条 trap。
闸门:
quick_validate✅ |security_scan无 secret ✅(1 条 MEDIUM 为内网服务占位符误报,基线本有同类)| 回归审计 829 项精确保留、4 候选全分类 ✅ | description 1013/1024 ✅description 达 1020/1024 后新增触发词是零和交易:删掉结尾一句纯方法论摘要(不承担触发职能,内容在正文原则 2/3 里更完整),并放弃一条候选触发词,压回 1013。
未做的事(如实标注)
throughput collapse/"it works, it's just slow"等短语能否赢得路由,未跑run_loop也未用claude -p实探timeout在部分 BSD 需gtimeout,未在真 BSD 复验