修复: 反面对照的**样本表自己没人守** —— 删掉 5 个样本里的 3 个,默认跑 0 条 RED、静默缩水

我自己变异出来的(M45)。这是 `957ec5e` 新加那道防线**内部**的缺口,
而且缺口长在"**覆盖**"这件事本身上。

## 一、缺陷:`发现规则在 N 种写法上都有对照` 当时是**散文**,不是判据

实测:把 `FORM_SAMPLES` 的 5 个样本**删掉 3 个**(只留 `function 声明` 与 `const = async`)
⇒ 默认跑 **0 条 RED**、`red` 仍是 9(与干净树逐字相同),
只是成功报文里那半句从"在 **5** 种写法上都有对照"变成"在 **2** 种写法上…"。

⇒ 它把 N 打出来,而 **N 变小没有任何东西拦**。
而"样本齐不齐"正是这条防线**唯一的防线** —— 样本可以被悄悄删光,
防线的覆盖就静默退回到"只验我恰好留下的那一种"。
**这是"判据存在 vs 判据在路径上"的又一个变体:样本在,但"样本够不够"没有判据。**

★ 与我上一轮那条同源、但换了一层:`957ec5e` 修的是"**发现规则看不见某种写法**",
这次是"**样本表可以静默缩水**" —— 修掉前者之后,后者的存在才显出来。
**每修一层,缺口换一层**,这与 `baseline` 那条线"接缝会移动"是同一个观察。

## 二、修法:要求清单**具名**,两个方向都判

· 列出 `REQUIRED_FORMS`(要覆盖哪些写法,**具名**);
· ① 每种要求的写法都必须有样本(⇒ **删样本 = 红,并指名删的是哪一种**);
· ② 每个样本都必须能被认出来(原有方向);
· ③ 多出来的样本未登记进 `REQUIRED_FORMS` ⇒ 也红(加了样本却没说守哪种写法)。

★ 为什么不用 `assert length >= 5`:那个数**自己**就是可以随手改小的常量,
改小它不会留下任何痕迹。具名清单让"我要放弃支持箭头函数写法"变成一次**看得见的声明**。
成功报文也随之带上"(要求 5 种,缺一即红)",让**要求**和**实际**同时可见。

## 三、变异验证(都已还原)

| 变异 | 结果 |
|---|---|
| **M45** 删掉 3 个样本 | **红**:`这些写法要求覆盖、却没有样本:箭头函数、async function、名与括号间有空格`,`red=9→10`(**修复前 0 条 RED**) |
| **M46** 加一个未登记的多余样本 | **红** 2 条:`不在要求清单里` + `发现规则认不出这些写法` |
| 回归:**M40/M41/M42** 箭头/async/带空格三种未接线写法 | 各 **1 条报警**(`957ec5e` 的修复未被破坏) |
| 回归:**M43** 发现规则退化回窄正则 | **红**:`认不出…` |

## 四、验证与状态

· 五个自检单独跑仍全 rc=0;默认跑 `checks=459 pass=455 fail=4 skip=0 red=9 … verdict=red`、
  `mutants=48 ran=47 skipped=1 on_new_criteria=35 diag=none baseline=7/7✓`;
  自检段现为 `…发现规则在 5 种写法上都有对照(要求 5 种,缺一即红)`。
· 残留全清;`git status` 仅本文件;`HEAD=957ec5e`。
· 同期 pi 已把我在它插件里报的那例修好并提交 `cc8beb7`(`--self-check` 改进程内 + 默认路径先跑 +
  量纲分三档)。我**复核过**它:干净树 rc=0 且自检真出现在默认路径上,变异 `findDuplicates → []`
  ⇒ `npm test` **rc=3** 并打出"工具坏了,不是被测代码坏了"。它的目录我一行未动。
This commit is contained in:
2026-09-18 07:23:31 +08:00
parent cc8beb79db
commit 8691940505

View File

@ -2370,6 +2370,40 @@ let selfCheckRuns = 0;
['名与括号间有空格', 'function fooSelfTest (a) {}\n'],
['const = async (…', 'const fooSelfTest = async (a) => {};\n'],
];
/*
* ★★ **样本表本身也必须被钉住**(我自己变异出来的 M45)。
*
* 实测:把 5 个样本**删掉 3 个**(只留 `function` 声明与 `const = async`)
* ⇒ 默认跑 **0 条 RED**、`red` 仍是 9,只是成功报文里那半句从
* "在 **5** 种写法上都有对照" 变成 "在 **2** 种写法上…"。
* ⇒ 也就是说 **"发现规则在 N 种写法上都有对照" 当时是散文,不是判据**:
* 它把 N 打出来,而 N 变小**没有任何东西拦**。
* 而"样本齐不齐"正是这条防线**唯一的防线** —— 样本可以被悄悄删光,
* 防线的覆盖就静默地退回到"只验我恰好留下的那一种"。
* **这是"判据存在 vs 判据在路径上"的又一个变体**:样本在,但"样本够不够"没判据。
*
* ⇒ 修法:把**要求覆盖的写法**列成一份**具名的**清单(`REQUIRED_FORMS`),
* 然后两个方向都判:
* ① 每种**要求**的写法都必须有样本(⇒ 删样本 = 红,不再是静默);
* ② 每个样本都必须能被认出来(原有方向)。
* ⇒ 删样本现在会红,而且**指名**删的是哪一种。
* (为什么不是"断言 `length >= 5`":那个数**自己**就是可以随手改小的常量;
* 具名清单至少让"我要放弃支持箭头函数写法"变成一次**看得见的声明**。)
*/
const REQUIRED_FORMS = ['function 声明', '箭头函数', 'async function', '名与括号间有空格', 'const = async (…'];
const sampleNames = FORM_SAMPLES.map(([w]) => w);
const noSample = REQUIRED_FORMS.filter(w => !sampleNames.includes(w));
if (noSample.length) {
console.log(`RED 这些写法**要求覆盖、却没有样本**:${noSample.join('、')}`
+ ' ⇒ 覆盖静默缩水(发现规则对那种写法可能就是瞎的)');
reds.push(`(自检)\`FORM_SAMPLES\` 缺了要求的样本:${noSample.join('、')}`
+ ' —— 覆盖缩水必须看得见,不能只改一个数字');
}
const stray = sampleNames.filter(w => !REQUIRED_FORMS.includes(w));
if (stray.length) {
console.log(`RED 这些样本不在要求清单里(加了样本却没说是为了守哪种写法):${stray.join('、')}`);
reds.push(`(自检)\`FORM_SAMPLES\` 有多余样本未登记进 \`REQUIRED_FORMS\`:${stray.join('、')}`);
}
const missed = FORM_SAMPLES.filter(([, src]) => !declaredSelfTests(src).includes('fooSelfTest'));
if (missed.length) {
console.log(`RED 发现规则认不出这些写法(⇒ 那种写法写了自检也不会被接线防线看见):`
@ -2404,7 +2438,7 @@ let selfCheckRuns = 0;
console.log(`自检段:${SELFTESTS.length} 个自检已在**默认路径**内跑过`
+ `(${SELFTESTS.map(([n]) => `--${n}`).join('、')});`
+ `源码里 ${declared.length} 个 \`*SelfTest\` 函数**全部**在册(无"写了没接线");`
+ `发现规则在 ${FORM_SAMPLES.length} 种写法上都有对照`);
+ `发现规则在 ${FORM_SAMPLES.length} 种写法上都有对照(要求 ${REQUIRED_FORMS.length} 种,缺一即红)`);
}
}