跨端: 采纳"记录投影"根治:删掉整条源码扫描器(含五轮补丁与自检),四个计数器改为从 records 投影;新判据是"记录数==文件数"的行为判据
pi 2026-09-15 两条都成立,第二条我**采纳并实现了**(`ef1c1a0` 之前的这轮改动)。 ## 一★ "正则字面量里的引号"是**假绿方向** —— 我实测,比你写的还准 我用真 `stripStrings` 跑了你说的样本,**方向确认为"躲",而且躲掉的是真写**: ``` 输入: const RE = /["']/; totalTests += 1; 抹除后: const RE = /[" ← 从这个 " 起一路吞到下一个 " 命中: 0 处 ← 你写的 0,我复现 ``` ★ 更要紧的是**假绿那一支**,我用"把第二处写藏进被吞区间"直接验了: ``` 输入: const RE = /["x]; totalTests += 1; OK"]/ ; totalTests += 1; 抹除后: const RE = /[" "]/ ; totalTests += 1; 命中: 1 处(**未抹除时是 2 处**) ``` ⇒ 真身**有两处写**,判据数出 **1** ⇒ **绿**。而"第二处写"正是这条判据**唯一存在的理由** ⇒ 这是**绕过**,不是误伤。我注释里原来写的是"能躲或误伤" —— 词是对的,**但读不出严重性**, 已按你的话改成"**这一侧是假绿**",并写明**绿的准确含义**: "**在它能看见的文本里**没有第二处写",不是"没有第二处写"。 ★ 你指的第二点也修了:三条样本原来只要求 `truePosRaw > 0`("至少一条命中") ⇒ 其中一条哪天不再是假阳性,自检**照样绿**。现在**逐条断言**"未抹 >0 且抹后 0", 并且失败报文分开说**是哪一侧**(未抹=0 ⇒ 这条样本白测了;真写被吃 ⇒ 假绿方向)。 变异验过:把样本 #1 的分号去掉 ⇒ 自检立刻响(旧写法**照样绿**)。 ## 二★★ 投影:我**采纳了**,而且它确实把整族一起消掉了 你说得对,纯函数**仍然可以被调用两次** —— 那只是把"覆盖哪条路径"换个地方问一遍。 按你的形状改了: ```js records.push({ file, checks: fileChecks, fail: …, exitCode: r.status }); // 唯一的写点 // 循环之后: const reportedRecords = records.filter(r => r.checks !== null); totalTests = reportedRecords.reduce((n, r) => n + r.checks, 0); totalFail = reportedRecords.reduce((n, r) => n + r.fail, 0); ranFilesCount = reportedRecords.length; unreportedFiles = records.length - reportedRecords.length; ``` **删掉的东西**(这才是重点,不是"又加了一版"): - 整条**源码扫描器**(注释 → 字符串 → `= X + n`/`X++` → 锚钉行首 vs 语句起点 → 正则引号,**五轮补丁**); - 它的**抹除器自检**(`mkWrite` / 三条样本 / 逐条断言)—— **没有对象可扫了**; - `stripStrings` 在 `run-all.mjs` 的 **import**(留着不用的 import 就是下一次"看着像还在用"的源头)。 `stripStrings` 本身留在 `read.mjs`(通用工具),但它那条"正则引号"的边界**现在没有任何判据依赖** —— 这一族的风险从"判据可能假绿"降级成"一个没被用的工具函数有已知边界"。 **行为判据**(替代那条文本判据): ```js if (records.length !== SUITE.length) { … 并列出重复的文件 … } ``` **它抓得住那个形状**(注入"同一个文件 push 两次"实测): ``` RESULT files=25 ran=50 checks=800 … ✗ 套件自检 5:跑出的记录数 50 ≠ 套件文件数 25 —— 每个文件**恰好一条**记录 重复的文件:test/xxx.test.mjs×2 ``` **行为等价性**:`RESULT files=25 ran=25 checks=400 pass=399 fail=1 red=4 broken=0 unreported=0` 与改前**逐字节相同**。 ★ 顺带修掉一处**我自己造的**同类毛病:`ok 5` 的报文原来还写着"四个计数器各只有一处'写', **由源码扫描判**" —— 判据已经换了,文案没换。已改成"四个计数器由 25 条记录**投影**而来 —— 判的是记录数==文件数这个**行为**"。**改了判据却留着旧文案,就是又一次"说法与实现不一致"。** ## 三、★ 我要修正你一句措辞(实测后才敢说) 你写"**'把一个文件计两次'没有对应的语句可以写**"。我实测后认为**过强**: `records.push` **仍然可以被复制粘贴两次**(上面那条注入就是), 而且那正是历史上真实发生的形状(搬动累加块时把旧路径留在原地)。 准确的说法是:**重复不再表现为"某个计数器被加两次",而表现为"同一条记录出现两次"** ⇒ 它可以被**行为**判据抓住,而不是只能靠猜文本。 ★ **这是"文本判据 → 行为判据",不是"可能错 → 不可能错"。** 前者可测,后者只是话好听 —— 我不想把这次改进说成后者。 (我第一版就在注释里写了"写不出来",然后自己想了想不对,改成上面这句。) ## 四、残余与射程 - 上面那条"记录数==文件数"是**唯一**的新判据;`ranFilesCount > SUITE.length` 留着当 "投影被改坏"的探测器(投影下有构造性保证,所以它是冗余的 —— 我在注释里写明了"冗余", 不假装它还是主力)。 - 【本轮未做】纯函数形状**没有**保留(按你的建议直接上投影了)。 - 其余状态:`ok 5`/`ok 6` 绿、红线仍 4 条(都不是我的)、探针残留 0。
This commit is contained in:
@ -53,11 +53,26 @@ export function code(path) {
|
||||
* ★ **行号必须不变**(与 `stripComments` 同一条硬要求):字符串里的每个换行
|
||||
* 换成等价数量的空行。而且**保留引号本身**,这样"这里原本有个字符串"仍然看得见。
|
||||
*
|
||||
* ⚠️ 已知限制,写清楚免得下一个人以为它比实际更强:
|
||||
* 逐字符扫描,**不区分正则字面量** —— `/a"b/` 里的 `"` 会被当成字符串开始
|
||||
* (随后一直吃到下一个 `"`)。本仓判据里没有把真计数器名写进正则字面量的情形,
|
||||
* 但这是**这个实现的边界**,不是"已经解决"。
|
||||
* (真正根治是"判行为而不是判文本":把被扫的逻辑抽成纯函数,拿合成输入喂它。)
|
||||
* ⚠️ 已知限制,**方向是"假绿",不是"误伤"** —— 这一点我第一版写错了,pi 2026-09-15 测出真方向:
|
||||
* 逐字符扫描,**不区分正则字面量**。单个**不成对**的引号就够把后面一大段当字符串吞掉:
|
||||
*
|
||||
* 输入: const RE = /["']/; totalTests += 1;
|
||||
* 抹除后: const RE = /[" ← 从那个 " 起,一路吞到**下一个 "**
|
||||
* 命中: 0 处
|
||||
*
|
||||
* 两种后果,**要防的是第二种**:
|
||||
* ① 常见的是**假红**(唯一那处写被吞 ⇒ 报"出现 0 处",而文案还指不到真因);
|
||||
* ② **假绿**:被吞的区间里正好藏着**第二处写**,而第一处写在区间之外 ⇒ 计数仍是 1
|
||||
* ⇒ **"重复"就在那里,判据却绿**。
|
||||
* 实测:`const RE = /["x]; totalTests += 1; OK"]/ ; totalTests += 1;`
|
||||
* ⇒ 真身 **2** 处,抹除后只数出 **1** 处。
|
||||
*
|
||||
* ★ 而"第二处写"正是这条判据**唯一存在的理由** ⇒ ② 是**绕过**。
|
||||
* 所以这条判据的绿只能保证:"**在它能看见的文本里**没有第二处写" —— 不是"没有第二处写"。
|
||||
* **把绿当成证明,就会在这里栽。**
|
||||
*
|
||||
* (修它需要词法状态机 —— 不划算,因为下一版要改成"记录投影",
|
||||
* 那时"计两次"根本没有对应的语句可写,这一族连扫描对象都不存在。)
|
||||
*/
|
||||
export function stripStrings(src) {
|
||||
let out = '';
|
||||
|
||||
Reference in New Issue
Block a user