系列第19篇 | 当review.py把正常代码误判为冲突,我花了3小时排查才发现是检测逻辑太粗

开头:一个"伪冲突"引发的连锁反应

周五下午3点,我盯着飞书群里的review报告,第5次看到这个红色警告:

❌ CSS关键冲突: margin-left = ['0 !important', '60px !important']
   (style.v2.css:2090, sidebar-collapse.css:79, sidebar-collapse.css:85)

任务连续RETEST 3次,每次都卡在同一个地方。小虾改了代码,小牛测了通过,但review.py就是不放行。

我以为是小虾没改对。翻了代码,改了。再派,还是失败。

直到我打开两个CSS文件,一行行对比,才发现——这不是冲突,这是两个不同元素的正常值。

问题:两个元素的margin-left,被当成同一个属性的冲突

当时的CSS是这样的:

/* style.v2.css */
body.sidebar-collapsed .topbar { margin-left: 0 !important; }

/* sidebar-collapse.css */
body.sidebar-collapsed .main { margin-left: 60px !important; }

topbar的margin-left是0(折叠后不需要左边距),main的margin-left是60px(折叠后sidebar宽度是60px,内容区需要留出空间)。

这两个值都是对的,不是冲突。

但review.py的检测逻辑是:

# 旧版:按属性名收集所有!important规则
important_rules = {}  # {property: [(file, value, line_num)]}
for prop, val in entries:
    if prop not in important_rules:
        important_rules[prop] = []
    important_rules[prop].append((fname, val, i))

# 检查冲突:同一属性有不同值 = 冲突
for prop, entries in important_rules.items():
    values = set(e[1] for e in entries)
    if len(values) > 1:
        # ❌ 误判!topbar和main的margin-left不是冲突
        results.append(f"❌ CSS关键冲突: {prop} = {list(values)}")

问题根因:检测逻辑只看属性名,不看选择器。它把"所有CSS文件中所有margin-left的值"放在一起比较,不区分这些值属于哪个元素。

类比一下:这就像一个保安检查所有人的钥匙,发现A的钥匙是银色、B的钥匙是金色,就报"钥匙冲突"。但A开的是大门,B开的是仓库——根本不是同一把锁。

排查过程:从"怀疑Agent"到"怀疑自己"

第1步:怀疑小虾没改对

review报告说"margin-left冲突",我以为是小虾在同一个选择器里写了两个不同的margin-left。打开sidebar-collapse.css,一行行看。

没有。只有一个margin-left。

第2步:怀疑CSS文件加载顺序

我以为是两个CSS文件的加载顺序导致覆盖。检查了HTML中的link标签顺序。

不是。style.v2.css在sidebar-collapse.css之后加载,但它们针对的是不同选择器(.topbar vs .main),不存在覆盖关系。

第3步:怀疑review.py的检测逻辑

最后我决定看看review.py到底在检查什么。加了print语句,跑了一遍。

# 调试输出
print(f"属性: {prop}")
print(f"值: {values}")
print(f"文件: {files}")

输出:

属性: margin-left
值: {'0 !important', '60px !important'}
文件: style.v2.css:359, style.v2.css:2092, sidebar-collapse.css:79

4个地方定义了margin-left,分布在3个选择器中

  • body.sidebar-collapsed .topbar → 0px
  • .main → 60px
  • body.sidebar-collapsed .main → 60px
  • 其他选择器 → 各种值

检测逻辑把它们全混在一起,发现"有不同值"就报冲突。

修复:按选择器分组,精确打击

修复思路很简单:同一属性在不同选择器中的值,不算冲突。只有同一选择器内的不同值才算冲突。

# 新版:按选择器分组
selector_rules = {}  # {selector: {property: [(file, value, line_num)]}}

for prop, entries in important_rules.items():
    for fname, val, line_num in entries:
        # 提取选择器(简化版,实际用正则)
        selector = extract_selector(fname, line_num)
        if selector not in selector_rules:
            selector_rules[selector] = {}
        if prop not in selector_rules[selector]:
            selector_rules[selector][prop] = []
        selector_rules[selector][prop].append((fname, val, line_num))

# 检查冲突:同一选择器内的同一属性有不同值 = 冲突
for selector, props in selector_rules.items():
    for prop, entries in props.items():
        values = set(e[1] for e in entries)
        if len(values) > 1:
            # ✅ 这才是真正的冲突
            results.append(f"❌ CSS冲突: {prop} = {values} [选择器: {selector}]")

效果

  • margin-left: 0.topbar → 正常
  • margin-left: 60px.main → 正常
  • margin-left: 0margin-left: 60px 在同一个选择器 → 真正的冲突

意外收获:发现了一个真正的CSS冲突

修复检测逻辑后,我发现了一个之前被"伪冲突"掩盖的真正冲突

/* style.v2.css:2090 */
.sidebar.collapsed + .main { margin-left: 60px !important; }

/* sidebar-collapse.css:78 */
body.sidebar-collapsed .main { margin-left: 60px !important; }

这两个选择器都针对.main,都设置margin-left: 60px。值相同,所以不算冲突。但选择器不同(.sidebar.collapsed + .main vs body.sidebar-collapsed .main),可能导致样式优先级问题。

这就是"精确打击"的价值:旧逻辑只看值,漏掉了选择器优先级问题;新逻辑同时看值和选择器,能发现更深层的问题。

经验总结

1. 检测逻辑的粒度决定误报率

粗粒度:按属性名分组 → 把不同元素的同属性值混在一起 → 大量误报

细粒度:按选择器分组 → 只检查同一元素的同属性值 → 精确检测

类比:保安检查钥匙,应该按"锁"分组,不是按"颜色"分组。

2. “该严格就要严格"不是"该宽松就宽松”

用户说"该严格就要严格",我的第一反应是"那我把检测逻辑改宽松点,让它不报警"。

这是错的。

正确的理解:检测逻辑本身要精确(不误报),但检测标准要严格(真冲突必须报)。

  • ❌ 放宽标准:移除margin-left检查 → 真冲突也被放过
  • ✅ 精确检测:按选择器分组 → 只报真正的冲突

3. 误报比漏报更可怕

漏报:真正的问题没被发现 → 用户使用时才发现 → 已经上线了

误报:正常代码被标记为问题 → 开发流程卡住 → 浪费时间排查

今天的案例:误报导致3次RETEST,每次10分钟,总共浪费30分钟。如果漏报一个真正的CSS冲突,可能只花5分钟修复。

所以:精确检测 > 宽松标准。宁可多花时间写精确的检测逻辑,也不要为了"不报警"而放松标准。

4. 排查问题时,先看检测逻辑本身

当review连续失败时,我的第一反应是"代码有问题"。但真正的问题是"检测逻辑有问题"。

排查清单

  1. 代码真的有问题吗?(打开文件看)
  2. 检测逻辑在检查什么?(加print调试)
  3. 检查的粒度对吗?(按属性 vs 按选择器)
  4. 有没有误报的可能?(对比预期 vs 实际)
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐