pi `6aa2b17f` 报的第三例(`unlisted`/`ghosts` 退 0)**我复核后已经在 `6ee9902` 修好了**
—— 而它的**父提交正是 pi 报信时读的 `4b841e0`**(差 11 分钟)。又是同一形状的竞态。
但顺着同一条线**审计全部 61 个变异条目的锚点**,发现**还有一个同形状的落点,
而且它在真树上是活的**(不是构造的):
hits=0 SettingsPage.ets 「管理入口不做门禁」(jobs-all.json)
根因:该锚点写的是 **6 空格**,而 `c523c21`(09-17 17:43)把该文件**重排成 4 空格**
⇒ 锚点从此命中 0 次(写进清单时 `bcd4f97`(09-15)它是**对的**)。
为什么没人发现:`summary.py` 把「hits=0 … 过期条目」**只打印**、**不进严重度链**
⇒ `rc=0`、`diag=none` ⇒ 套件 `whyLines: (note || status!==0) ? … : []` 为假 ⇒
**整段丢掉**。端到端实测(真树、干净工作树):套件输出里 grep「过期条目」= **0 次**。
★ 危害是"**一个变异守具被静默关掉**",不是"数字错了":`ran` 少 1、`skipped=1`
是个**中性数字**,读者看不出少了哪一个。而**手工施加那个变异仍能让判据红**
(`harmony-admin.test.mjs` `# fail 1`)⇒ **有齿,只是没挂上**。
修法(沿用 `summary.py` 的**唯一严重度链**):
· 新增 `mutant-anchor-stale` ⇒ **1 档**(清单/数据该改;**不是** 2 档 ——
照 `env-defaults.sh:25` 的反方向:别让"清单没跟上"冒充环境)。
· 修锚点 6→4 空格 ⇒ `ran` 47→48、`skipped` 1→0,守具重新挂上(实测变异能红)。
· `whyLines` 过滤器加 `^\s*hits=`:只说"有锚点过期"不够,**点名的才是可行动的**。
· 两张表都登记(`UPSTREAM_RC` + `DIAG`,`blocksGreen: true`)+ 真跑案例
(迷你仓库里放一个不含锚点的同名文件 ⇒ `hits=0`)+ 读者侧具名案例。
★ 射程如实标出:判据只看 **`hits == 0`**,**不看 `hits == -1`**(文件打不开是另一回事,
且迷你夹具里目标文件本来就不在 ⇒ 算进来会造假红)。**`-1` 那一半无判据守着**(真树 0 条)。
变异验证(4 个方向全抓):
① 新档关闭(回到"只打印")⇒ exitcode-selftest rc=1、2 条红 ✓
② 从 UPSTREAM_RC 删掉新码 ⇒ 反向覆盖点名 ✓
③ blocksGreen true→false ⇒ 双向口径漂移红 ✓
④ 放宽成任何 skipped_detail(含 hits=-1)⇒ rc=1、**5 条红**(夹具假红)
⇒ **`h == 0` 这个射程是承重的** ✓
端到端 A/B(隔离 worktree,同刻对照):
A 锚点已修 ⇒ rc=0、diag=none、ran=48/skipped=0、"锚点已失效" grep **0**
B 锚点退 6 格 ⇒ rc=1、diag=mutant-anchor-stale、ran=47/skipped=1、grep **2**
★ 通用规则(本仓第 4 次同一形状):**"跑了多少个"与"该跑多少个"之间也要有判据。**
被跳过时 `ran` 只少 1、`skipped` 只多 1 —— 都是中性数字,而"少了哪一个"没有通道。
凡"登记一批东西、再逐个挂上"的结构(变异条目、判据、样本表)都要问:
**挂不上的那一个,谁来说?**
自检 5 个各 rc=0;全套 checks=516 pass=511 fail=5 red=5 verdict=red;
mutants=48 ran=48 skipped=0 on_new_criteria=36 diag=none baseline=7/7✓。
CRITERIA.md §16.1.1 记这一笔(含 4 个变异与 A/B 表)。
234 lines
7.9 KiB
JSON
234 lines
7.9 KiB
JSON
[
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/model/AdminUsers.ts",
|
||
"pat": "return role === 'admin';",
|
||
"repl": "return role !== undefined && role.trim().toLowerCase() === 'admin';",
|
||
"test": "admin",
|
||
"why": "角色判定放宽"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/model/AdminUsers.ts",
|
||
"pat": " if \\(!found\\) \\{\n out\\.push\\(item\\);\n \\}\n return out;",
|
||
"repl": " if (!found) {\n list.push(item);\n }\n return list;",
|
||
"test": "admin",
|
||
"why": "toggled 就地改入参"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/model/AdminUsers.ts",
|
||
"pat": "if \\(v === undefined \\|\\| v\\.length === 0\\) \\{",
|
||
"repl": "if (v === undefined) {",
|
||
"test": "admin",
|
||
"why": "空串不当从未登录"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/model/AdminUsers.ts",
|
||
"pat": " if \\(user\\.role === 'admin'\\) \\{\n return false;\n \\}\n",
|
||
"repl": "",
|
||
"test": "admin",
|
||
"why": "管理员也打受限徽标"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/model/AdminUsers.ts",
|
||
"pat": "return '操作失败(服务端没有给原因)';",
|
||
"repl": "return '操作失败';",
|
||
"test": "admin",
|
||
"why": "兜底句不说原因缺席"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/model/AdminUsers.ts",
|
||
"pat": " return message;\n \\}\n if \\(!isApiError",
|
||
"repl": " return '操作失败';\n }\n if (!isApiError",
|
||
"test": "admin",
|
||
"why": "吞掉服务端文案"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/model/ImagePrep.ts",
|
||
"pat": "const longest: number = Math\\.max\\(width, height\\);",
|
||
"repl": "const longest: number = width;",
|
||
"test": "prep",
|
||
"why": "缩放按宽算"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/model/ImagePrep.ts",
|
||
"pat": "const scale: number = Math\\.min\\(1, maxEdge / longest\\);",
|
||
"repl": "const scale: number = maxEdge / longest;",
|
||
"test": "prep",
|
||
"why": "小图被放大"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/model/ImagePrep.ts",
|
||
"pat": "second\\.maxEdge = RETRY_EDGE;",
|
||
"repl": "second.maxEdge = MAX_EDGE;",
|
||
"test": "prep",
|
||
"why": "退档不降分辨率"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/model/ImagePrep.ts",
|
||
"pat": "second\\.quality = RETRY_QUALITY;",
|
||
"repl": "second.quality = FIRST_QUALITY;",
|
||
"test": "prep",
|
||
"why": "退档不降质量"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/model/ImagePrep.ts",
|
||
"pat": "return actualBytes > MAX_UPLOAD_BYTES;",
|
||
"repl": "return actualBytes >= MAX_UPLOAD_BYTES;",
|
||
"test": "prep",
|
||
"why": "等于上限也退档"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/model/ImagePrep.ts",
|
||
"pat": "if \\(sizeBytes > MAX_SOURCE_BYTES\\) \\{",
|
||
"repl": "if (sizeBytes >= MAX_SOURCE_BYTES) {",
|
||
"test": "prep",
|
||
"why": "边界 >= 而不是 >"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/model/ImagePrep.ts",
|
||
"pat": "if \\(!mimeType\\.startsWith\\('image/'\\)\\) \\{",
|
||
"repl": "if (false) {",
|
||
"test": "prep",
|
||
"why": "不判类型"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/model/ImagePrep.ts",
|
||
"pat": "return '上传失败:' \\+ serverMessage;",
|
||
"repl": "return '上传失败';",
|
||
"test": "prep",
|
||
"why": "吞掉上传失败文案"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/model/ImagePrep.ts",
|
||
"pat": "export const MAX_UPLOAD_BYTES: number = 3_670_016;",
|
||
"repl": "export const MAX_UPLOAD_BYTES: number = 6_291_456;",
|
||
"test": "prep",
|
||
"why": "上限超过服务端 4MiB"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/model/ImagePrep.ts",
|
||
"pat": "export const MAX_EDGE: number = 2560;",
|
||
"repl": "export const MAX_EDGE: number = 4096;",
|
||
"test": "prep",
|
||
"why": "首档最长边改 4096"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/pages/AdminUsersPage.ets",
|
||
"pat": " if \\(isRestricted\\(user\\)\\) \\{",
|
||
"repl": " if (false) {",
|
||
"test": "admin",
|
||
"why": "去掉受限徽标"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/pages/AdminUsersPage.ets",
|
||
"pat": " onlyStatus\\.status = 'active';",
|
||
"repl": " onlyStatus.status = 'active';\n onlyStatus.display_name = this.editDisplayName;",
|
||
"test": "admin",
|
||
"why": "启停顺带发 display_name"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/pages/AdminUsersPage.ets",
|
||
"pat": " \\.backgroundColor\\(Theme\\.surface\\)\n \\.borderRadius\\(Theme\\.radiusCard\\)\n \\.onClick\\(\\(\\) => \\{ this\\.toggleEdit\\(user\\); \\}\\)",
|
||
"repl": " .backgroundColor('#123456')\n .onClick(() => { this.toggleEdit(user); })",
|
||
"test": "admin",
|
||
"why": "写死色值 + 去卡片圆角"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/pages/SettingsPage.ets",
|
||
"pat": " this\\.isAdmin = false;\n \\}",
|
||
"repl": " this.isAdmin = true;\n }",
|
||
"test": "admin",
|
||
"why": "身份读不到时乐观显示入口"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/pages/SettingsPage.ets",
|
||
"pat": " if \\(this\\.isAdmin\\) \\{",
|
||
"repl": " if (true) {",
|
||
"test": "admin",
|
||
"why": "管理入口不做门禁"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/pages/SettingsPage.ets",
|
||
"pat": " @State isAdmin: boolean = false;",
|
||
"repl": " @State isAdmin: boolean = true;",
|
||
"test": "admin",
|
||
"why": "isAdmin 初值 true"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/common/BackgroundPicker.ets",
|
||
"pat": "if \\(!overLimit\\) \\{\n\\s*break;",
|
||
"repl": "if (true) {\n break;",
|
||
"test": "prep",
|
||
"why": "永远只压第一档"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/common/BackgroundPicker.ets",
|
||
"pat": "if \\(packed === null \\|\\| overLimit\\)",
|
||
"repl": "if (packed === null)",
|
||
"test": "prep",
|
||
"why": "两档都超限不拒绝"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/common/BackgroundPicker.ets",
|
||
"pat": "overLimit = shouldRetryWithActual\\(usedBytes\\);",
|
||
"repl": "overLimit = false;",
|
||
"test": "prep",
|
||
"why": "结论恒 false"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/common/BackgroundPicker.ets",
|
||
"pat": "await packer\\.release\\(\\);",
|
||
"repl": "packer.release();",
|
||
"test": "prep",
|
||
"why": "packer.release 不 await"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/common/BackgroundPicker.ets",
|
||
"pat": "await head\\.release\\(\\);",
|
||
"repl": "head.release();",
|
||
"test": "prep",
|
||
"why": "head.release 不 await"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/common/BackgroundPicker.ets",
|
||
"pat": "desiredSize: \\{ width: size\\.width, height: size\\.height \\}",
|
||
"repl": "desiredSize: { width: info.size.width, height: info.size.height }",
|
||
"test": "prep",
|
||
"why": "按原图尺寸解码"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/common/BackgroundPicker.ets",
|
||
"pat": " quality: Math\\.round\\(quality \\* 100\\)",
|
||
"repl": " quality: quality",
|
||
"test": "prep",
|
||
"why": "quality 不换算"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/common/BackgroundPicker.ets",
|
||
"pat": " this\\.onUploaded\\(UPLOAD_OK_HINT\\);",
|
||
"repl": " this.bgKind = 'image';",
|
||
"test": "prep",
|
||
"why": "组件自己宣布成功"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/api/ApiClient.ets",
|
||
"pat": "name: 'file',\n contentType: mimeType",
|
||
"repl": "name: 'image',",
|
||
"test": "prep",
|
||
"why": "multipart 字段名不是 file(uploadBytes 那一处;此前锚点命中 2 次跑不起来,已切片)"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/api/AppearanceApi.ets",
|
||
"pat": "fileName, 'image/jpeg'\\);",
|
||
"repl": "fileName, 'application/octet-stream');",
|
||
"test": "prep",
|
||
"why": "上传 mime 发错"
|
||
},
|
||
{
|
||
"file": "client/harmony/entry/src/main/ets/pages/SettingsPage.ets",
|
||
"pat": " await store\\.syncFromServer\\(ctx, client\\);\n this\\.adoptSnapshot\\(store\\.current\\(\\), store\\.statusText\\(\\)\\);\n \\}\n\n /\\*\\* 把 store 里的快照复制进 @State",
|
||
"repl": " this.bgKind = 'image';\n }\n\n /** 把 store 里的快照复制进 @State",
|
||
"test": "prep",
|
||
"why": "上传成功后不重新同步"
|
||
}
|
||
]
|