#!/usr/bin/env python # -*- coding: utf-8 -*- """变异对照:证明自检的判据不是「恒绿」。 做法:把原型复制一份,往里注入一处**已知的假病**,再跑一次自检, 要求它必须报红,并且红在**预期的那一条**上。测完即删临时件。 为什么要这个:一个永远 PASS 的自检等于没有自检。只有「注病 → 报红 → 复原 → 回绿」 这条链走通了,前面那串 PASS 才算证据。 用法:python _tools/mutate_test.py """ import os, re, subprocess, sys, tempfile, html HERE = os.path.dirname(os.path.abspath(__file__)) UI = os.path.abspath(os.path.join(HERE, "..")) PROTO = os.path.join(UI, "mcn-workbench.html") PY = sys.executable # (名称, 说明, 查找串, 替换串, 期望出现的报红片段, 视口[可选,默认 1440x900]) MUTANTS = [ ("M1 死控件", "把 P2-01「低粉爆款」的实现掏空 —— 点它不再有任何反馈", "'P2-01':()=>{ S.benchFilter='低粉爆款'; paint(); },", "'P2-01':()=>{},", "P2-01"), ("M2 未登记的可点元素", "在页面上塞一个没写进交互清单的 data-act。" "⚠️ 2026-10-08 改:P2 的「对标内容列表」已由行式列表改成满宽表格," "原来那个注入点(.action-cell 里的 P2-03)**在文件里已经不存在了** —— 脚本本来会直接" "判「找不到注入点、跳过」,那等于这条变异悄悄失效 ⇒ 注入点跟着形态换到表格那一行。", "`;", " const close=``;", "中性关闭出口 = 2"), ("M14 外壳高度链没约束住(整页滚不动、折叠线以下拿不到)", "把 .app 的 height:100vh 退回 min-height:100vh —— 就是 2026-10-08 修掉的那个真缺陷的原样:" "主区跟着内容长高、自己不滚,滚动落回 ,而 body 是 overflow:hidden," "于是内容掉到折叠线以下就永远拿不到", "grid-template-rows:minmax(0,1fr); height:100vh; }", "grid-template-rows:minmax(0,1fr); min-height:100vh; }", "高度链没约束住"), ("M15 用内联 style 表达选中态(组件口径被破坏)", "把 P2 选题库分组那个分段控件改回「`.btn btn-sm` + 内联 border-color:var(--ink)」的写法 ——" "这正是 2026-10-08 统一掉的那套旧画法:选中态一旦能被内联样式随便盖," "全站就会出现第三套、第四套画法,而「选中态只有一套」这件事就没人看着了。" "⚠️ 这条变异是给新增判据「组件口径」(自检第 84 项)配的对照 —— " "没有它,那条判据是不是恒绿就无从证明。", "