docs(spec): close the P15-B branch review's document-side findings
The P15-B branch review returned "needs fixing before merge" on a critical gap in the guard layer. The code side is with a second fix-forward; this commit closes the document side, which is the controller's. F-FINAL-4 - the leg-8 cell carried a one-line regex that the controller wrote into the spec during re-pin. It was worse than the version in the adjudication it copied: 4 of 7 attribute forms fail rather than 2, because markdown escaping turned the alternation bar into a literal bar and `'[^']*'` was mistyped as `'[^'*]`. The controller had checked that every table row has the same number of pipes as the header, but never checked that the escaped code was still the code it came from. The cell now describes the implemented approach (extract every style attribute value in all three quoting forms, strip quotes, test each for color:) and deliberately carries no regex literal, with the reason stated: an escaped bar inside a table cell is a literal character in the regex. Read the code entity for the pattern. §6-1 - leg 9 was never re-pinned: the spec still said "length > 500" while the code says 4000 plus three structural preconditions. Corrected, with the unit spelled out (source characters 4423, not the built artifact's 7056/7588). §6-2 - "the only discriminating grid" survived re-pin in four places; the review measured two legs reddening on a real decision-order defect. Narrowed to "the only discriminating grid on the child-side direct-visit path" here and in the plan. The two occurrences in code comments belong to the fix-forward. §6-3/§6-4/§6-5 - the §1.2 count now states which tree it came from (base 66, HEAD 67, the extra one being this batch's own note text quoting the figure); the §4 boundary table lists the e2e spec it had omitted; D-G says "form aligns" rather than "aligns verbatim", since the script bodies differ by design (the main app has one more level, 16 token-level differences). F-FINAL-1 - two claims already committed are corrected rather than deleted. The §3.1(a) annotation and the §7 risk row both rested on "maskSourceComments() neutralises the trap comment, so the guard does not depend on the implementer's wording discipline". That is true for the file in question, but only because the trap comment happens to use `//`: the function's HTML-comment branch compared a 2-character slice against a 4-character literal and never ran. Merely rewording a comment reddened up to four legs while the e2e suite stayed green. §8 gains the census case for discipline 15 (an unlanded census script is why the 48-versus-47 discrepancy is still untraceable), plus disciplines 16 and 17: every evidence file a report cites must be ls-checked before delivery, and executable details in a spec must be grepped from the code entity or recomputed, never hand-written. Discipline 17 records this batch's three instances, one of them a contrast ratio cited twice by two documents and recomputed by nobody (3.0269; the actual value is 2.5519, which no grey background produces). Post-write checks: 25/25 assertions, 11 table blocks with 0 column anomalies, 16 code fences paired, 25 headings with no duplicates, copyable-block cleanliness scanned across 8 blocks.
This commit is contained in:
@@ -106,7 +106,7 @@ const targets = computed<RuntimeTarget[]>(() => resolveRuntimeTargets(props.game
|
||||
```
|
||||
|
||||
**缺口**:`ink on surface` **未钉**,而它是**所有未钉配对里使用面最大的**:
|
||||
- `bg-surface` **`.vue` 模板内 62 处**(`app/` + `runtime/`);⚠️ 整个 `src` 是 **66 处**(另 4 处在 `.test.ts`:BaseInput/BaseTabs/BaseTextarea/FileInput 各 1)→ 原写「全仓 62 处」与 66 冲突(RE-PIN 2026-10-03)。**不影响结论**:Tailwind 只从 `.vue` 模板生成 utility,`.test.ts` 里的字面量属 §8-8 的另一回事
|
||||
- `bg-surface` **`.vue` 模板内 62 处**(`app/` + `runtime/`);⚠️ 整个 `src` 是 **66 处**(另 4 处在 `.test.ts`:BaseInput/BaseTabs/BaseTextarea/FileInput 各 1)→ 原写「全仓 62 处」与 66 冲突(RE-PIN 2026-10-03)。**不影响结论**:Tailwind 只从 `.vue` 模板生成 utility,`.test.ts` 里的字面量属 §8-8 的另一回事(⚠️ RE-PIN 2026-10-04 补口径,闭合终审 §6-3:以上是 **base 树 `f823195`** 的计数;HEAD 树上整个 `src` 是 **67** 处、`.test.ts` 是 **5** 处——多的 1 处正是本批在 `contrast.test.ts` 的 AA_PAIRS note 文案里引用了这个数字。**结论不受影响**:Tailwind 只从 `.vue` 模板生成 utility,`.test.ts` 里的字面量属 §8-8 的另一回事)
|
||||
- 其中只有 **4 处**显式写 `text-ink-soft`(已钉)、**2 处**的 `text-paper` 属三元表达式**另一支**(P15 账本 §1 第 19 次错误:把互斥分支当同元素共现,是假阳性)
|
||||
- **58 处靠继承**:全局文字色源是 `src/index.html:34` `<body class="bg-paper text-ink font-sans antialiased">` → 继承到 **`ink`**
|
||||
- 实测对比度:亮 **18.4225** / 暗 **14.8468** → **必然通过 4.5**
|
||||
@@ -156,7 +156,7 @@ const targets = computed<RuntimeTarget[]>(() => resolveRuntimeTargets(props.game
|
||||
| **D-D** | 新守卫的形态 | **新增 `app/lib/bootstrapTheme.test.ts`**(源码级、node 环境、读文件文本),**不扩展 `noHardcodedColor.test.ts`** | §1.4:`noHardcodedColor` 的 `candidates()` 只收 `.vue`(`if (file.endsWith('.vue'))`),且 `maskNonTemplate()` 把 `<style>` 块整体等长空白掉 → **它从设计上无法覆盖 `bootstrap/index.html`**(HTML 文件 + 颜色全在 `<style>` 里)。扩扫描根不会有任何效果 |
|
||||
| **D-E** | inline `style` 的两处违规 | **删掉 inline 的 `color` 声明**(保留 `font-size`),让样式表的达标值生效 | §1.1 实测:inline 是冗余重复且对比度更低(2.2594 / 2.4776 vs 样式表 6.3586 / 4.8700)。删 inline 即修复,**同时消除"两处定义同一元素颜色"的分歧源** |
|
||||
| **D-F** | 暗色实现方式 | 在 `bootstrap/index.html` 的 `<style>` 块里用 **`html[data-theme="dark"]` 覆盖**(同主应用 `main.css:44` 的手法),**不用 `@media (prefers-color-scheme)`** | 主应用用 `data-theme` 属性驱动(`color-scheme` 也由它驱动,D-L),使"显式选择能压过系统偏好"。bootstrap 若用 `@media` 就只能跟随系统、无法接收 hash 传入的显式主题 → **两套机制会让判定序无法一致**(违反 D-A) |
|
||||
| **D-G** | pre-paint 脚本形态 | **IIFE + 仅 `var`,置于 `<head>` 内、任何 CSS 与 `<body>` 之前**,逐字对齐主应用 `src/index.html:15-31` 的写法 | 主应用该形态由 `themeBootstrap.test.ts` 腿 4("脚本在 `<head>` 内、`<body>` 之前")与腿 5(IIFE + var)钉住,理由是 CSP 落地前不能用 module/let。**同款形态让两处可被同一套守卫断言** |
|
||||
| **D-G** | pre-paint 脚本形态 | **IIFE + 仅 `var`,置于 `<head>` 内、任何 CSS 与 `<body>` 之前**,**形态**逐字对齐主应用 `src/index.html:15-31`(IIFE / 仅 `var` / 位置 / catch 兜底 / 媒体查询字面量五项)——⚠️ **脚本体本身不同**:主应用多 stored 一级(D-A 明列),token 级差异 16 处;RE-PIN 2026-10-04 闭合终审 §6-5,原措辞「逐字对齐…的写法」会被读成脚本体也相同 | 主应用该形态由 `themeBootstrap.test.ts` 腿 4("脚本在 `<head>` 内、`<body>` 之前")与腿 5(IIFE + var)钉住,理由是 CSP 落地前不能用 module/let。**同款形态让两处可被同一套守卫断言** |
|
||||
| **D-H** | 派发形态 | **一个实现者子代理**做完 T1→T4(同动 crearte 单一工作树与 git index) | AGENTS.md / `sdd-parallel-dispatch` §1「一仓一写者」;P12/P13/P14 同款 |
|
||||
|
||||
### §2.1 被否方案
|
||||
@@ -233,7 +233,7 @@ function maskNonTemplate(content: string): string {
|
||||
</script>
|
||||
```
|
||||
|
||||
> ⚠️ **RE-PIN 2026-10-03(本段是控制者批注,不属于上面可复制的代码块)**:上面注释的措辞**刻意避开** `<style`、`<body`、`localStorage` 三个字面量。实现者与审查者**双双实测**:原措辞「必须在 `<style>` 与 `<body>` 之前」会让**腿 1 对正确实现假红**(注释自身在脚本之前 → `indexOf('<style')` 先命中注释;审查者复现 styleOpen=145 < scriptPos=811),而注释里的存储键字样会让 §5 指标「bootstrap 内该项 = 0」**字面不达标**。**对照**:主应用 `src/index.html` 的注释刻意写「必须在 **CSS 与 Vue** 之前」正是为了避开这两个字面量,所以 `themeBootstrap.test.ts` 的 naive indexOf 才不误红 —— 原措辞违反了这一既有范式。**照抄上面代码块时不要把这批注抄进去。**
|
||||
> ⚠️ **RE-PIN 2026-10-03(本段是控制者批注,不属于上面可复制的代码块)**:上面注释的措辞**刻意避开** `<style`、`<body`、`localStorage` 三个字面量。实现者与审查者**双双实测**:原措辞「必须在 `<style>` 与 `<body>` 之前」会让**腿 1 对正确实现假红**(注释自身在脚本之前 → `indexOf('<style')` 先命中注释;审查者复现 styleOpen=145 < scriptPos=811),而注释里的存储键字样会让 §5 指标「bootstrap 内该项 = 0」**字面不达标**。**对照**:主应用 `src/index.html` 的注释刻意写「必须在 **CSS 与 Vue** 之前」正是为了避开这两个字面量,所以 `themeBootstrap.test.ts` 的 naive indexOf 才不误红 —— 原措辞违反了这一既有范式。**照抄上面代码块时不要把这批注抄进去。** 🔴 **RE-PIN 2026-10-04 更正(终审 F-FINAL-1)**:本批注描述的「避开三个字面量」范式**仍然成立且必要**,但当时三方都以为 `maskSourceComments()` 已经把注释这一整类载体中和了——**实测并非如此**:该函数的 HTML 注释分支用 2 字符切片去和 4 字符的 `<!--` 比较,恒 false = **死代码**,故它此前只屏蔽 `//` 与 CSS 块注释两种载体。后果是「守卫不依赖措辞自律」这句声称不成立:仅仅把注释措辞改成含 `<style` / `<body` 就会让腿 1–4 假红(同变异跑 e2e 9/9 全绿,证明红的是守卫不是实现)。修法(1 行新增 + 1 处比较对象)已由第二轮 fix-forward 执行并验牙。
|
||||
⚠️ **白名单校验必须与主应用同款**(`=== 'light' || === 'dark'`),不得放宽成"任意 truthy"——`themeBootstrap.test.ts` 腿 3 正是为此存在(P13 finding #2 / N2:垃圾 `stored=neon` 必须被忽略)。
|
||||
|
||||
**(b) `<style>` 块**:加暗色覆盖块(D-F),值取 §1.1 的暗色调色板;`color-scheme: dark` 随块声明一次(同 `main.css:57` 的手法与理由)。**亮色色值逐字不变、渲染结果等价**(⚠️ RE-PIN 2026-10-03:裁定**接受**偏离 4——亮色值改由具名 `--bs-*` 变量供给、暗色块覆盖同名变量。理由三条:① 审查者独立写 CSS 解析器验证亮色态 **15 条颜色相关声明零差异**(含 4 条简写内嵌 `var()` 的 border/box-shadow)② 手法与主应用 `main.css` 同构(`@theme` 定义令牌 + `html[data-theme="dark"]` 覆盖),符合 D-F ③ 严格守「文本形态逐字不变」则暗色块须重写 **12 条规则**,同步维护面从 6 个值涨到 12 条规则)。
|
||||
@@ -288,7 +288,7 @@ const targets = computed<RuntimeTarget[]>(() => resolveRuntimeTargets(props.game
|
||||
|
||||
| 项 | 在范围内 | 不在范围内 |
|
||||
|---|---|---|
|
||||
| 文件 | `bootstrap/index.html`、`runtime/host/adapters.ts`、`runtime/host/GameHost.vue`、`app/lib/contrast.test.ts`、`app/styles/main.css`、**新增** `app/lib/bootstrapTheme.test.ts` | 其它 `.vue` / `useTheme.ts` / `themeBootstrap.test.ts` / `noHardcodedColor.test.ts` |
|
||||
| 文件 | `bootstrap/index.html`、`runtime/host/adapters.ts`、`runtime/host/GameHost.vue`、`app/lib/contrast.test.ts`、`app/styles/main.css`、**新增** `app/lib/bootstrapTheme.test.ts`、**新增** `e2e/bootstrapTheme.spec.ts`(RE-PIN 2026-10-04,闭合终审 §6-4:该文件 294 行 / 9 腿、fix 轮 +162 −13,§5-T4.3 有它的完整测试计划而 §4 的范围内清单漏列) | 其它 `.vue` / `useTheme.ts` / `themeBootstrap.test.ts` / `noHardcodedColor.test.ts` |
|
||||
| 主题 | bootstrap 加载屏的两态 | GameHost 展柜框架本身(它吃主应用令牌,已正确跟随暗色;其亮色徽标 3.26 属设计决策,挂账) |
|
||||
| 守卫 | 新增 bootstrap 主题一致性守卫 + `AA_PAIRS` 补一对 | 改既有守卫的断言语义 |
|
||||
| 令牌 | 删 `--color-info` | 新增任何令牌 |
|
||||
@@ -317,8 +317,8 @@ const targets = computed<RuntimeTarget[]>(() => resolveRuntimeTargets(props.game
|
||||
| 5 | **`GameHost.vue` 注入的是 `effectiveTheme()` 而非 `useTheme().theme.value`** | 🔴 **D-B 的架构陷阱**(响应式会让主题切换重载运行中的游戏) | 改成 `theme.value` → 必须红 |
|
||||
| 6 | bootstrap 暗色块的 hex 值与 `main.css` 的 `html[data-theme="dark"]` 调色板**逐值一致**(`paper`/`surface`/`ink`/`ink-soft`/`accent`/`accent-ink` 六个) | D-E 方案否决理由:bootstrap 自包含 hex,故须钉"两处调色板不分叉" | 把 bootstrap 暗色 `--paper` 改成别的值 |
|
||||
| 7 | bootstrap **亮色调色板的值**未被改动 —— ⚠️ **须从 `:root` 块提取 `--bs-*` 再逐值比对(与腿 6 对称),不得用全文子串搜索**(RE-PIN 2026-10-03:审查者 F-4 实测 `#f7f2e7` 在文件里出现 **3 次**(`:root` 的 `--bs-paper`、暗色块的 `--bs-ink`、脚本的 theme-color meta)→ 全文搜索**必然命中**;把 `:root` 亮色 paper 改成 `#eeeeee` 时 `RED=[]`,且**四个守卫合跑 38 passed / 0 failed 全绿** = 本腿的守卫目的「防顺手把亮色也改了」**无断言覆盖**,是 P14「spec D-J 守卫目的无断言覆盖」的同族形态。改为按 scope 提取后**同时消除「暗色块 `--bs-ink` 与亮色 `--bs-paper` 同值」造成的耦合**,即 P13 `LEGACY_KEYS` 钉桩里「scrim 两主题同值 → 后写覆盖不可观测,故排除」的同一类推理) | 防"顺手把亮色也改了"(本批只加暗色) | 改 `:root` 的亮色 paper |
|
||||
| 8 | `bootstrap/index.html` 内**不存在 inline `style` 里的 `color:`**(D-E 的回归钉桩)—— ⚠️ **正则须同时覆盖单引号与双引号属性形态**(RE-PIN 2026-10-03:审查者 F-5 实测只匹配 `style="` 时,把 inline style 改**单引号**并塞回违规色 `#a3a3a3` → `RED=[]`,而 HTML 单引号属性完全合法(`prettier`/手写都可能产出)→ **mutation (f) 想钉的性质被引号形态绕过**。与 P13「`bg-[#…]` 手写反斜杠转义对 minified 假阴性」、控制者「带引号 grep 对 minified 假阴性」**同族:模式只覆盖了自己见过的那种写法**。修法 `/style\s*=\s*(?:"[^"]*\|'[^'*])[^>]*?\bcolor\s*:/i`) | 防 inline 违规复发(本批刚删两处 2.2594 / 2.4776) | 加回 `style="color:#a3a3a3"` **或单引号形态** |
|
||||
| 9 | **前提检查**:扫描到的文件数 ≥ 3 且 bootstrap html 长度 > 500 | 防"读 0 文件也报 0 违规"的假信心(P13 审查者 M5/M6、P14 终审者 R0 的同族教训) | 把路径改成不存在的文件 |
|
||||
| 8 | `bootstrap/index.html` 内**不存在 inline `style` 里的 `color:`**(D-E 的回归钉桩)—— ⚠️ **修法必须覆盖双引号 / 单引号 / 无引号三种属性形态**(RE-PIN 2026-10-04,闭合终审 F-FINAL-4):**提取所有 `style=` 属性值、去引号后逐条查 `color:`**,即已实现的 `inlineStyleValues()`。🔴 **正则字面量刻意不写进本表格**——markdown 表格单元格里的竖线必须转义,而转义后的竖线在正则里是**字面量字符**而非「或」;控制者上一轮正是这样把一条本来正确的正则抄成 **4/7 形态失配**(比裁定原文的 2/7 更错),而它检查了「表格每行竖线数 == 表头」、**没检查转义后的代码是否还是原来那段代码**。要看正则请读代码实体 `src/app/lib/bootstrapTheme.test.ts` 的 `inlineStyleValues()`。⚠️ **不要写成「先匹配 style 属性、再往后找 `color:`」的一行正则**:实测该形态在单引号与无引号上失配——贪婪的引号内匹配会吃掉整个属性值(含内部的 `color:`),闭引号之后剩余文本以尖括号开头 → 失配。与 P13「`bg-[#…]` 手写反斜杠转义对 minified 假阴性」、控制者「带引号 grep 对 minified 假阴性」**同族:模式只覆盖了自己见过的那种写法** | 防 inline 违规复发(本批刚删两处 2.2594 / 2.4776) | 加回 `style="color:#a3a3a3"`、**或单引号形态、或无引号形态** |
|
||||
| 9 | **前提检查**:扫描到的文件数 ≥ 3、bootstrap html 含 `<head>` / `<style` / `<body` **三个结构性标记**、且长度 **> 4000**(⚠️ 单位是**源码字符 4423**,不是产物 7056 字符 / 7588 字节) | 防"读 0 文件也报 0 违规"的假信心(P13 审查者 M5/M6、P14 终审者 R0 的同族教训)。**RE-PIN 2026-10-04**(闭合终审 §6-1):原写「长度 > 500」是 F-8 修**前**的旧值——500 对 4423 而言宽松到截断至 501 字符仍绿(mutation K4 实测),代码已改为 4000 并补了三个结构性前提 | 把路径改成不存在的文件;**或截断到 501 字符**(腿9 应红——修前它仍绿) |
|
||||
| 10 | `AA_PAIRS` 含 `['ink','surface']` 且 `REQUIRED_KEYS` **不含** `info`、长度为 **11** | 改动②③ 的回归钉桩 | 删掉补钉的那一对;或把 `info` 加回 |
|
||||
|
||||
**RED 相位要求**:T1 在 T2/T3/T4 之前提交时,`npx vitest run app/lib/bootstrapTheme.test.ts` 必须**红**,且**至少腿 1/2/5/6/8/10 红**(bootstrap 还没有脚本、没有暗色块、GameHost 还没注入、AA_PAIRS 还没补、info 还在)。腿 7/9 应当**已绿**(亮色值本来就在、文件本来可读)——**这是有意的**:它们是本批的"不得回退"钉桩,不是新功能的断言。实现者须把 RED 输出**逐字存证**到 `.superpowers/sdd-p15b/impl-evidence/t1-red.txt`,并**逐腿标注红/绿**。
|
||||
@@ -347,7 +347,7 @@ const targets = computed<RuntimeTarget[]>(() => resolveRuntimeTargets(props.game
|
||||
1. `npm run build`(含 `vue-tsc --noEmit` + `vite build` + `build-runtime.mjs`)exit 0
|
||||
⚠️ **`npm run build` 会类型检查 `e2e/*.spec.ts`**(P13 spike 6 曾因此 `BUILD_EXIT=2`)
|
||||
2. `npm run e2e` 主套件 **102+1skip** 基线不回退(P13 交付值;`dark.spec.ts` 10 腿须全绿)
|
||||
3. **新增 e2e 腿**(⚠️ RE-PIN 2026-10-03:**偏离 5 已被裁定驳回**,本项按原意执行,不接受「fixture 不支持所以不测」):(a) **端到端腿**——暗色 + 虚拟游戏页,用 **`context.route`(不是 `page.route`)** + inert sw.js 冻住加载屏,断言 **`snap.url === 父侧 iframe src`**(**因果链闭合**:子文档读到的就是父侧算出的那个 url,不是测试手搓的 hash)、`attr=dark`、`bg=rgb(23,20,15)`、h1 是加载屏、`theme-color` meta = `#17140f`、**+8s 后 `path` 仍是 `/__bootstrap`**;另加亮色格(`stored=light` 压过系统暗色)。🔴 **根因纠正**:实现者四种拦截手法**全用 `page.route`**,审查者 PROBE-6A 实测**三个 route 命中数全为 0**(SW 注册请求、SW 发起的请求、被 SW `respondWith` 合成的导航都**不经 page 级路由**)→ 「挂起从未生效,真 SW 照常安装 → `runtime:ready` → `location.replace('/')`」被误归因为「加载屏本质瞬态」。改 `context.route` 后 PROBE-7A `sw.js hits = 1`、子文档稳定停在 `/__bootstrap`;PROBE-7B **T+0/T+10s/T+25s 三次全 `attr=dark`**,T+25s 因 Playwright 自身 `timeout: 45_000` 中断而**不是被顶掉**。(b) **行为腿**——游戏页切主题 → iframe `src` **不变**(同一 inert-SW 手法下可确定性断言):这是 D-B 那个 critical 性质的**唯一行为级防线**(腿 5 改回全文断言后仍有残留局限,见 §5-T1 腿 5)。(c) **反方向格**——子侧 `[系统暗色] × [hash=light]` → 期望 **`light`**(6 行):审查者实测 **H1**(白名单提取成变量 + prefers 优先)与 **H3**(拆中间变量的嵌套三元)都让腿 2 全绿,而**缺陷是真的**(显式选亮色的暗色系统用户看到暗色加载屏,违反 D-A「hash 优先」);且 **H1 同时逃过源码守卫(10 passed)与全部 5 条 e2e 腿(5 passed)**。缺失的正是这一格,它是 H1/H3 的**唯一鉴别格**。对照:主应用 `dark.spec.ts:169` **有**这条反方向腿(「stored 压过 system(反方向)」),bootstrap 侧缺
|
||||
3. **新增 e2e 腿**(⚠️ RE-PIN 2026-10-03:**偏离 5 已被裁定驳回**,本项按原意执行,不接受「fixture 不支持所以不测」):(a) **端到端腿**——暗色 + 虚拟游戏页,用 **`context.route`(不是 `page.route`)** + inert sw.js 冻住加载屏,断言 **`snap.url === 父侧 iframe src`**(**因果链闭合**:子文档读到的就是父侧算出的那个 url,不是测试手搓的 hash)、`attr=dark`、`bg=rgb(23,20,15)`、h1 是加载屏、`theme-color` meta = `#17140f`、**+8s 后 `path` 仍是 `/__bootstrap`**;另加亮色格(`stored=light` 压过系统暗色)。🔴 **根因纠正**:实现者四种拦截手法**全用 `page.route`**,审查者 PROBE-6A 实测**三个 route 命中数全为 0**(SW 注册请求、SW 发起的请求、被 SW `respondWith` 合成的导航都**不经 page 级路由**)→ 「挂起从未生效,真 SW 照常安装 → `runtime:ready` → `location.replace('/')`」被误归因为「加载屏本质瞬态」。改 `context.route` 后 PROBE-7A `sw.js hits = 1`、子文档稳定停在 `/__bootstrap`;PROBE-7B **T+0/T+10s/T+25s 三次全 `attr=dark`**,T+25s 因 Playwright 自身 `timeout: 45_000` 中断而**不是被顶掉**。(b) **行为腿**——游戏页切主题 → iframe `src` **不变**(同一 inert-SW 手法下可确定性断言):这是 D-B 那个 critical 性质的**唯一行为级防线**(腿 5 改回全文断言后仍有残留局限,见 §5-T1 腿 5)。(c) **反方向格**——子侧 `[系统暗色] × [hash=light]` → 期望 **`light`**(6 行):审查者实测 **H1**(白名单提取成变量 + prefers 优先)与 **H3**(拆中间变量的嵌套三元)都让腿 2 全绿,而**缺陷是真的**(显式选亮色的暗色系统用户看到暗色加载屏,违反 D-A「hash 优先」);且 **H1 同时逃过源码守卫(10 passed)与全部 5 条 e2e 腿(5 passed)**。缺失的正是这一格,它是 H1/H3 在**子侧直访**路径上的唯一鉴别格(⚠️ RE-PIN 2026-10-04,闭合终审 §6-2:实测判定序真缺陷会让**两条**腿红——本格**与端到端亮色格**,故「唯一」只在子侧直访这一类里成立;原措辞是纪律 #4 惩罚的全称声明)。对照:主应用 `dark.spec.ts:169` **有**这条反方向腿(「stored 压过 system(反方向)」),bootstrap 侧缺
|
||||
4. **产物验证**(P13 教训:源码级守卫不等于产物正确):⚠️ **必须在 e2e 之后重跑生产 `npm run build` 之上做**——`npm run e2e` 内部跑 `build:e2e`(`playwright.config.ts:12` 的 `webServer.command`)会**覆盖 `dist/`**,并用 `grep -c -F 'localhost:4173' src/dist/bootstrap/index.html` = **0** 证明量的是生产构建而非 e2e 残留(⚠️ RE-PIN 2026-10-03:控制者当日正是量了 e2e 残留,导致它的 7069 与实现者的 7056 本就不该相等而被当成矛盾;实现者报告 §2 已明写这个顺序陷阱)。判据:`dist/bootstrap/index.html` **含暗色块**、**不含** `#a3a3a3` 与 `#f87171`;主应用 CSS 里 `--color-info` **零命中**、两个色值(`2b62cc`/`7aa7f0`)**零命中**;**定义侧**令牌数 **12→11**(源码 `@theme` 与 `html[data-theme="dark"]` 各 11、产物暗色块 11);**usage 侧** `var(--color-*)` 计数与 P13 交付值 **75 保持不变**——`info` 是**零 usage 的死令牌**(7 种 utility 后缀 `bg-`/`text-`/`border-`/`ring-`/`fill-`/`stroke-`/`outline-` 全部零命中),删它**必然**不改变 usage 计数,这是预期行为而非缺陷。🔴 **判据方向**:若 usage 计数**发生变化**,才说明存在对 `info` 的引用,与 §1.3 的「零使用」取证前提矛盾,须停下来重新取证。**定义侧与 usage 侧是两个口径,不得混用**(⚠️ RE-PIN:原判据「删一个令牌应使计数变化」方向写反了,由实现者发现、审查者独立复现成立)。⚠️ **产物 grep 用 `grep -F`**,不手写转义、不用带引号模式(minifier 会去属性引号:产物是 `data-theme=dark` 而非 `data-theme="dark"`;CSS 类名是**转义**形态 `.bg-\[\#…\]`)。⚠️ **报数字必须同时报口径**:字节用 `wc -c`(**不要用 `len(str)` 标注成 B**,那是字符数,差值精确等于 UTF-8 多字节贡献)、计数**同时给 raw 与屏蔽注释后两个数**并声明大小写敏感性、行数用 `wc -l`
|
||||
⚠️ **产物 grep 用 `grep -F` 不手写转义**(P13 教训:`.backdrop\:bg-scrim` 手写反斜杠转义对 minified 产物假阴性;AGENTS.md 已立不变量)
|
||||
|
||||
@@ -398,7 +398,7 @@ const targets = computed<RuntimeTarget[]>(() => resolveRuntimeTargets(props.game
|
||||
| 风险 | 缓解 |
|
||||
|---|---|
|
||||
| **`theme` 键写成必填 → 破既有测试** | §3.1(d) 明令可选;`adapters.test.ts` **11 处**调用零修改仍绿是验收项 |
|
||||
| **🔴 注入改成响应式 → 主题切换重载运行中的游戏** | D-B + §3.1(e) 的 ⚠️ 注释 + **T1 腿 5 与 mutation (d) 专门钉这条** + **§5-T4.3(b) 的行为腿**(RE-PIN 2026-10-03)。⚠️ **腿 5 只钉字面形态,不够**:审查者 F-1(critical)实测 **G1**(调用点字面量与交付版**逐字相同**、依赖在 computed 体内别处建立)与 **G2**(import 别名 + 本地同名响应式包装)**双双绕过**(10 腿全绿、`vue-tsc` exit 0),而用 `effectScope` + `computed` 求值计数**证明缺陷语义上真实**(基准 `evals=1`/url 不变;G1/G2 均 `evals=2`、url 从 `…theme=light` 翻成 `…theme=dark` → **iframe `src` 变化 → 运行中的游戏被重载、存档丢失**)。**修法(一行 ×2)**:腿 5 的两条负向断言从 `args`(调用点内)改回 `gameHostMasked`(已屏蔽注释的**全文**)——审查者逐字验证 `maskSourceComments()` **本身已解决**「陷阱注释必然写出被禁字面量」的冲突(屏蔽后 `useTheme()` 与 `theme.value` 都从文件消失、`effectiveTheme` 真实代码保留、陷阱注释 28–33 行屏蔽后全空)→ **缩小断言范围不必要,第二道防线反而制造了缺口**(与 P14 终审「在第一轮修复自身里找到洞」完全同构)。已验五种组合:交付形态+G1/G2 = 绿(绕过);改法+G1/G2 = **红(抓到)**;改法+合法树 = **10 腿全绿(不误红)**。⚠️ **残留局限须写进守卫注释、不得声称「唯一手段」**(纪律 #4):改后仍抓不住「在 computed 之外的**模块级作用域**读 ref 再传值」,但该形态**无害**(快照在模块加载时求值一次、无依赖 → 不会重载);真正的解药是行为腿 |
|
||||
| **🔴 注入改成响应式 → 主题切换重载运行中的游戏** | D-B + §3.1(e) 的 ⚠️ 注释 + **T1 腿 5 与 mutation (d) 专门钉这条** + **§5-T4.3(b) 的行为腿**(RE-PIN 2026-10-03)。⚠️ **腿 5 只钉字面形态,不够**:审查者 F-1(critical)实测 **G1**(调用点字面量与交付版**逐字相同**、依赖在 computed 体内别处建立)与 **G2**(import 别名 + 本地同名响应式包装)**双双绕过**(10 腿全绿、`vue-tsc` exit 0),而用 `effectScope` + `computed` 求值计数**证明缺陷语义上真实**(基准 `evals=1`/url 不变;G1/G2 均 `evals=2`、url 从 `…theme=light` 翻成 `…theme=dark` → **iframe `src` 变化 → 运行中的游戏被重载、存档丢失**)。**修法(一行 ×2)**:腿 5 的两条负向断言从 `args`(调用点内)改回 `gameHostMasked`(已屏蔽注释的**全文**)——审查者逐字验证 `maskSourceComments()` **本身已解决**「陷阱注释必然写出被禁字面量」的冲突(🔴 **RE-PIN 2026-10-04 限定适用范围,终审 F-FINAL-1**:这句对**当前这个文件**是真的,但成立原因是「陷阱注释恰好是 `//` 行注释形态」,**不是**「屏蔽了注释」这一整类——该函数的 HTML 注释分支当时是死代码。故 F-1 的修法方向正确、必须做,而它的**安全论证**依赖一个只对 `//` 成立的泛化,那个泛化已被 C5/G1x 实测推翻)(屏蔽后 `useTheme()` 与 `theme.value` 都从文件消失、`effectiveTheme` 真实代码保留、陷阱注释 28–33 行屏蔽后全空)→ **缩小断言范围不必要,第二道防线反而制造了缺口**(与 P14 终审「在第一轮修复自身里找到洞」完全同构)。已验五种组合:交付形态+G1/G2 = 绿(绕过);改法+G1/G2 = **红(抓到)**;改法+合法树 = **10 腿全绿(不误红)**。⚠️ **残留局限须写进守卫注释、不得声称「唯一手段」**(纪律 #4):改后仍抓不住「在 computed 之外的**模块级作用域**读 ref 再传值」,但该形态**无害**(快照在模块加载时求值一次、无依赖 → 不会重载);真正的解药是行为腿 |
|
||||
| **判定序与主应用分叉 → 同一用户两处不同主题** | T1 腿 2 钉 bootstrap 内部顺序;**另需一条腿比对 `useTheme.effectiveTheme()` 的后两级顺序**(`themeBootstrap.test.ts` 腿 2 的同款手法:用 `indexOf` 比较,且先断言三者都 `> -1`) |
|
||||
| **bootstrap 与主应用调色板分叉**(bootstrap 自包含 hex) | T1 腿 6 逐值比对六个暗色 hex + 腿 7 钉亮色值不变。**这是 D-E 方案(引令牌)被否后的必要补偿** |
|
||||
| **新守卫写成恒真** | §5-T1 的 ⚠️ 三条(`indexOf` 返 -1 时顺序断言恒真、`max(a,b) ≥ k` 形态、`toBeGreaterThanOrEqual` 无牙)+ mutation (i) 腿 9 前提检查 + (a)–(h) 逐条验牙 |
|
||||
@@ -424,4 +424,6 @@ const targets = computed<RuntimeTarget[]>(() => resolveRuntimeTargets(props.game
|
||||
12. **测试被迫修改 = 设计失败的信号**,除非 spec 明列例外(本批唯一例外:`contrast.test.ts` 的 "12→11 令牌" 文案)。若发现必须改其它测试,**停下来报告**。
|
||||
13. 🔴 **(RE-PIN 2026-10-03,审查者 F-2)手法的适用范围没验证就当已穷尽**:`page.route` **拦不到** SW 注册请求、SW 发起的请求、以及被 SW `respondWith` 合成的导航 → 冻住 SW 驱动的加载屏须用 **`context.route`**。**用错层级会让「挂起」静默失效,并把结果误归因为「被测对象本质瞬态」**——实现者四种手法全用 `page.route`(实测命中 0),据此得出「加载屏无法冻结、端到端腿必然 flaky」并撤掉了腿,而归因是错的。与第 6 条(带引号 grep 对 minified 假阴性)**同族**。**换一层再试一次,再下结论。**
|
||||
14. **(RE-PIN 2026-10-03,审查者 F-9)报数字必须同时报口径**:字节用 `wc -c`(**不要用 `len(str)` 标注成 B**——那是字符数,差值精确等于 UTF-8 多字节贡献:CSS 12、html 532);计数**同时给 raw 与屏蔽注释后两个数**并声明大小写敏感性(控制者用 raw、实现者用屏蔽注释+大小写不敏感 → 同一份产物两份互斥数字、第三方无法判定谁错);行数用 `wc -l`(不要 `split('\n')`);**产物验证须在 e2e 之后重跑生产 build**(`npm run e2e` 内部跑 `build:e2e` 会覆盖 `dist/`)。**没有口径的数字无法交叉核对,等于没报。**
|
||||
15. **一次性验证脚本一律落盘存证**(控制者的 CSS 解析器没落盘 → 结论虽被审查者独立复现,但**实现不可审计**)。
|
||||
15. **一次性验证脚本一律落盘存证**(控制者的 CSS 解析器没落盘 → 结论虽被审查者独立复现,但**实现不可审计**)。**适用例补全(RE-PIN 2026-10-04,终审 §8.2):census / 统计类脚本同样必须落盘** —— 前任的 arbitrary-utility census 脚本没落盘,导致「48 vs 47」这个差 1 的口径争议**至今无法对齐**(终审用三份口径独立数都得 48,判据「真 hex 色 = 0」三方一致,故结论不受影响,但差异本身不可追溯)。
|
||||
16. 🔴 **(RE-PIN 2026-10-04,终审 §8.7)报告里引用的每一份证据文件,交付前须逐个 `ls` 核在盘上;引用别处输出须给出该输出的落盘路径,路径不存在即视为未验证。** 这条纪律本轮**两次生效**:fix 者靠它抓到自己的假取证(把审查者的 PROBE 数字写成自己的实测结论——**是靠核「文件在不在盘上」抓到的,不是靠核结论对不对,因为结论恰好是对的**);终审者靠同一手法抓到控制者 `verify-fix-v4.py` 的 §③ 是 21 条无条件 `print('[OK]')`、且它引用的 v2 输出从未归档 → 「0 FAIL」结论虽真却不自足。**落盘纪律的价值不在结论正确性,而在可审计性。**
|
||||
17. 🔴 **(RE-PIN 2026-10-04)spec 与裁定里凡可执行的技术细节(正则、函数签名、字面量、行号区间、对比度数字)必须从代码实体 `grep` / `git show` 出来粘贴,或自己按公式复算,不得凭记忆手写。** 本批控制者因此错了三处:① 把审查者建议的一行正则抄进 §5-T1 腿8,而 markdown 表格的竖线转义让它变成 **4/7 形态失配**(比裁定原文的 2/7 更错)② 漏 re-pin 腿9(spec 仍写 > 500 而代码已是 4000)③ 引用的对比度 `3.0269` 从未被任何人复核,自算实为 **2.5519**(反查任何灰底都得不到 3.0269 → 那是个孤值)。与 P15-A 的同族错误(形参顺序、阶段区间行号)合并为一条纪律,两份 spec §8 同源。
|
||||
|
||||
Reference in New Issue
Block a user