AskaEth
|
fc48094249
|
Merge branch 'feat/javareg-registry-fallback-for-microsoft'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-12 20:20:31 +08:00 |
|
AskaEth
|
23e98635dd
|
feat(core): JDK 发现补 Microsoft 注册表支(HKLM\SOFTWARE\Microsoft\JDK\*\MSI\Path)(M281)
`registry_homes()` 原来只读 `HKLM\SOFTWARE\JavaSoft\JDK`,而 MS JDK 的 MSI 把安装路径
登记在 `HKLM\SOFTWARE\Microsoft\JDK\<ver>\<flavor>\MSI` 的 `Path`(本机 21/25 两条,
JavaSoft 那个键在本机根本不存在)。装在自定义目录(不在 Program Files 下)的 MS JDK
因此漏扫——Program Files 那支兜住的只是默认位置。
- 新增 `parse_msi_paths()`:判据是「当前键尾段叫 MSI」(不数层数),多一层/空层都认;
同键里的 `JavaHome REG_DWORD`、非 MSI 键下的 `Path`、值名不是整段 `Path` 的都不认。
- 注册表读取改为可注入:`RegSource` trait + `ProcessReg`(真 `reg.exe`),
`registry_homes_from(&dyn RegSource)` 让单测喂真实 `reg query` 输出样本,不依赖本机。
- 两个键 × 64/32 两个视图各查一遍,与 JavaSoft 那支同口径;键不存在照旧静默跳过。
- `scan_java_binaries()` 拆出 `java_binaries_in(roots, homes)`:注入式验收可以屏蔽
Program Files / PATH / JAVA_HOME,只走注册表;去重改用仓库既有的 `path_key` 口径
(末尾反斜杠、`\?\` 前缀、大小写都不会重复列)。
- 注册表这支整体搬进 `discover/registry.rs`(`mod registry;` + `pub use`,
`discover::registry_homes` / `parse_java_home` 公开路径不变,`mod.rs` 一行未动)。
|
2026-11-12 20:19:23 +08:00 |
|
AskaEth
|
749d6a8fe2
|
Merge branch 'feat/motion2-sidebar-collapse-and-hover-press'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-12 20:08:04 +08:00 |
|
AskaEth
|
c869a7b346
|
fix(motion2): 修掉 drawer 药丸守卫的同义反复(M257 r1 复审 P2-1)
`rail_selected_label_is_secondary` 里那条 `pill_color(..) == pill_color(..)` 比的是同一个
表达式,恒真、抓不到任何东西,而注释还写着「rail 与 drawer 两条线的药丸一致」。
药丸从 M257 起不吃 layout(`pill_color` 的签名里没有 `NavLayout`),所以「两条线一致」由
签名保证;本用例改判真正要守的那一件事:静息选中药丸来自 `SecondaryContainer`。
变异验证:把 `pill_color` 的兜底角色换成 `Primary` ⇒ 该用例在 tests.rs:54 红。
|
2026-11-12 20:05:58 +08:00 |
|
AskaEth
|
b61f342431
|
feat(motion2): 右栗折叠过渡 + 状态层 hover/pressed 过渡
A2 侧边栏(右栗 AI 栏)折叠/展开:
- 帧泵第三条轨道(pump/rail):展开 medium1 250ms + Emphasized、收起 short4 200ms +
EmphasizedAccelerate;拆出档走 snap(不做过渡);MotionScale::NONE 下不插轨道
- 宽度在 metrics::rail_width 逐帧插值;面板内容按目标宽排版 + 按动画宽裁切
(不重排 / 不溢出),宽度回到 48 时换成图标条(图标贴右端,位置不跳)
- pages/agent/mod.rs 的展开档面板纯搬移到 panel.rs(371 → 289 行)
A8 状态层过渡:
- interaction/layer:令牌化的进度时钟(short1 50ms),NONE 当帧落终值
- interaction/hover:取样控件把起跑搬到 Widget::update(早于订阅表求值)
- 按钮五式 + IconButton + 导航项药丸统一走 interaction::layer_color
(没有取样控件时逐位退回旧口径,出图基线不变)
- components/motion/mod.rs 的 needs_frames / advance 各加一句委托,
帧订阅仍然只有一处判断点(app/subscription.rs 未改)
|
2026-11-12 20:05:58 +08:00 |
|
AskaEth
|
c4706c759c
|
Merge branch 'feat/multiroot-discover-official-launcher-gam'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-12 19:56:45 +08:00 |
|
AskaEth
|
72137dc658
|
fix(presenter): 回落日志两因并列 + 老 ext 档案覆盖 + 死根档案边界用例(M274 r3)
|
2026-11-12 19:55:52 +08:00 |
|
AskaEth
|
70c6aec6dd
|
fix(presenter): 写设置那道门按(根,版本)判绑定 + 反查回落留日志 + 旗标改真名(M274 r2 P1-1)
|
2026-11-12 19:55:52 +08:00 |
|
AskaEth
|
a614b49538
|
fix(presenter): 多根 r1 复审修四处(磁贴按自己根描述 / 挑 Java 按自己根要求 / 一拍一次扫描 / 只读内容级快照)(M274 r1)
|
2026-11-12 19:55:52 +08:00 |
|
AskaEth
|
dc8323531b
|
feat(presenter): 多根发现(配置根 + 官方启动器标准位置),实例按自己的根解析存档/模组/启动(M274)
|
2026-11-12 19:55:51 +08:00 |
|
AskaEth
|
dfc0f4bcb3
|
Merge branch 'feat/ghostfix-boot-must-not-draw-a-stale-prev'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-12 19:44:26 +08:00 |
|
AskaEth
|
fb30598604
|
fix(app-iced): 首屏(Kind::Boot)不再把旧页画成 250ms 幽灵
`pages::render` 把 `previous_view` 整棵画在当前页**下面**(`stack![opaque(ghost),
current]`),只在空隙里露出来。boot 那份快照是 `construct.rs` 的零实例初始模型,
被 `refresh.rs` 在第一次 refresh 时换进去 ⇒ 每次启动都闪一张「这里还没有实例…」+ 空态
图标磁贴(退场腿 250ms,`Kind::Boot` 用的就是它)。
修法:`App::sync_motion` 丢掉旧页快照的判据从「没有轨道了」扩成「没有轨道了 **或**
这一档本来就没有旧页」,后者走 `Kind::has_old_page()`——M256 写好的设计口径,此前全仓
无人调用(死代码)。丢在 `update` 的尾巴、`view` 之前 ⇒ 一帧都画不到;不动
`components/motion/**`(M257 的 scope),也不动 `app/state/refresh.rs`(不在本 scope)。
用例:`motion_sync/tests.rs` 新增三条(首屏每拍都不许留旧页 / Peer 换页仍留着旧页退场 /
NONE 档既不起轨道也不留旧页),删掉守卫即红。`motion_sync.rs` 已 398 行,按 400 行硬线
把测试模块**纯搬移**到 `motion_sync/tests.rs`(233 行逐字节相同,仅去掉模块缩进)。
出图:`DevDocs/artifacts/ghostfix/`(页面区 y<720 残余 = 0px;去掉守卫的同一棵树在同样
三相位残余 6897/5291/4762px)。
|
2026-11-12 19:35:19 +08:00 |
|
AskaEth
|
a9f691e3ff
|
Merge branch 'feat/cifix2-stabilize-mtime-probe-and-annotat'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-12 18:51:27 +08:00 |
|
AskaEth
|
686dd9335a
|
test(core): mtime 探针改用显式 set_modified,update-e2e 失败升注解(M275)
tree_snapshot_sees_new_nodes_and_mtime_changes 的「同内容重写」时间维度断言在
Windows/NTFS 上不可靠:同一系统时钟 tick 内的两次写、或时间戳只在句柄关闭时按
粗粒度落盘,都会让 mtime 与写之前逐字节相同 —— main 上 test (windows-latest)
偶发红在这行 assert_ne!(check-run 130965734990)。改成显式时间戳:
File::options().write(true).open + set_modified(基线 + 5s),值由测试指定、
与 tick/落盘粒度无关;不用 sleep、不用重试。用例原本的目的(快照必须看得见
mtime 变化)保持不变,另附 4s 下界断言把「我们推的那一下」与 stat 抖动分开。
ci.yml 的 update-e2e 步骤原本是裸 cargo test,失败只留一句 exit code 1、
check-run 上零注解,匿名侧等于信息为零。改成与 test 作业同款:pwsh +
Tee-Object 存 $RUNNER_TEMP 日志、失败把 panicked at / error / test result:
FAILED / failures: 行以 ::error:: 输出(% → %25)、打印并 exit $LASTEXITCODE
(cargo 真码,不是 Tee 的码)。步骤名 update-e2e 与 --test update_e2e_test
逐字保留(check_test_source_off.py 的 ALLOWED_STEP 钉死这两点)。
行数不变(ci.yml 397 → 398,≤400):注释块等价压缩腾出的行给运行时逻辑。
|
2026-11-12 18:44:51 +08:00 |
|
AskaEth
|
a0a35596c8
|
Merge branch 'feat/adoptwire-wire-adopt-wizard-entry-on-ins'
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-12 14:36:05 +08:00 |
|
AskaEth
|
c5009442c5
|
fix(presenter): 自动发现实例按盘上痕迹选运行目录 + 空态用例补上分支/传参(M273 r1)
r1 复审的两条 P2。
## P2-1:外部实例不再一律 isolated
`versions/<id>/` 里出现 `mods` / `saves` / `config` / `options.txt` 任一 ⇒ `isolated`
(gameDir = `versions/<id>`);否则 ⇒ `shared`(gameDir = 游戏根 `.minecraft`)。
判据只 `exists()`,只读,不改盘;写盘仍然只在用户第一次改设置时(`ensure_profile`)。
- 判据收在 `host/game/discovered.rs::base_mode()`,签名 `(env, id, version_id)`:
**有档案的实例返回全局 `instances.mode`**(D20 原口径,一字不变),自动发现的按痕迹判。
- 派生运行目录的五处(启动 / mods / saves / addon install / addon pack)从传
`global_mode` 改成传 `base_mode` ⇒ 「读到的存档/模组在哪」与磁贴上的 ISOLATION 同一份判据。
- `isolation_of(env, id, version_id)` 多收一个版本 id:实例文件优先,缺省 `base_mode`。
于是懒创建档案(只写 launch 段、isolation 仍为空)之后,磁贴不会从「共用目录」翻回「独立目录」。
- 磁贴/详情:自动发现的实例多一句「运行目录由盘上已有的文件决定」(i18n 中英);
按启动时再弹一条说清**这一次**用哪一边(`pages::instances::discovered_run_toast`,
同文案在屏不重复叠加)。
- `dto::DISCOVERED_ID_PREFIX` / `is_discovered_instance` 成为「自动发现」的公开判据
(不给 `InstanceSummary` 加字段:那要改 24 处结构体字面量,跨 agent / cli)。
## P2-2:空态用例的覆盖面
r1 复审实测:把 `!block.rows.is_empty()` 改成 `block.rows.is_empty()`(0 实例时反而没按钮),
当时三条用例全绿。现在:
- `detail_empty` 拆成 `empty_not_selected` / `empty_no_instances` 两个函数,分支各自可断言;
- `the_cta_lives_only_in_the_no_instances_branch`:CTA 只在「一个都没有」那一支、
「没选中」那一支必须是 `Vec::new()`,且分派必须写成 `if has_instances {`(取反即红);
- `the_page_asks_the_empty_state_with_the_negated_row_count`:两条调用点(宽窗 + 窄窗)
都必须传 `!block.rows.is_empty()`,条数也钉住(多一条即红)。
- 两个变异已自证:传参取反 ⇒ 后一条红;CTA 挪支 ⇒ 前一条红(红原文见交付报告)。
## P3
- `java::target_major()` 改读并集快照:只有自动发现实例的用户原来会拿到默认 JDK17 而不是
该版本要求的 JDK(`profiles.json` 是空的)。有档案时顺序与旧写法一致。
- `instances()` 全量扫仍在 UI 线程——本轮不动,刷新时机与代价写进报告(塔另开一条)。
## 结构
`host/game.rs` 394 → 286 行:自动发现那一簇(痕迹判据 / 扫出来的版本 / 合成视图)搬到
`host/game/discovered.rs`(138 行),只为守 400 行硬线;`game::base_mode` 仍是同一条路径。
## 门禁
app-iced lib 973 passed / presenter all-targets 386 passed / i18n 36 passed /
fmt 0 / clippy -D warnings 0。
|
2026-11-12 14:21:30 +08:00 |
|
AskaEth
|
5479f25068
|
feat(presenter): 实例清单改并集(档案 ∪ versions/*)+ 空态接导入向导(M273)
用户实测:HMCL 能看见 `.minecraft/versions/樱落怡然客户端`,ULL 的实例页是空的
(日志 `instances=0`)。根因不是路径找错,而是 `host::game::instances()` 只列
`profiles.json` 里的档案,第三方启动器装好的版本在界面上根本不存在。
## 列表改并集(presenter/src/host/game.rs)
- `versions/*` 里**能解析出清单**(`<id>/<id>.json` 的 id 与目录名一致、继承链走到底、
符号链接不越界)且没有被任何档案绑定的版本,就地合成一条实例视图:
显示名 = 目录名、`version_id` = 目录名。扫描复用 `core::game::adopt::scan`,
没有另写一套目录遍历。
- 只读:合成视图不建目录、不写 profiles.json、不碰 versions/ 一个字节。
- 档案优先:某版本已被档案绑定时只按档案出一条(名字/隔离档/启动参数都保留),不重复列。
- 坏目录(无清单 / JSON 坏 / id 不符 / 缺父 / 越界)不当实例;`versions/` 读不出来也只是
退回「只有档案」的旧行为,不让实例页报错。
- 外部实例的视图 id 用 `ext-<fnv64>`:`is_valid_instance_id` 只收 `[A-Za-z0-9_-]`,
而版本目录名可以是中文(现场就是),直接沿用版本 id 会让**启动**在读实例设置时就失败。
## 懒创建档案(只有第一次改它才写盘)
`set_isolation()` 与 `apply_launch_args()` 的「找不到实例」改成 `game::ensure_profile()`:
外部实例第一次改隔离档 / 启动参数时才写 profiles.json(幂等,id 与视图 id 相同)。
## 空态可见入口 + 文案(app-iced / i18n)
- 空态分两种:列表里有东西但没选中(原「未选择实例」)与**一个实例都没有**
(新:「还没有实例」+ 实心主行动「导入已有版本」)。用户现场看不到任何入口正是前一种文案
在误导。
- `adopt_action()` 成为「导入已有版本」意图的唯一产地,页头按钮与空态主行动共用它。
## 门禁
`cargo test -p unlucky-app-iced --lib` 971 passed / `cargo test -p unlucky-presenter
--all-targets` 384 passed / `cargo test -p unlucky-i18n` 36 passed /
`cargo fmt --all --check` 0 / `cargo clippy -p unlucky-presenter -p unlucky-app-iced
--all-targets -- -D warnings` 0。
|
2026-11-12 13:13:53 +08:00 |
|
AskaEth
|
751ae80f28
|
chore(release): 版本号 0.2.5(动效第一批 + 发布链路三处根因修复 + 平台测试收口)
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-12 12:06:46 +08:00 |
|
AskaEth
|
1c3fc2bdd9
|
Merge branch 'feat/quietspawn2-quiet-child-spawn-for-motion'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-12 12:02:29 +08:00 |
|
AskaEth
|
634955d323
|
fix(app-iced): 探针与字体测试的子进程改走 platform::child::quiet(M272)
`cargo test --workspace` 在合并 tip f8d83d1 上被 child_quiet_guard 抓住两处绕开
`platform::child::quiet` 的子进程构造:
① `platform/motion.rs` 的 OS「减少动效」探针(M256 新增,Linux gsettings / macOS
defaults)是产品代码真回归:`run()` 里裸 `Command::new(program)`。改走 `quiet(program)`。
`quiet` 返回的就是普通 `Command`,`.spawn()` 的 `Child` 仍归 `run` 自己,所以
**300ms 超时 + 超时 kill + wait 一个字没丢**;`OnceLock` 缓存、单调用点、不在帧路径
这三条性质也原样保留(这次只换了构造器)。
② `fonts/tests.rs` 里重起自身 test binary 的那处同样改走 `quiet`;`.output()` 的管道
语义不变(已实测子进程断言仍然「1 passed」)。没有往 ALLOWED_ROLES 加白。
|
2026-11-12 11:28:56 +08:00 |
|
AskaEth
|
f8d83d1f77
|
Merge branch 'feat/motion1-page-transition-and-dialog-motio'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-12 10:52:05 +08:00 |
|
AskaEth
|
add8617494
|
fix(app-iced): 修 about_center 的平移项(模态从「斜滑」回到「绕中心缩放」)+ 补几何断言
复审 r3 的 P1:about_center 写成 T(-c)·S·T(c),按 iced_core 的「先右后左」算出来是
p → s·p − (1−s)c,不动点在 (−cx,−cy)(窗口外的镜像点)⇒ 卡片开框时从左上斜滑约
(1−s)·c,而不是绕中心缩放。改成 T(c)·S·T(-c)(= s·p + (1−s)c,不动点就是 c),注释同步。
根因是 zoom.rs 里 0 条 #[test](矩阵从没被断言过,967 条全绿抓不住)。新增
tests::about_center_keeps_the_center_fixed:中心不动(<1e-3)、尺寸 = 倍率、缩小时左上角
收向中心;三组矩形 × 三档倍率。把 translate 换回去即红。
顺带补齐「抓得住/抓不住」:components/motion/** 与 motion_sync.rs、platform/motion.rs 里
31 条用例现在全部两侧都有(脚本核对 missing=0)。
|
2026-11-12 10:50:04 +08:00 |
|
AskaEth
|
b6127b4735
|
fix(app-iced): 补回被变异回滚误删的两处修复(钉住档位 + 退场起点)
上一提交把一个中间态(BYPASS_PROBE + serialize 未删、Modal::Closing 无 from)提交了:
变异实验后我用 git checkout -- 复原,而那些修复当时还没入库,于是被一起抹掉,
直接用例把这件事抓了出来(3 条恒定红)。现在按正确内容重做:
- delete motion::serialize()(空操作)
- FORCED_SCALE: Cell<Option<MotionScale>>,configure 里钉住优先
- Modal::Closing { start_ms, from },退场从当前进度连续缩到 0
(教训记在报告:变异只在已提交的树上做,别在未提交的树里 checkout。)
|
2026-11-12 10:50:03 +08:00 |
|
AskaEth
|
787cb16189
|
fix(app-iced): 删掉空操作串行门 + 钉住档位贯穿产品启动路径 + 退场起点接当前进度
复审 r2 的三条:
1) 删掉 motion::serialize() 那把串行门:PUMP/LAST_FRAME 都是 thread_local,libtest 每用例
一线程 ⇒ 它改变不了任何结果(ULL_MOTION=off 下握门照样红)。留一个不起作用的同步原语
比没有更坏。subscription.rs 与 motion_sync.rs 的 idle_app 恢复原签名。
2) 真实根因是「喂给 configure 的值」:idle_app 先钉 STANDARD,随后 on_boot →
configure_motion(app/update.rs:276)→ boot_env::motion_scale(..) 再定一次档,把钉住覆盖掉。
改成 BYPASS_PROBE(bool) → FORCED_SCALE(Cell<Option<MotionScale>>):configure 里钉住优先,
于是三条定档路径(configure_for_test / on_boot / boot_env 的 env·后端 tier)都穿不过去。
两条用例钉住它(纯合成 + 真 Message::Boot),删掉「钉住优先」两处都红。
3) P2-2:Modal::Closing 记住起手进度 from,progress = from*(1-eased) —— 进场途中被关掉时
从当前进度接着缩,不再先跳到 1.0(反向硬跳)。新用例
closing_during_the_entrance_does_not_jump_to_fully_open,把 from 改回 1.0 即红。
|
2026-11-12 10:50:03 +08:00 |
|
AskaEth
|
c21c6df51b
|
fix(app-iced): 动效全局态按「测试隔离」修 —— 串行门 + 探针旁路(复审 P2-5 返工)
tip 8b6f090 上 7 条用例在 -j 2 下红,根因两层:
1) P2-5 引入的 configure_for_test 只挡调用点,而 App::update(Boot) 会走**产品** configure
再按真探针定档 —— 本机(RDP 会话)SPI_GETCLIENTAREAANIMATION 报「动画关」⇒ 档位被压成
NONE ⇒ 位移恒 0 ⇒ 门槛 B/C 的守卫全红(日志里 [motion] scale=0.00 就是它)。
2) 动效全局态(档位 + 进程级时间轴)在并行用例间共享,谁先写谁赢。
修法(判据一条没动):
- components :: motion :: serialize():**跨模块共用的唯一一把**串行门,subscription.rs 与
update/motion_sync.rs 的用例都取它(idle_app 把 guard 一起返回,活到用例结束)。
- configure_for_test 置 thread-local BYPASS_PROBE,configure 读它 ⇒ 产品启动路径也不会再按
探针改档;reset() 清旁路,供「故意走真探针」那条用例使用。
- NONE 仍要求严格 0、STANDARD 仍要求 12~20 帧与端点归位。
|
2026-11-12 10:50:03 +08:00 |
|
AskaEth
|
1edf645c2f
|
fix(app-iced): 复审 p2 五条(方向取轨序 / 退场重开 / 遮罩口径 / 恒真断言 / 探针隔离)
- P2-1 方向改由**导航轨序**决定:新增 nav_rank(route)(NavItem::ALL 下标),
Pump::observe 收 (screen, rank, detail),forward 比较 (rank, key) 而非只比屏键 ——
修掉 Logs↔Settings 的左右镜像;Download↔Content 同 rank,仍判同级水平(已登记偏离)。
- P2-2 退场途中开新框:observe_modal 先当场收尾(Hidden + 待关)再起 Opening;
sync_motion 收尾时若队列里还有框就不把 dialog_model 置空。
- P2-3 只改文字:遮罩起手是**整块画布色的不透明矩形**(把页面内容遮住),不是透明看不见。
- P2-4 删掉恒真断言(单个 Option 的 <=1),写明「不累积」是字段形状 + mem::replace 给的;
tests/timeline.rs 与 tests/modal.rs 的每条用例都补了「抓不住」。
- P2-5 新增 motion::configure_for_test(绕过 OS 探针),用例一律走它;
另留一条故意走真探针的用例证明 Reduced ⇒ NONE 的合成仍在产品入口上。
- 顺带:pump 的模态状态机拆到 pump/modal.rs(守 400 行硬线)。
|
2026-11-12 10:50:03 +08:00 |
|
AskaEth
|
4a77f1b421
|
fix(app-iced): 退场窗口内 Esc 也被吃掉(补键盘入口的闸门)
模态退场期间逻辑状态已关(state.dialogs 空),但 Esc 走的是 Message::Agent(Key) 那条
独立路径,不经过 on_dialog 的 modal_closing 闸门 ⇒ 那 150ms 里 Esc 会落到 AI 栏去收起面板。
escape() 顶部加同一道闸门(返回 true = 被吃掉),并给退场用例补一条断言。
|
2026-11-12 10:50:02 +08:00 |
|
AskaEth
|
098f3b8946
|
fix(guards): 把 unsafe-scope 的说明注释压缩到 397 行(守卫文件自己也要过 400 行红线)
M256 加 EXPECTED 一行后本文件 401 行,触发 guards.yml::file-size 自己那条门禁。
只合并注释里的重复表述(词法器段 8→6 行)+ 去掉两处纯分隔空注释行;
判据逻辑、shell 片段、EXPECTED 的三条路径一字未动。
|
2026-11-12 10:50:02 +08:00 |
|
AskaEth
|
b1c09c4c91
|
feat(app-iced): 页面切换按导航深度定向 + 模态缩放淡入(M256 MOTION1)
- transition.rs:唯一一张动效表。同级换页走水平(±12 / 旧页 ∓8)、进深层走纵向
(新页 +16 上浮 / 旧页 -8),进腿 medium2/medium1(300/250),退腿 short4→medium1(更短),
进用 Standard、退用 StandardAccelerate。幅度全部取自 Space 令牌。
- pump.rs:Slot 三互斥槽 → Kind(Boot/Peer{forward}/Push/Pop)+ 两腿轨道;新增模态状态机
(Opening/Open/Closing,展开后不占帧,退场走完交回一次 take_modal_closed)。
- components/motion/zoom.rs:绕中心缩放的自绘控件(renderer.with_transformation,
tiny-skia 与 wgpu 都实现了,文字也真的跟着缩放);scale==1.0 时不套变换 ⇒ 出图基线不变。
- pages/mod.rs:page_with_settings_transition(ui, current, previous, …) 叠
stack![opaque(旧页@退场位移), 新页@进场位移];旧函数只剩一句委托(blast radius 不变)。
- App.previous_view:换页时用 std::mem::replace **移动**旧模型(冻结快照),过渡结束当拍
置 None(不随换页次数增长)。
- 模态:逻辑状态当拍关(state.dialogs 立刻空),画面多留 150ms;退场期间弹窗消息被闸门吞掉。
- platform/motion.rs:系统「减少动效」三态探针(Windows=SystemParametersInfoW,
Linux/macOS=子进程+300ms 超时),OnceLock 缓存;Unknown **不降级**(探针失败不静默关动效)。
- MotionScale::HALF 语义核清并写进 design 文档:是**时长减半**(更快),不是半速。
|
2026-11-12 10:50:02 +08:00 |
|
AskaEth
|
5a182e80de
|
Merge branch 'feat/cachedim-rust-cache-key-includes-image-d'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-12 10:36:30 +08:00 |
|
AskaEth
|
a6060e7649
|
fix(cache): 发布侧 key 只取 inputs.os 且 tag run 只读不写(M270 r1 复审 P2)
r1 复审(p2-1items)指出:tag 触发的 run 只能读 main 或同名 tag 的缓存,而 main 上
没有任何 job 跑 ubuntu-22.04 / windows-11-arm 这些标签 ⇒ 发布侧四个槽位每次发布都是冷编,
写出的 target 缓存永远拿不回来,还会占 10 GB 配额把 main 的热缓存 LRU 挤掉。r0 的
「key = inputs.os + inputs.slot」既没解决死缓存,还把 Windows 借 main 热缓存的路掐了。
- 发布侧 key 改成 `${{ inputs.os }}`:linux-x64(22.04) 与 main 的 ubuntu-latest 仍然不同
key(M269 那条必须断),而 windows-x64(windows-latest) 与 main 的 ci/build 重新同 key
—— 同镜像,借它的热缓存安全(M270 前 Windows 一直走这条且产物是绿的)。
- 发布侧加 `save-if: ${{ github.ref_type != 'tag' }}`:tag run 一律不写(写出来是死缓存),
只有 main 上的 dispatch 预热会写 —— 那次 run 的作用域是 main,之后的 tag run 才读得到。
刻意不用 `cache-targets: false`:那会把 Windows 借热缓存的路一起掐掉。
- ci.yml 侧只修 Nit:`Linux-X64` → `Linux-x64`(node 的 os.arch() 是小写)。
- docs 照实重写:tag 缓存作用域、发布侧每次冷编的必然性、预热怎么做、只读不写防什么、
「同一镜像标签但底层悄悄升级」这档剩余风险与手动 bump 办法;并补回 M164 MSYS 报错的
原文(可搜索的指路)。新的 key 演算:改前主 key 相同 2 对(跨镜像 1 对)/ 跨镜像前缀
命中 2 组;r1 = 1 对(全是同镜像 windows)/ 0 组。
|
2026-11-12 10:31:23 +08:00 |
|
AskaEth
|
93dc27bed8
|
ci(cache): rust-cache 的 key 带上镜像维度,发布构建不再吃 24.04 编的 target/(M270)
M269 已端到端定性:`aws-lc-sys` 的 `bcm.o` 在 glibc≥2.38 的头下编出 `__isoc23_sscanf`,
被从 24.04 的缓存恢复到 22.04(glibc 2.35)去链接 ⇒ undefined。根因是 `Swatinem/rust-cache@v2`
的 key 只有 `-Linux-X64`:`ci.yml` 的 `build`(ubuntu-latest = 24.04)与 `release-build.yml`
的 `build`(ubuntu-22.04)同 job id、同 rustc、同一份 `Cargo.lock` ⇒ key 逐字节相同。
- 7 处 `Swatinem/rust-cache@v2` 全部加显式 `key:`(发布侧 `inputs.os-inputs.slot`,CI 侧矩阵
维度或该 job 的字面量镜像标签)。`key` 落在 key 的**前缀段**,故主 key 与兜底 `restoreKey`
两条恢复路径一起分开。
- 演算判据(按 config.ts 复刻拼接,12 站点 × 2 锁状态两两比对):改前主 key 相同 2 对、
restoreKey 跨站点前缀命中 4 组;改后均为 0。
- 机制注释写进两个 workflow(`-C metadata` 不含 glibc ⇒ 恢复后 cargo 判 fresh 的因果链),
完整推导、`cache-targets` 的取舍结论与「抓得住/抓不住」写进 packaging/docs/build-and-ci.md §3。
- 两个 workflow 仍 ≤400 行(397 / 399):加注释腾地方时按「判据一字不动、只并句」精简了
M164/M171/M184/M147 几处既有注释。
|
2026-11-12 10:18:06 +08:00 |
|
AskaEth
|
14e8cc4ef2
|
Merge branch 'feat/releasename-explicit-run-name-for-releas'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-12 09:29:04 +08:00 |
|
AskaEth
|
e2d8fd9593
|
ci(release): 发布工作流加显式 run-name,Actions 列表不再显示提交信息(M268)
release.yml / release-guard.yml 之前没有 run-name,GitHub 默认拿提交信息当运行名,
于是 v0.2.4 那条把「孤儿提交 / 无父 / 树 = dev <hash>」这类快照模型术语贴到了公开界面。
- release.yml: tag push / tag ref dispatch => `Release v0.2.4`;分支 ref dispatch => `Release 试跑 main(分支 ref,不发布)`
- release-guard.yml: push main / dispatch => `Release guard · main`;PR => `Release guard · PR #123`
- release-publish.yml: workflow_call 不产生自己的 run,故不加(文件头写明理由)
- 只新增 run-name,所有已有 `name:` 一字未动
- packaging/docs/artifacts-and-version.md 新增 §2.1:里程碑快照提交信息只写版本 + 一句话,附正/反例
|
2026-11-12 09:29:01 +08:00 |
|
AskaEth
|
ba5403a056
|
Merge branch 'feat/gentkey-multiline-pinned-key-output'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-12 09:28:49 +08:00 |
|
AskaEth
|
66eb912df0
|
fix(packaging): --generate-key 打印的 PinnedKey 改成 rustfmt 保持的多行形态(M265)
`--generate-key` 与「信任表是空的」报错里给人粘贴的 `PinnedKey` 原来是单行:
PinnedKey { key_id: "…", public_key_hex: "<64 hex>" },
这一行有 113+ 字符(即使 key_id 为空),超出 .rustfmt.toml 的 max_width = 100,
所以 `cargo fmt` **必然**把它拆成 4 行 —— 人贴单行 → 下一轮 fmt 改动 → 谁忘了跑 fmt
就把 fmt 红带进 main(T30 那次就是这条路径)。
改的是「打印模板」与文档,不是解析器(KID_RE 的 \s* 本来就吃两种形态):
- 新增 render_pinned_key(),字段各占一行、元素缩进 4/字段缩进 8、结尾逗号,
与 trust.rs 里 cargo fmt 生成的形状逐字一致;两条打印路径都走它。
- 提示文案不再暗示「贴完还要 fmt」,改成「贴完直接提交,不用再跑 fmt」。
- docs:signing.md §3 与 signing-runbook.md §2/§4/§8 的「那一行」→「那一段(4 行)」,
并在 runbook §7 明写这条断言在没装 rustfmt 时的退化边界。
- --self-test 新增形态断言:① 渲染结果 == 4 行期望字面量 ② 形状与 trust.rs 现存条目
(字段值抹平后逐行)一致 ③ 单行必然超 max_width、多行每行都不超 ④ 贴进标记之间能解析
⑤ 有 rustfmt 时在临时副本上真跑一遍,要求粘贴块字节不变(没有则打 note 跳过,不装成验过)。
|
2026-11-12 09:26:05 +08:00 |
|
AskaEth
|
cb051ca933
|
Merge branch 'feat/releasefix-windows-pe-version-readback-r'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-12 09:25:37 +08:00 |
|
AskaEth
|
bb572ac2dd
|
refactor(release): PE 读回步骤体拆进 packaging/ci-pe-version-gate.sh(release-build.yml 400 行守门)
复审 P1-1:main 上 release-build.yml 是 400 行(贴线),上一版 +23/−10 顶到 421,越过
guards.yml 的 file-size 硬门禁(LIMIT=400 是用户硬约束,不许抬)。按复审给定的方向「拆」:
- 新增 packaging/ci-pe-version-gate.sh(59 行):读回两个产物 + 从 Cargo.toml 同源取版本号 +
失败逐条转 ::error:: 注解(上限 30 行)+ 非 0 退出;找不到 python 仍 fail;
临时日志用 mktemp + trap 清理,仓库根按脚本位置反推(不依赖 cwd)
- release-build.yml 那一步只留 bash packaging/ci-pe-version-gate.sh "$BUILD_DIR" →
文件 421 → 396 行(比 main 的 400 还少 4)
- 判据一行没改:仍然读产物字节、仍然由 check_pe_version.py 决定红绿、仍然非 0 退出
复审 P2-2:check_pe_version.py 的「什么时候退出非 0」清单补上第 7 条第 ③ 项
(签名后不足 16 字节、读不到 dwFileVersion,check():124-125)。
|
2026-11-12 09:25:30 +08:00 |
|
AskaEth
|
47386ef2ff
|
fix(release): PE 版本读回在 Windows runner 上崩在 print 编码上(判据内部一条未降)
v0.2.4 Release run 43031591576 的 windows-x64 / windows-arm64 都红在「PE 版本资源读回」,
而 cargo build 是绿的、注解 API 上只有一句 Process completed with exit code 1.
根因:check_pe_version.py 把中文打在 stdout/stderr,本机 cp936 能编,Windows runner 是 en-US
镜像(代码页 1252),print() 直接 UnicodeEncodeError —— 判据全过也红,而且崩在 print 上,
日志里只剩 traceback。同仓 5 个 packaging 脚本已有 reconfigure(errors=backslashreplace) 守卫,
Windows 槽位跑的脚本里只有这一个漏了。
- check_pe_version.py: safe_streams()(管道钉 UTF-8、终端只放宽 errors)、
每产物摘要行 flush(stderr 的判据不再跑到摘要前面)、模块 docstring 补判据清单;
--self-test 增加「版本资源齐全的合成 PE 必须绿」正控 + 真子进程 cp1252/cp437 回归(红/绿两路)
- release-build.yml: 读回步骤先 ls -l 落产物大小,输出 tee 后逐行转 ::error:: 注解
(下次红能看到原因,不再只有退出码)
- 判据未降:仍然是读产物字节、仍然 exit 1,未改任何断言
实测:RUSTFLAGS='-C target-feature=+crt-static' 的 release 产物本机读回 exit 0;
同产物同命令在 PYTHONIOENCODING=cp1252 下,修前 exit 1 + traceback、修后 exit 0。
|
2026-11-12 09:25:30 +08:00 |
|
AskaEth
|
03b644fa0c
|
Merge branch 'feat/deadcode-linux-cfg-gate-listing-helper'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-11 22:58:04 +08:00 |
|
AskaEth
|
150243e0b6
|
fix(core): listing() 与唯一调用点同 gate #[cfg(windows)],消掉 Linux 上的 dead_code
CI 的 clippy (ubuntu-latest) 退出码 101 的唯一首因:
error: function `listing` is never used
--> crates/core/tests/adopt/surface_cases.rs:147:4
note: `-D dead-code` implied by `-D warnings`
`listing()` 的 4 处调用(43/65/69/73 行)全部落在 `#[cfg(windows)]` 的
`a_junction_on_the_instances_dir_must_not_write_into_the_game_root` 里,helper 本身没 gate
⇒ Linux 上调用点整块编掉,helper 变死代码;Windows 上跑不出来(本地一直绿的原因)。
最小改法:只给 helper 加 `#[cfg(windows)]`,与调用点对齐(同仓既有写法见
tests/launch/watchdog_cases.rs 的 `chatty_lines()`)。不删用例、不改断言、不给整个 mod 加
cfg、不用 #[allow(dead_code)] 静音。
验证:Linux 容器 ull-linux-check:bookworm 上 cargo clippy --workspace --all-targets
-- -D warnings;Windows 上 cargo clippy -p unlucky-core --all-targets -- -D warnings
且 adopt 测试清单仍有那条 junction 用例(4 处调用仍在编)。
|
2026-11-11 22:57:43 +08:00 |
|
AskaEth
|
a8ef1c70af
|
Merge branch 'feat/testport-three-platform-dependent-test-f'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-11 22:43:22 +08:00 |
|
AskaEth
|
d4959fd3d3
|
test(m262): 三条平台/时序依赖的测试改成确定性判据
① prefs.rs:custom 落点不再写死 D:/JDKs(Linux 上它是相对路径,会被拼到数据根后,
同一条断言在两个平台上验的是两件事)。改由临时目录给绝对路径;另加一条相对路径
必须按数据根解析的断言,把上一版抓不住的那一半补上。
② fonts/tests.rs:install::status() 读的是进程级静态槽,同 binary 的 page_messages
用例切 zh_cn 会真的发起下载并把它写成 Downloading ⇒ 前后快照比较与谁并行谁赢。
改成用 current_exe() 在**只有本用例的进程**里量,并断言前后逐值相等(任何写槽都算越界)。
③ fixture_cases.rs:目录 mtime 不再赌「写文件恰好落在另一个 tick」,改为显式钉 mtime
(t0/t1 整秒,对任何 filetime 粒度无余数)并逐 ns 比对。
三条都补了「抓得住什么 / 抓不住什么」。
|
2026-11-11 22:41:06 +08:00 |
|
AskaEth
|
98eb4c14c1
|
Merge branch 'feat/cinightly-nightly-ci-on-dev'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-11 22:21:47 +08:00 |
|
AskaEth
|
8abe8c1fa3
|
ci(m266): r3 只修注释的事实性,不动触发面(复审 p2-3items)
- ci.yml: 换算口径统一成「19:17 UTC = 本地(UTC+8)次日 03:17」(cron 是 '17 19',
原先内联写 17:19 差 12 小时,同一文件两套换算互相打架),全文只留这一种写法
- ci.yml: 通知收件人按文档原话改(发给「最后修改 cron 语法的人」,不是「默认分支的维护者」)
- ci.yml: 新增边界 ⑤ public 仓库 60 天无活动 scheduled workflow 自动 disable,
这是「夜里那轮某天不再响且没人发现」的真实失效模式
- ci.yml: 「:30 也挤」无文档依据,改成文档原话(整点高负载 + 负载过高时排队任务会被 dropped)
- ci.yml: ③ 的 event 依赖断言改严:除 concurrency 组那一处与 runtime-smoke-macos 的 if 之外无 event 表达式
- docs: 归因改对——base(main) 上 :101 当时是对的,:246/:283 早就指偏(依赖安装步 / 一行注释),
行号锚点本来就在腐,不是本次改动造成
行为面零改动:on / concurrency / jobs 的键值行与 r2 逐字相同(本提交的 ci.yml 差异全部是注释行)
|
2026-11-11 22:21:30 +08:00 |
|
AskaEth
|
55f0e9bbf1
|
ci(m266): 按塔判决把 cron 挪到本地凌晨 03:17(cron 是 UTC,写 17 19 * * *),补默认分支边界与证据强度
- cron 从 '17 3 * * *' 改成 '17 19 * * *'(17:19 UTC = 本地 UTC+8 的 03:17),
文件头写死「本地每天 03:17」,不留让读者自己换算的口子
- 文件头第 ① 条写严厉:schedule 只对默认分支生效、跑的是 main 末梢,dev 未合入的内容不在这一轮;
「dev 已经有人天天看着了」是错的(全量矩阵在 dev 上一次都没跑过)
- 新增第 ④ 条:定时必须与 push main 分列 concurrency 组,因为取消不是红,
被静默取消的夜间入口会让「昨晚没报错」变成一句假话
- concurrency 注释里把结论降级为「GitHub 文档语义,未在真实 runner 上构造并发验证」
- docs/backend-cjk-evidence-matrix.md:三处 文件:行号 锚点改成作业名 + 步骤名(行号必再腐),
并加一条「锚点不用行号」的说明防复发
|
2026-11-11 22:21:30 +08:00 |
|
AskaEth
|
86746153f9
|
ci(m266): ci.yml 加夜间定时触发(schedule 17 3 * * *),补 T30 的全量矩阵缺口
- 只新增 schedule,push main / pull_request / workflow_dispatch 三条一字不改
- 文件头写清为什么是 schedule 而不是 push dev(成本/排队 vs T+1 发现延迟),这是故意的取舍
- concurrency.group 带上 github.event_name:定时与 push main 落在同一个 ref 上,
否则 cancel-in-progress 会让两者互相取消,夜跑被取消 = 那一夜没有结果
- 逐个 job 核过 github.event 依赖:只有 runtime-smoke-macos 的 workflow_dispatch 条件,
schedule 下按设计跳过;其余 job 无 event 表达式,夜间不空转
|
2026-11-11 22:21:30 +08:00 |
|
AskaEth
|
060910c8b7
|
Merge branch 'feat/fmtfix-trust-rs-pinned-key-rustfmt'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-11 22:11:18 +08:00 |
|
AskaEth
|
00965e19d2
|
fix(core): trust.rs 的 PinnedKey 交回 rustfmt 排版(多行字段)+ 自测锁住多行解析
v0.2.4 推 main 后 CI 的 fmt job 红:cargo fmt --all --check 退出码 1,唯一 diff 在
crates/core/src/update/trust.rs:60 —— PRODUCTION 里那条 PinnedKey 被写成单行。
* trust.rs:由 `cargo fmt --all` 直接生成(不是手写对齐)。git diff 只有这一处;
两个 PINNED-KEYS 标记行的字形与行位置未动;public_key_hex 与 HEAD 逐字比对 identical。
* sign_manifest.py --self-test:补一条多行(rustfmt)形态样例的解析断言。解析器本身无需改
(KID_RE 字段之间是 \s*,本来就跨行),但旧自测只喂单行样例 —— 变异重放(换成单行整行正则)
时旧自测全绿、新断言红,正是这次事故的形状。
验证(真实退出码):cargo fmt --all --check = 0;python packaging/sign_manifest.py
--check-pinned-keyring --key <私钥路径> = 0;signing_ready_selftest.py = 0;
signing_ready_wiring_selftest.py = 0;sign_manifest.py --self-test = 0;
cargo test -p unlucky-core --lib update = 0(114 passed / 0 failed)。
|
2026-11-11 22:01:26 +08:00 |
|
AskaEth
|
38c568d6b7
|
Merge branch 'feat/gatefix-presenter-wildcard-dependency'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-11 21:52:13 +08:00 |
|
AskaEth
|
3c99b55110
|
fix(presenter): 给 [dev-dependencies] 的 unlucky-core 补 version 修 cargo-deny wildcard
crates/presenter/Cargo.toml 的 [dev-dependencies] 路径依赖漏写 version,
deny.toml 的 [bans] wildcards = "deny" 因此报
error[wildcard](cargo deny check licenses bans sources 退出码 2),
CI Guards 的 cargo-deny 步红。
补上与其他路径依赖同款的 version = "0.2.4",features 保持 update-test-key 不变;
该条仍只在 [dev-dependencies],不进发布二进制。
|
2026-11-11 21:49:55 +08:00 |
|
AskaEth
|
68362e5408
|
feat(core): 钉入生产签名公钥(key_id ull-release)+ .gitignore 防私钥入库
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-11 21:35:10 +08:00 |
|
AskaEth
|
c9eed39bd6
|
docs: 新增 CHANGELOG(自上而下,0.2.4 起;早期版本按 tag 从简)
|
2026-11-11 21:28:33 +08:00 |
|
AskaEth
|
4b184df1ba
|
chore: 版本号 0.2.4(里程碑快照;本批含存档截图/强调色/回车提交/GPU 门禁/签名就绪度)
|
2026-11-11 21:26:15 +08:00 |
|
AskaEth
|
fa30dd9450
|
Merge branch 'feat/m255-signready-signing-readiness-checker'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-11 20:22:55 +08:00 |
|
AskaEth
|
bc1b09b776
|
fix(packaging): 播报 workflow 的种子只在 main 注入 + --key 用法错回 2 + 指针修正(M255 r1 P2-1/2/3)
P2-1(安全):`signing-status.yml` 原先把生产签名种子注入到 `pull_request` 与任意 ref 的
`workflow_dispatch` 运行里,而 CI 跑的是那个 ref 自己的代码 ⇒ 任何能推分支的人都能把种子搬走
(这把种子能给全量已装客户端签可信清单)。改成:
- 去掉 `pull_request` 触发;
- 注入行加 ref 门:`github.ref == 'refs/heads/main' && secrets.… || ''`;
- 非 main 用 `ULL_SIGNING_KEY_WITHHELD` 说明原因,于是 ② 读「未判定(未注入)」而不是「没配」。
runbook §2 步② 加硬顺序:先合这笔、再录 Secret。
P2-2:`--key` 读不了(不存在 / 是目录 / 非 UTF-8)曾在 main 的 try 之外抛 traceback + rc=1,
与「未就绪」同码;现在统一 rc=2 + 一行说明,并补用例 `key_unreadable`。
P2-3:4 处文案指针修正(`signing-ready.yml` → `signing-status.yml`;§3 → §2 步③;§1–§3 →
§2 步①–③;§4 → §2 步⑤ / §5)。新增两条机检:`section_refs`(输出的 `§N` 必须是 runbook 真实
章节号)与 `signing_ready_wiring_selftest.py`(文档里的 `packaging/…`/`crates/…`/`.github/…`
路径必须真存在),后者另带三处变异重放:去掉 ref 门 / 加回 pull_request / 改坏文档路径 ⇒ 必须红。
顺带修掉 r1 复审指出的两处小问题:`--release-assets` 里只有 `manifest.json.sig` 时的子串假读数
(先剥 `.sig` 再判清单)、③ 那个从不参与判定的 bool 保持原样(无行为影响)。
selftest 拆成两份(400 行红线):判据本体 + 接线/文案。guards.yml 仍 399 行零改动。
|
2026-11-11 20:17:04 +08:00 |
|
AskaEth
|
bff48e27fa
|
feat(packaging): 签名就绪度播报 + 操作手册(M255)
M254 查出 `trust.rs` 的 `PRODUCTION` 表在 PINNED-KEYS 标记之间是空的 ⇒ 任何正式二进制都验不过
任何清单(fail-closed)。工具链早就齐了,缺的是操作步骤与可见性;本条补的就是这两样,不生成钥、
不改 Rust、不碰 release-publish 的判据。
- `packaging/check_signing_ready.py`(只读、零第三方依赖):三行读数 ① 信任表空不空 ② 环境有没有钥
(顺手对账「这把钥在不在表里」,种子一个字节都不打印)③ 最近一次发布的 .sig/清单痕迹(取不到写
「未知」)。退出码 0 就绪 / 1 未就绪(打印 fail-closed 后果 + 一行修复命令)/ 2 用法错。
- `packaging/tests/signing_ready_selftest.py`:判据自测,含**两个方向的变异重放**(退出码永远 0 ⇒
判据①必须红;永远 1 ⇒ 判据②必须红)与接线判据(没人 uses: 它、没有 error 级注解、就绪度那步恒 exit 0)。
- `packaging/docs/signing-runbook.md`:人类照做的 5 步 + 每步判据 + 11 条已知坑 + 没验的部分(无任何密钥值)。
- `.github/workflows/signing-status.yml`:把三行搬进 job summary 的**播报** workflow。guards.yml 已
399/400,与 gpu-evidence.yml 同先例另开文件;刻意不声明 workflow_call:,未就绪用 ::warning::、
就绪度那步恒 exit 0,明确「不应被设为 required」。
- `packaging/README.md` / `packaging/docs/signing.md`:索引与指针。
|
2026-11-11 20:05:45 +08:00 |
|
AskaEth
|
fbb663c87c
|
Merge branch 'feat/m252-fieldtouch-expose-on-submit-and-wir'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-11 19:55:23 +08:00 |
|
AskaEth
|
9ce54a8119
|
test(app-iced): accent 回车守卫改判代码行并整取实参(M252 r2 P2-1)
上一轮(e2a9692)声称三处守卫都改了,实际只有 field.rs 与 rows.rs 进了提交:
`section/accent.rs` 的改写留在工作树里,被我自己重放 C4 变异时的 `git checkout --`
还原掉了 —— 提交面因此看不到它,那条守卫仍是 `body.contains(..)`,注释掉接线后它照样绿
(只有白名单顺手红)。先修过程、再修判据:
- `the_accent_field_wires_enter_to_commit` 改成 `code_line(body, ".on_submit(")` 取**代码行**,
再用 `call_arguments` **整取**实参并断言等于 `["accent_commit"]` —— 顺带堵上
「`accent_commit_none` 这类同前缀产地静默通过」。
- `code_lines()` 的边界文档换成实测口径(`app-iced/src` 9 处 `r#"`、8 处内容含引号;
全仓 `crates/**.rs` 291 处),并写清两个方向:内嵌引号 ⇒ 尾部注释漏剥 ⇒ **假绿**;
JSON 原始字符串同行有 `//` ⇒ 多剥 ⇒ **假红**。两种都由 `code/tests.rs` 钉住。
过程纪律(写进报告):**变异重放只在已提交的树上做**,否则 `git checkout --` 会把未提交的
修复一起扔掉,而「声称改了、diff 没动」正是这次被复审抓到的形状。
|
2026-11-11 19:55:21 +08:00 |
|
AskaEth
|
a17f6fb29f
|
test(app-iced): 接线守卫改判「代码行」,注释掉不再算接线(M252 r1 P1-1)
复审实测:三处接线守卫都是纯文本 `contains`,把真接线注释掉(行注释,或 `/*` 独占一行的
块注释)之后 943 条用例一条不红;`field.rs` 那处连 rustc 的 unused-variable 兜底也能用
`let _submit` 一起绕掉 —— 功能整条静默失效时,测试与 lint 两条线同时不响。
- `pages/layout/source/code.rs`(新):`code_lines()` 剥 `//` 与跨行/可嵌套 `/* … */`,
字符串与字符字面量整段保留(`"http://x"` 里的 `//` 不剥);`code_line()` / `has_code_line()`
给「某行代码的行首就是那个调用」这条谓词。边界写在模块文档:原始字符串里出现 `"` 是唯一
可能朝松方向错的形态(本仓 src 现无此写法),其余偏差都是假红。
- 三处守卫改用它:`text_field_forwards_the_submit_callback`(行首是 `on_submit_maybe(`
且实参不是 `None`、消息来自 `submit_message`)、`the_rename_field_wires_enter`(回车与
「改名」按钮的行都含 `commit_rename()`,且都不含第二种页面意图 —— 一并关掉 r1 P2-1
「Debug 自证撑不住判据」)、`the_accent_field_wires_enter_to_commit`(含产地那条代码行)。
- `submit_is_wired_only_at_the_two_call_sites` 的 `scan()` 换成同一谓词并跳过测试文件
(守卫自己的 fixture 里也有 `.on_submit(` 样本)。
- T29 顺手收敛 scope 内的同族旧断言:`pages/instances/tests.rs` 的行距/散文接线判据与
`pages/instances/tests/i18n.rs` 的五条取词判据改走 `code_text()`(一行改动、方向不变)。
|
2026-11-11 19:55:21 +08:00 |
|
AskaEth
|
92a3c1bbc8
|
refactor(app-iced): 「禁用不提交」的规则收进 Field::submit_message
原来 text_field 里内联着 `if state.is_disabled() { None } else { field.submit_message() }`,
那条分支在 Element 上量不到(没有内省接口)⇒ 等于没验。现在把状态作为参数交给
Field::submit_message(state),规则本身可单测:submit_is_opt_in 多了 Disabled 那一档断言
(接了回调 + 禁用 ⇒ None),text_field 相应少 3 行。
|
2026-11-11 19:55:21 +08:00 |
|
AskaEth
|
ebce41b2c5
|
test(app-iced)+docs(appearance): 强调色「已提交」那一档的出图开关
D30 要拍「回车之后」的样子,但出图通道没有键盘(渲染第 3~4 帧就落盘)。所以把强调色
的两档取证开关(ULL_APPEARANCE_ACCENT / …_COMMIT)收进 accent 自己的模块:第二档在灌值
之后补一次 Accent::commit(),它做的正是回车那条消息(Change::AccentCommit)在状态层做的事
(State::apply 开头的 commit)。拍的因此是同一个状态,不是另一条渲染分支。
顺带两处口径订正:
- state.rs 少了 3 行(它贴 400 行硬线),强调色的 env 开关不再散在 state 里;
- rows.rs 的模块文档把「名字规则」指到 core 的
game/saves/write/boundary::check_name(原来写的是 presenter)。
|
2026-11-11 19:55:21 +08:00 |
|
AskaEth
|
f7cfc8ded6
|
feat(app-iced): 单行输入框透出 on_submit,回车接上存档改名与强调色提交
债务台账 P18(M250 + M247 的共同缺口):iced 0.14 的 text_input 有 on_submit、
没有 on_unfocus(builder 逐条核过:on_input/on_input_maybe/on_submit/on_submit_maybe/
on_paste/on_paste_maybe,on_unfocus 零命中),而本仓的 text_field 只透出
on_input ⇒ 行内重命名只能点按钮、强调色拿不到「用户确认」这一下。
- field.rs:Field 变 `Field<'a, M>`(M 只被新加的 on_submit 用到,调用点的类型由
text_field 的 on_input 一并推出,一行都不用改),回车消息唯一从 submit_message()
取,`on_submit_maybe(None)` 是默认档 ⇒ 既有调用点逐位不变;禁用态与 on_input 同一
条规矩被摘掉。
- 存档改名:回车与「改名」按钮共用 commit_rename() 一个消息产地,空名/原名不变/
非法字符那几道校验因此对回车同样成立。
- 强调色:回车发 Change::AccentCommit(不是又一次 Accent(原文))⇒ 立即 commit(),
把「打了一半就放着」的 #12 亮成红框 + 错行;逐键不报短错的规矩不变,落盘仍取
上一次合法值。
- 判据:submit_is_opt_in(未接就是 None)、text_field_forwards_the_submit_callback、
submit_is_wired_only_at_the_two_call_sites(白名单扫源码)、
enter_takes_the_same_message_as_the_rename_button、the_rename_field_wires_enter、
the_accent_field_wires_enter_to_commit、enter_commits_a_half_typed_value_and_
persists_only_legal_ones。真键盘回车量不到(Element 无内省、造不出 iced::Renderer
的测试替身),报告里单列。
|
2026-11-11 19:55:21 +08:00 |
|
AskaEth
|
026b9f6e94
|
Merge branch 'feat/m253-gpucigate-wire-gpu-shot-gate-into-c'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
GPU evidence / GPU 证据 — 真实产物判据 (push) Canceled after 0s
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Canceled after 0s
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Canceled after 0s
Dev Guards / GPU 证据门禁(同 main 口径;无 GPU 只跑自测) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-11 18:43:34 +08:00 |
|
AskaEth
|
8722c5a0ab
|
fix(ci)+test(packaging): 关掉「门禁关死而自检全绿」三条路:outputs 绑定、判据步真跑、自检步指向
r2 复审:r1 那 11 条 + M3 已闭合(复审自己重放 12/12 红),但它新造的 40 条变异仍有 11 条漏网,
其中三条是「1 行改绿」:把 `probe.outputs.available` 改指向 `reason`/字面量(门永久关、每次绿);
判据步的退出码在标记表之外被吞(`|| echo 先忽略` / `; :` / `| cat` / `if !` / `true # 原行`,
复审真 bash 实测 exit 0);自检步被指向另一个脚本(自检根本不跑)。
三条 P1:
- 新判据 9:`probe.outputs.{available,log,png}` 必须逐个等于 `${{ steps.detect.outputs.<同名> }}`;
- 新判据 5:把 `id: judge` 的 `run` 原文真跑两次 —— 正例必须 exit 0、CPU 档日志必须 exit≠0,
且 `bash -e` 与**裸 bash** 各跑一遍(`; :` 只在后者现形,实测 `bash -e`=1 / 裸 bash=0)。
这一条一次封掉整类「吞码」,不依赖标记表;
- 判据 2:`id: wiring` 的 `run` 必须含 `gpu_shot_ci_selftest.py`,且 `vectors`/`wiring` 必须是**单条命令**
(不许换行/管道/`;`/`&`/反引号/`#`/`$(`)⇒ `|| echo 先忽略` 无从下手。
两条 P2:
- 新判据 10:`on:` 必须同时有 `push`(含 `main`)/`pull_request`/`workflow_call`;
- 「载体依赖」这条边界补进四处(自检 docstring / gpu-evidence.md §8 / README / workflow 头注释):
整个 workflow 被删掉就没有东西会跑自检,`guards.yml` 399/400 行塞不进去。
止步线(写进 docstring 与 §8):封闭清单 10 类 / 33 条变异向量;明确不覆盖「改判据脚本」「删掉整个
workflow」「求值器与 GitHub 语义差」「判据 4 的合法调用替换」;本文档不宣称「不可能被改成总是绿」;
**自 2026-11-11 起停止加固,扩项须由人类新开 mission**。
新增 `packaging/tests/gpu_shot_ci_expr.py`(`if:` 小求值器,判据脚本仍守 ≤400 行:377/215/64)。
|
2026-11-11 18:31:30 +08:00 |
|
AskaEth
|
2e9702790d
|
fix(ci)+test(packaging): 接线自检不再给假安全证书:19 条变异重放全红,dev 线接上
r1 复审 P1-1:上一版自检对外宣称「不可能被改成总是绿」,实测 19 条变异漏 11 条
(最要命的是把 `available` 写成恒 false —— 门禁永久关闭而每次运行都绿)。根因是三条
判据只是字符串存在性检查、自测步与自检步被整条豁免、`is True` 挡不住 `'true'`。
`packaging/tests/gpu_shot_ci_selftest.py` 重写为 6 条判据:
① 五步按 `id:` 必须在场且无条件(`vectors`/`exitcodes`/`wiring`/`detect`/`judge`,
`exitcodes` 还得真断言 `expect 0/1/2`);
② 解析 `if:` 表达式后**求值**判「判定挂门」(`available` 取 `'true'`/`'false'`/`''`
必须真/假/假,认不出一律红)—— `|| always()` 这类文本层面的门现在真红了;
③ 两种调用形态都必须在场且产物来自 `needs.probe.outputs.*`;
④ 跳过路径**实跑 7 种输入**(新增 `packaging/tests/gpu_shot_ci_probe.py`),
断言 `$GITHUB_OUTPUT` 的 `available`、summary 文案、`::warning::` 与三条红线的 exit≠0;
⑤ `continue-on-error` 按 YAML 布尔语义判(`'true'` / `${{ true }}` 也算开启);
⑥ 扫所有 `python`+`packaging/` 的 `run`(自测步、自检步都在内),`|| true` / `; true` /
`|| exit 0` / 裸 `exit 0` 全算吞。
变异向量搬到 `packaging/tests/gpu_shot_ci_vectors.json`:21 条,按解析后的结构注入。
P2-1:`gpu-evidence.yml` 加 `workflow_call:`,`dev-guards.yml` 里 `uses:` 同一份 ——
直推 dev 时 selftest 那一半现在会跑(evidence 在 dev 上照样显式跳过)。
P2-2:跳过摘要与 `gpu-evidence.md §8` 改成唯一可行的补救路径(把产物提交进被 dispatch
的 ref,`git add -f`),并写明「就地拷文件在 CI 上做不到」。
措辞:三处「不可能被改成总是绿」改成能复跑的覆盖面 + 边界。
|
2026-11-11 18:08:31 +08:00 |
|
AskaEth
|
075769c6d2
|
feat(ci)+test(packaging): GPU 证据接进 CI:自测无条件跑,无 GPU 显式跳过(跳过≠通过)
新增 `.github/workflows/gpu-evidence.yml`:`selftest` 无条件跑 `--self-test` 与三个退出码
(0 过 / 1 判据不过 / 2 用法错,用真 CLI 走一遍);`probe` 只探测可用产物与硬件适配器;
`evidence` 的 job 级 `if:` 要求 `needs.probe.outputs.available == 'true'`,所以没产物时它在
Actions 里是灰色 skipped(不是一个绿色 ✓),原因写进 job summary 并打 `::warning::`。
新开文件而不是并进 `guards.yml`:那份已 399/400 行,`release-build.yml` 正好 400。
新增 `packaging/tests/gpu_shot_ci_selftest.py`:把接线口径变成机器判据(自测步不许有 `if:`、
真判据必须挂门、跳过必须 summary+`跳过`+`::warning::`+`$GITHUB_OUTPUT`、禁 `continue-on-error`、
判据命令不许 `|| true`),并带 5 条变异向量证明每种「改绿」写法都会当场红。
`packaging/docs/gpu-evidence.md` §8 记账(含「CI 上从没真跑过」)与 `packaging/README.md` 索引更新。
|
2026-11-11 17:53:53 +08:00 |
|
AskaEth
|
9234c1eeae
|
Merge branch 'feat/m251-saveshots-save-screenshot-thumbnail'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-11 17:29:30 +08:00 |
|
AskaEth
|
779a2ae254
|
feat(app-iced)+test(presenter): 句柄按 (目录,mtime,大小) 复用;缓存可清空;端到端出图
复审 r1 的三条:
1. 端到端图:DevDocs/artifacts/m251/saves-real-decode-light.png —— 真 host
(ULL_SCREENSHOT_REAL_HOST=1 + --data-dir 一个临时数据根)→ 后台列 screenshots/ →
palette_extract::thumbnail 真解码 → DTO → Handle::from_rgba → 上屏。三格是三张真 PNG
的真像素,副文案的 2.3 KB / 2.2 KB / 24.1 KB 也是真扫描值。造根脚本与复现命令进
artifacts/m251/{README.md,make-real-root.py},并写明「哪张是排版、哪张是端到端」。
2. 每帧重建 Handle 的成本:改成**缓存**(不是实测)。model.rs 新增 process-level
memo,键 = (截图目录, 文件, mtime, 字节数)(与 presenter 的缓存键同源,目录单列一格),
上限 120 条(≈12MB)。判据:the_same_screenshot_reuses_one_handle 断言两次
from_state 拿到**同一个 Handle id**;变异(换回 Handle::from_rgba)即红。
a_changed_screenshot_gets_a_new_handle 钉住「文件变了必须换句柄」。
3. 进程级 memo 的副作用:两层各加 #[cfg(test)] clear_cache()/clear_handles(),用例从
已知状态起跑;两条上限从 240 收到 120(≈12MB/层,算术写进常量文档)。
顺带:句柄键改用 push 拼(format! 模板串会被页面字面量守门当成英文文案抓走);
preview 的页面模型用例拆到 tests/saves/preview.rs(saves.rs 破 400)。
|
2026-11-11 17:09:17 +08:00 |
|
AskaEth
|
dd9962e610
|
test(presenter)+fix(i18n): 淘汰策略与共享缓存解耦;注释里的 ASCII 引号
- prune() 抽成独立函数:全局缓存被所有用例共享,谁往里面灌 240+ 条都会顺手清掉
别的用例刚缓存的键(实测:并行跑时 the_second_identical_refresh_... 假红)。淘汰
策略改在本地 Cache 上测,两条判据各自确定;
- clippy -D warnings:from_ref 代替 &[x.clone()];
- model.rs 注释里的 "原因 + OS 原文" 用 ASCII 引号 → 页面字面量白名单守门报红,改
中文引号;core/events 的三处文档失真登记为 TowerFinding(scope 外);
- 出处文案口径:TooLarge 的 detail 只留数字(presenter 不产人读文案)。
|
2026-11-11 16:52:47 +08:00 |
|
AskaEth
|
0b6b8f0a62
|
fix(app-iced): 缩略图墙一行两格 + 副文案只留元数据(出图实测)
- 3 格装不下:详情列宽 = FrameMetrics::column ≤ 424(1180 窗宽),卡片内宽约 370,
3×160+2×12=504 会把第 3 格挤成一条竖条(实测图上『未生成预览』竖着排)。改成 2 格
= 332px,与同列元信息卡的两列栅格同宽;
- 原因只进占位框(短句),副文案仍画『大小 · 时间』:160px 里塞「原因 + OS 原文」会摊成
三行,把墙变得比图还吵;原文留在 ScreenshotThumbFailure::detail 供排障;
- 出图:DevDocs/artifacts/m251/saves-light.png / saves-dark.png(1180×1400,
--software-renderer --screenshot,ULL_SAVES_FIXTURE=1)。
|
2026-11-11 16:40:41 +08:00 |
|
AskaEth
|
843f6d08f9
|
feat(saves): 截图缩略图墙(M251 · D54 全套:解码/缓存/上限/可见失败)
按 D54 落地,解码在 palette-extract、缓存在 presenter、像素随快照走、壳层只画。
palette-extract(上一提交)→ presenter:
- dto/saves/preview.rs:ScreenshotPreview{NotGenerated,Ready,Failed} + ThumbnailView
(带 is_consistent:长度 ≠ w×h×4 的像素不许进渲染器)+ ScreenshotThumbKind;
ScreenshotView 新增 preview 字段(像素不落盘,随 IPC 面走);
- host/saves/thumbs.rs:attach() 在后台线程(spawn_list 同一条线程)按 mtime 倒序解前
60 张、单张 >8 MiB 先查大小不进解码器、失败也逐条带原因;进程级 memo 键 =
(路径, mtime, 大小),覆盖/替换都会重解;超过 240 条只留本拍碰到的键;
- presenter 新增依赖 unlucky-palette-extract(塔已批,方向 presenter → palette-extract)。
app-iced:
- saves.rs 拆成 model / rows / wall(原文件 385 行,加完墙必破 400):model 里建一次
iced Handle(渲染回调零解码、零重复上传),wall 3 列排格子,没像素的格子给
160×90 占位框 + 逐条原因;text.rs 拆出 text/saves.rs 承载存档/截图一族文案;
- i18n 加 4 条「这一格为什么没有图」(未生成/太大/读不出/解不出)+ 金表同步;
- 出图夹具(saves_shot)合成两种场景的像素,并摆出四种格子(真图×2/未生成/超限失败)。
用例:presenter 9 条(含缓存命中不重解、mtime 变了必重解、第 61 张不解、
>8MiB 不进解码器、缓存有界)+ app-iced 3 条(三种格子的副文案、坏像素降级)。
|
2026-11-11 16:33:56 +08:00 |
|
AskaEth
|
798bfb279b
|
feat(palette-extract): 加 thumbnail(path, max_edge) 出限尺寸缩略图(M251 · D54)
D54 第 1 条:解码与缩放留在 palette-extract(截图是 PNG,现有 png/jpeg/bmp
解码 feature 已覆盖,零新依赖、零新 crate、零新 feature)。
- 把 finish() 拆成 decode_with_limits()(读 + 嗅探 + 卡解压炸弹)与 fit()(等比缩、
不放大),取色与缩略图共用同一段解码,只是目标边长不同;
- 新增 thumbnail(path, max_edge) -> Result<Rgba8, ExtractError>,失败给结构化错误
(Io / Decode),不 panic、不返回灰图;max_edge=0 按 1 处理;
- THUMBNAIL_MAX_EDGE = 160(D54 第 5 条)。
用例 5 条:真 PNG 等比缩到给定长边、小图不放大、坏 PNG 给 Decode、缺文件给 Io、
0 边按 1 处理。
|
2026-11-11 16:15:37 +08:00 |
|
AskaEth
|
5487f661d9
|
Merge branch 'feat/m250-accentwire-apply-custom-accent-colo'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-11 16:10:09 +08:00 |
|
AskaEth
|
c14622f828
|
test(app-iced): 钉住错行里不可见字符的 U+XXXX 形态(M250 r1 小事 2)
|
2026-11-11 15:54:59 +08:00 |
|
AskaEth
|
14282f3325
|
fix(app-iced): 复审 r1 的四条 p2 + 三条小事(M250)
- p2-1 删死状态:`Accent::scheme` 字段 / `scheme()` 取值口 / `set_flavor` 重派生全仓零读者,
且注释谎称「Ui 的唯一入口」。色值 `color` 与深浅无关,`Ui` 由 `rebuild_ui` 重算,
所以整条删掉,`new/set/reset` 也不再收 `flavor`
- p2-2 规则只定义一次:新增 `theme::tokens(flavor, accent, scheme, families) -> (Ui, Colors)`,
`App::rebuild_ui` 与出图 harness 都调它(harness 传 scheme=None,如实说明不覆盖 D38 分支);
README 改成「同一条规则、不是同一个调用点」
- p2-3 逐键不闪红:`Accent` 区分「显示中的错」与 `pending`。只有**不可能再变合法**(非 hex / 超 6 位)
才立刻亮;位数不足先挂 pending,等 `commit()`(点到别的外观控件)或配置读回才亮
- p2-4 订正 `app/state.rs:151` 那句反话(强调色现在是 UI 写入字段)
- 小事:hint 文案改「中性表面与错误色不随强调色变」;`Digit{ch}` 里的不可见字符显示成 U+XXXX;
8 颗色块补 tooltip(hex 原文,Overlay 底)
判据:新用例 `accent_errors_only_appear_when_they_are_actionable` 覆盖「输入中不亮 / 提交后亮 /
配置读回立刻亮」三态;gallery 出图逐字节与重构前相同(规则搬迁没改像素)。
|
2026-11-11 15:46:10 +08:00 |
|
AskaEth
|
c609827c49
|
chore(app-iced): 撤回越界的注释改动(app/state.rs 不在 M250 scope)
|
2026-11-11 15:12:15 +08:00 |
|
AskaEth
|
e345b55c09
|
fix(app-iced): 预设色板改成两行排版,注释与用例去掉色值字面量(M250)
- 预设色板原来挤在一行,出图实测被折成「7 + 1」(最后一颗掉到下一行)——改成
「标签 + 出厂」一行、8 颗色块一行;这是有意排版,不是被挤出来的
- src/** 的色值字面量门禁把注释与测试一起扫(那条门禁是对的):文档里的 `#RRGGBB`
实例改成不带 `#` 的描述,超长串用例改用 `TEAL.to_hex()` 拼出来
|
2026-11-11 15:10:03 +08:00 |
|
AskaEth
|
e6b2200360
|
feat(app-iced): 强调色接线落地(M250 ACCENTWIRE)
appearance.accent_color 此前是死线:配置键、presenter 往返、预设色板都在,但造 tokens 的
唯一入口恒走 DesignTokens::brand(board),用户填了色界面不变。
- theme.rs:Ui::with_accent + accent_scheme(唯一调用 DesignTokens::with_accent 的地方)
- app/state/theme.rs:rebuild_ui 三选一(显式强调色 > D38 取色方案 > 出厂板),diag 一行说清是谁生效
- pages/appearance:新增 accent 状态(原文/解析结果/方案/结构化错误)+ 设置页输入行与预设色块;
非法值保持上一次合法值并给出 Short/Long/Digit 三档原因,不静默回退出厂色
- presenter::prefs:AccentChoice::parse 成为唯一解析点,set_accent_checked 交回结构化原因
- app/appearance.rs:persist_appearance 带上 accent_color(此前写不进去),只写上一次合法值
- pages/harness/app.rs:gallery 也能吃 ULL_APPEARANCE_ACCENT,供出图矩阵
- core keys.rs:订正「空 = 出厂 #0078D4;accent 令牌全域生效」这句半真半假的说明
- i18n:强调色 7 条文案(链尾新域,金表只追加)
判据:同一组件(进度条 Primary)在空配置与 #006A5C 下色值不同(App 级 + 令牌级两条);
非法值拿到结构化错误、生效色不回退、不写盘、tokens 仍可构造。
|
2026-11-11 14:56:26 +08:00 |
|
AskaEth
|
4cdb46d2e0
|
Merge branch 'feat/m249-pixelpass-align-elevated-outlined-a'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-11 14:32:48 +08:00 |
|
AskaEth
|
31cb109445
|
fix(app-iced): 探针补回宽度判据,并订正覆盖面与 12.4 的口径(M248 r1 P2-1/P2-2)
P2-1:`TRACK_MIN_RUN = 200` 把「轨道被宿主裁短」这类几何回归从红变成绿——复审把 after 图里
0% 那条轨道右半截涂回画布色(连续段 160px)之后,那一行直接不成带,check_linear 靠另一条带
照样绿。现在拆成两条判据:成带阈值降到 **100**(实测:确定态行 318~320、不定态行 306~308、
文字行 22,两头都留得住),宽度**单独**判 ≥300(`TRACK_MIN_WIDTH`),不再藏在圆角检查的
find 里。反例复跑:两套主题都红「实测最宽段 160px @ 行 163」。
新增 `feedback/tests/bands.rs`(合成图,不依赖真实截图)两条用例,把两个阈值的取值区间钉住:
窄轨道必须仍成带且过不了宽度判据、22px 的文字段永远不成带。变异自证:调用点换回
`bands(.., 200)` → 两条用例都红(`left: [(12,15)] / right: [(5,8),(12,15)]`);
`TRACK_MIN_WIDTH=150` → 窄轨道用例红。
P2-2:订正两处写反的账——实测 `轨道带 2 条(高 [4, 5])`,**两条带都在**厚度/宽度判据里
(5 行那条含 1 行抗锯齿,near 容 ±1),不是「只剩一条」;12.4 只量主色两段的左右端点,
**不量**轨道厚度。根因(按单条 320 轨道心算 ~88px vs bands_by_run 按整行取 306px)已写进
`TRACK_MIN_RUN` 的文档。
顺带:12.1 的判据从 probe.rs 搬进 `probe/track.rs`——加完这几行 probe.rs 正好 401 行,
撞 400 红线;顺着 circular / panels 的先例一族几何一个文件。
|
2026-11-11 14:32:27 +08:00 |
|
AskaEth
|
8f5dac2ed6
|
fix(app-iced): 三处 Pixel 偏离按角色语义收敛(M248)
P2:Elevated 的容器改由高程令牌推导(Elevation::Level1.surface_role),解析值仍是
SurfaceContainerLow,图上逐像素不变;「比 Surface 高一档」从此只有 elevation.rs 一处定义,
组件里不再手抄角色名。
P3:Outlined 的 1dp 描边 OutlineVariant → Outline。design/color/mod.rs 的对比度纪律里
Outline 才是「非文字元素(目标 3:1)」,OutlineVariant 是纯装饰分隔线;图标按钮那一式本来
就是 Outline,文字按钮此前与它不一致(全站两套描边)。实测 Outline/Surface:Ink 5.08、
Silver 4.24(OutlineVariant 1.97 / 1.62)。
P4:进度轨道(线性 + 环形)SecondaryContainer → SurfaceContainerHighest——轨道是「还剩多少」
的背景尺,强调色只属于指示条。与 A1(LinearProgressIndicatorTokens.TrackColor)的差异在
progress.rs 模块文档与 geometry.rs 的 12.2 行登记,gallery 图注同步改成角色名。
判据:新增 buttons/tests/roles.rs 与进度轨道用例,断言的是「取到的角色」与 Ink / Silver /
Scheme::from_seed 三套方案下解析出的颜色,不钉任何 hex;三处各做一次变异自证(改回旧值即红)。
另修 feedback probe 的定位判据:轨道换成中性容器面后,「画布→轨道」的混色轴穿过中性文字,
按「计数」会把图注行数成轨道,改按「连续段」判(bands_by_run);顺带修掉 indeterminate_row
把标题行当主色带的既有问题(用改前的图 + 改前的探针复现过,与本次换色无关)。
|
2026-11-11 14:32:26 +08:00 |
|
AskaEth
|
81710ba38c
|
Merge branch 'feat/saves-write-operations-rename-delete-bac'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-11 14:32:12 +08:00 |
|
AskaEth
|
32384b952e
|
chore(presenter,app-iced): 复审 r2 三项(文档对齐 / 拆 dialog.rs / 收窄关闭路径)
1. 文档自相矛盾(复审 r2 第 1 项):`SavesState::apply` 的文档还写着「无论成败都清『在飞』与
『确认中』」——改完之后这是假的,而且与模块文档「安全判据第 3 条」互相打脸。现在逐支写清:
`Ok` 清 draft/pending、`confirm` 只在被确认的世界已不在快照里时清并登记 `closed_confirm`;
`Err` 只清 pending。生命周期表里的 `dismissed` 改成真字段名 `closed_confirm`(消费点是
`app/state/refresh.rs::settle_saves_confirm`)。
2. 拆 `app/update/dialog.rs`(复审裁决):397 行里 262 行是内联 `mod tests`,而它装的是
主按钮 / Esc / 次按钮三条不可逆分支。照仓内先例(`app/dialog.rs` + `app/dialog/tests.rs`)
把测试体搬到 `app/update/dialog/tests.rs`,**纯搬移**:`dialog.rs` 397 → 138,
`dialog/tests.rs` 262;从 `6c87e2a` 的 `dialog.rs` 里抽出 `mod tests` 去一层缩进后与
新文件**逐行比对 0 处差异**(262 = 262)。
3. 收窄关闭路径(复审 r2 加固):`SavesState::close_confirm(current)` 只在 `current` 等于登记的
那个框时才清;`clear_confirm()` 保持无条件(换实例/复位用)。`escape()` 与 `Secondary|Dismiss`
改成传当前框 id。新用例 `closing_another_dialog_leaves_our_target_alone`(别人关框不清我们的
目标;自己关框才清)。
|
2026-11-11 14:25:26 +08:00 |
|
AskaEth
|
6c87e2ad8a
|
fix(presenter,app-iced): 删除确认绑定到对话框身份 + 快照不再吃掉目标(M247 r2 复审)
复审 r1 P1-1(安全):`SavesState::confirm` 的「关闭即清」在 Esc 这条路径上没有落实,
而主按钮分支无条件先取目标 —— 「删存档 → Esc → 崩溃卡片 → 导出诊断包」会静默删掉那个世界。
两层修,各自有独立用例钉住:
1. **认框不认人**:`confirm` 改成 `Option<(DialogId, String)>`,`take_confirm(current)`
只在 id 相等时交付;`DialogMessage::Primary` 传 `dialogs.current().id`。即使别的路径漏清,
也核销不了别人那个框。用例 `a_leaked_target_cannot_be_redeemed_by_another_dialog`
(去掉 id 校验即红:实测「漏清的目标被别的框兑现了:世界被静默删除」)。
2. **关闭路径补全**:`update/dialog.rs::escape()` 补 `clear_confirm()`(次按钮/遮罩早就清了,
Esc 漏了)。用例 `escaping_the_delete_dialog_disarms_it_for_the_next_one` 复现整条事故链,
断言没有下发 `DeleteWorld`、框走自己的路关掉(去掉这行即红)。
P2-1:`changed()` 不再无条件清 `confirm`(一拍无关快照会让主按钮变成静默空操作)——
只在「被确认的世界已经从快照里消失」时清,并登记 `closed_confirm` 让 app 层**收起那个框**
(`refresh_view → settle_saves_confirm`),避免留一个点了没反应的按钮。`failed()` 同理不动 confirm。
用例:`an_unrelated_snapshot_keeps_the_confirmation_redeemable`、`a_vanished_world_closes_its_confirmation`、
`an_unrelated_snapshot_no_longer_drops_the_confirmation`、`the_confirmation_is_dropped_and_reported_when_the_world_vanishes`。
P2-2:`catalog/instances.rs` 那段「这一版只读 / InstancesSavesReadOnly」的陈旧说明改成现状
(写操作已接上,边界由 core 的 `write::boundary` 守)。
拆分(400 行):`presenter/src/state/saves.rs` 的交互态方法 → `state/saves/interaction.rs`、
判据 → `state/saves/tests.rs`;`app/update/dialog.rs` 的接线用例内联在该文件(5 条)。
|
2026-11-11 13:51:07 +08:00 |
|
AskaEth
|
8c67e4eca6
|
fix(presenter,app-iced): 「游戏在跑」挡在落 core 之前(M247 r2)
塔裁决:三个写操作在实例启动/运行时必须拒,判据不能只靠界面灰按钮 ——
Windows 上是 os error 32 兜底,**Linux 上删一个正在跑的世界会成功**,那是在
用户玩到一半时把存档删掉。
- host/dispatch.rs:`AppCommand::Saves` 与 `AppCommand::Mods` 同款,先查
`CoreHost::in_flight(&id)` 再把 `running` 传下去
- host/saves.rs::dispatch 增 `running`:为真时三个写操作**不 spawn、不碰盘**,
直接回一条 `SavesFailureKind::GameRunning` 的结构化失败;读路径与「打开文件夹」不受影响
- dto:新增 `GameRunning` 类别(不折进 WriteFailed —— 那不是"写失败",是"我们主动没写")
- i18n:「游戏正在运行(先退出再操作)」中英成对 + 金表同步
- 用例:在飞时三条写命令盘上指纹不变/世界还在/备份落点没建,且回 GameRunning;
反向用例钉住闸门没有把写操作一并关死;UI 侧断言三种动词的主句都不出现「写盘失败」且不画重试
- 拆分(400 行):host/saves.rs → host/saves/write.rs;两处测试各拆出 write 子模块
- SavesState 模块文档补「生命周期」表:draft / pending / confirm 谁写谁清、何时必须清,
并把 confirm「取走即清」(连点两下不删两个)写成安全不变量
- 出图:ULL_SAVES_FIXTURE=running 一张(失败那句话的样子)
|
2026-11-11 13:29:56 +08:00 |
|
AskaEth
|
c921b9c185
|
feat(core,presenter,app-iced): 存档三个写操作(重命名 / 删除 / 备份)+ 行操作区(M247 SAVESOPS)
core::game::saves 新增 write 模块,三个入口的第一句都是 boundary::guard:
- rename_world:saves/<old> → saves/<new>,撞名/同名/非法名字各自具名拒绝
- delete_world:递归删(不跟随符号链接、层级上限 64),先过边界校验
- backup_world:流式 zip(限额预检 → 临时文件 → 原子改名),
<世界名>-YYYYMMDD-HHMMSS.zip,撞名顺延,绝不覆盖上一份
guard 三道判据(每次写前重做,不信任调用方):名字必须是单段裸名字;
saves/ 必须是运行目录的真实直属子目录;世界目录必须是 saves/ 下的真目录
(解析后比父级,不比字符串前缀)。落点目录要求绝对路径(相对路径会落到进程 CWD,
实测过一次)。
presenter:SavesCommand 增 RenameWorld/DeleteWorld/BackupWorld,host 三个后台入口
写成功后重新列一遍目录并把「回执」挂在快照上(SavesOped:改名两端 / 被删的名字 /
备份落点+字节数+跳过的链接数)。SavesFailureView 增 file 与 op,写失败八类一一对应。
SavesState 增 draft / pending / confirm 三样交互态(草稿是行内输入缓冲,
pending 与 ModsState::pending 同一条先例,confirm 是删除确认框在等谁)。
app-iced:世界行右侧重命名/备份/删除;行内输入框改名(现成模态队列没有「输入变了」
这一条消息,用输入模态等于重写一套);删除走现成 modal 的 Confirm,主按钮落点接在
app/update/dialog.rs。回执行画在卡片里(备份落点看不见就是静默失败)。
i18n:新增 25 条中英成对 key + 金表同步;删掉 M235 的「这一版只读」那条(写操作接上
之后它是假的)。出图夹具 ULL_SAVES_FIXTURE=rename|confirm|backed-up 三种形态。
|
2026-11-11 13:29:56 +08:00 |
|
AskaEth
|
3e852331f4
|
Merge branch 'feat/m250-gpushotgate-gpu-screenshot-evidence'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-11 13:24:47 +08:00 |
|
AskaEth
|
ff1a1677f5
|
Merge branch 'feat/surface-unknown-launch-placeholders-inst'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-11 11:40:08 +08:00 |
|
AskaEth
|
c97ec293c3
|
test(core): 「哪些键真的交给日志」补自动化证据(M244 r2 P2-1)
复审 P2-1:旧用例只断言 `collect_fresh`(纯函数)与 `unknown_keys()`(表级清单),
没人断言 `report` **真的**只把「新键」交给 sink;把 `for key in fresh` 改成
`for key in unknown`(每次渲染重复写日志)当时 6 条用例全绿,而「同键只报一次」正是判据 ③。
- `report_with(&self, unknown, sink: impl FnMut(&str))`:`report` 变成
`report_with(unknown, emit)`,`emit` 仍是 `cfg(test)` 空实现(不碰进程级单例,flake 不回潮);
- 新用例 `report_emits_only_fresh_keys`:假 sink 收集,断言一次渲染内重复只交一次、
跨渲染只有新键才交、顺序与 `collect_fresh` 一致;
- 顺手收掉自己列的边角:`unknown_log_line` 先把键名里的 `\r`/`\n` 换成空格
(`launcher.log` 是行导向的,恶意清单塞换行能把一个键变成两行假日志),
新用例 `log_lines_never_break_into_two_lines`。
变异自证(各自 exit 101,均已还原、文件 sha256 复原):
- `for key in &fresh` → `for key in unknown` ⇒ `report_emits_only_fresh_keys` 红,
`left: ["a", "a"] right: ["a"]`;
- 删掉 `key.replace(['\r','\n'], " ")` 那一行 ⇒ `log_lines_never_break_into_two_lines` 红,
`left: 2 right: 1`。
门禁:fmt / check -p unlucky-core --all-targets / test -p unlucky-core -p unlucky-cli
(1678 passed / 0 failed)/ clippy --workspace --all-targets -- -D warnings 全绿。
|
2026-11-11 11:32:27 +08:00 |
|
AskaEth
|
60607ae7b3
|
feat(core): 未登记占位符不再静默(M244 PLACEHOLDERWARN)
`PlaceholderMap::render` 一直把未登记的 `${key}` 原样写回 argv,运行期零校验零告警:
第三方 version JSON 里一个拼错的占位符会原样进 `cmd_built.argv` / `--dry-run`,
用户只看到一个 `${xxx}` 和零解释(M239 REDACTAUDIT §5.2 的机制风险)。
- `render_tracked` 返回 `RenderOutcome { text, unknown_keys }`,未登记键去重保序;
`unknown_keys()` 交表级全量清单,供后续接 `AssemblyWarning` / `--dry-run`。
- `render` 在此之上把每个**新**见到的键写一行 `launcher.log`(进程级去重,一个键一次启动
只报一次);未登记键仍原样保留,argv 逐字节不变(第三方清单兼容性优先)。
- 大小写/首尾空白变体按**不同的键**处理(替换本来就是精确匹配),日志行对近似已登记键的
附「疑似 ${known} 的笔误」提示。
- 测试构建里 `emit` 是空实现:`launcher_log` 是进程级单例,lib 用例并行跑时会把它自己的
`buffer_is_bounded_before_the_dir_is_known` 顶红(实测 205 行);行文本与「哪些键该记」
分别由 `unknown_log_line` / `collect_fresh` 的用例覆盖,落盘用一次性探针手工验过。
变异自证:把 `note_unknown` 改成空实现 ⇒ 5 条新用例红(exit 101),还原后绿。
门禁:fmt / check -p unlucky-core --all-targets / test -p unlucky-core -p unlucky-cli /
clippy --workspace --all-targets -- -D warnings 全绿。
|
2026-11-11 11:12:23 +08:00 |
|
AskaEth
|
0b40e1dfe2
|
test(core,presenter): 夹具口径跟随 M240 收窄(长类名不再被抹)
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
M240 让词形长路径不再被当成不透明串,而 M241 的两份夹具仍断言「必须被抹成
<redacted:opaque>」——两条各自的 pre-rebase 门禁都绿,合并 tip 上才互相矛盾,
维护主线短暂变红。
- core:raw_evidence_hits_... 改成断言「脱敏文本也保留类名、两边都能命中」,
并改名 ..._and_m240_keeps_it_visible_after_scrubbing;保留一条守卫:若将来
又把词形长路径抹掉,这条会红并提示改用 raw 窗口的用例说明其必要性。
- presenter:展示面与盘上的断言从「不许出现类名」翻成「应保留类名、且不许
出现凭据」——类名不是凭据,留着才看得懂崩溃原因。
|
2026-11-11 10:48:16 +08:00 |
|
AskaEth
|
8a65562595
|
Merge branch 'feat/mask-bearer-tokens-followed-by-trailing'
|
2026-11-11 10:48:07 +08:00 |
|
AskaEth
|
cc90c64b84
|
fix(core): 行尾句读不再让真 token 整串落盘(M243 BEARERDOT)
`Bearer <JWT>.`(行尾一个英文句点)整串原样落盘:句点自己属于 dotted 字符集,
扫描器把它算进 run 后 `split('.')` 多切出一个空段,「每段 ≥8」当场不成立。
M240 实测 old == new == 原文,影响 core 的全部落盘/展示面。
修法不是抬阈值:`shape::split_trailing_punct` 在判定前把末尾句读(`.` `,` `;` `)`)
切出去,`push_dotted` 再把句读原位写回——占位符只吃 token 本体,句子标点一个不丢。
只有 `.` 会进入字符集,其余三个本来就在外面,列全是为了「句读不参与判定」只有一处出处。
`has_dotted_token` 的闸门同步改成看同一份切分结果,否则闸门关着就静默不抹。
判据:`tests/bearerdot.rs` 三组——四种形状(`Bearer <JWT>.` / `… <JWT>. then` /
`<JWT>,` / `(<JWT>)`)必须抹且标点保留;句点结尾的普通文本(`Service.Unavailable.today.`)
必须逐字原样;两个入口在带句读的输入上必须一致。语料 68 文件 / 243 行仍 0 行被改,
JWT/sha256/base64 正例仍被抹。变异自证:把 trim 改回旧行为 → 主判据红(EXIT=101)。
外层两个既有红(diagnose::session / presenter m241 夹具)是 M240 收窄后的口径冲突,
stash 回 main HEAD 可复现,已各 file 一条 finding,不在本轮修。
|
2026-11-11 10:48:04 +08:00 |
|
AskaEth
|
ea108c5fc4
|
Merge branch 'feat/sync-cli-and-agent-diagnosis-with-stored'
|
2026-11-11 10:47:33 +08:00 |
|
AskaEth
|
1a06163407
|
feat(cli): M242 —— ull diag 与 AI diagnose_crash 改读会话里落盘的结论
两个入口原来都是「读回会话事件 → 在**已脱敏**文本上重跑规则」。脱敏一旦有损
(`≥64` 不透明串、带点票据串 → 占位符),重跑出来的不是「少一点细节」,而是
**换一条结论**:`fabric.loader_0_12`(critical)降级成 `integrity.no_class_def_found`
(warning,把「Fabric Loader 版本不配套」说成「库没下全」)。M241 起写会话时已经在
原始行上算过结论并落成 `diagnosis` 事件(且过 `scrub_json`),所以两个入口改成
**优先读落盘那份**,只有没有该事件的老会话才回落到重跑。规则 pattern 一个字符没动。
- `commands/sessions/diagnosis.rs`(新):结论来源选择(stored / rerun)、人类输出渲染;
`ull diag` 的 payload 增加 `conclusion_source`,人类输出多一行「结论来源」。
- `commands/sessions.rs`:`diag` 瘦成取结论 + 拼 payload(240 行级 → 拆文件守 ≤400 行)。
- `commands/agent/ports/diagnose.rs`:`diagnose_crash` 同改;`read_game_log` 与
`CrashDiagnosis.evidence` 仍是脱敏文本(隐私选择不变),只换结论来源。
- 用例(真 `SessionWriter` 写盘 → 真 CLI `crate::run` / 真工具注册 + handler):
`sessions/m242_tests.rs`、`agent/ports/diagnose/tests.rs`;含老会话回落用例。
变异自证:关掉落盘分支 ⇒ 两条用例红(`rerun`/`integrity.no_class_def_found`),已还原。
|
2026-11-11 10:34:18 +08:00 |
|
AskaEth
|
d5a6f5dc50
|
Merge branch 'feat/narrow-log-redaction-overreach-on-brands'
Guards / 依赖许可与来源(cargo-deny) (push) Canceled after 0s
Guards / 文件行数 ≤400 守门 (push) Canceled after 0s
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Canceled after 0s
Guards / design 零第三方依赖守门 (push) Canceled after 0s
Guards / presenter 零 UI 守门 (push) Canceled after 0s
Guards / core 零 UI 守门 (push) Canceled after 0s
Dev Guards / 架构与合规守门(同 main 口径) (push) Canceled after 0s
Dev Guards / fmt (rustfmt --check) (push) Canceled after 0s
Dev Guards / clippy (ubuntu) (push) Canceled after 0s
|
2026-11-11 10:15:51 +08:00 |
|
AskaEth
|
a07482f828
|
docs(core): M240 r2 复审 —— 两处注释口径(= 边界代价写清、DNS 形归属改正)
纯注释,判据/阈值/测试断言/扫描器零改动。
① `is_dotted_token_char` 的 doc 原写「JWT 自己的 `=` 只可能是末尾填充,拆出去不损伤
覆盖面」——作为无条件说法不成立。改成有范围的措辞,并把这条例外并入模块文档
「旧行为抹、新行为留」第 1 条(不新开章节):`-DaccessToken=abcdefgh.ijklmnop.qrstuvwx`
(整段 40 字符、三段各 ≥8,旧规则抹)现在 `=` 切段后值只剩 26 < 40 → 原样留下。
旧/新两侧都实测过(`git show 74be510` + rustc 探针 / 当前代码探针)。
② `cdn_and_mirror_urls_survive` 里 `resources.download.minecraft.example-cdn` 的注释
原写「全靠必须含大写这条必要条件挡住」——不成立:它是 DNS 形状,由 `is_url_authority`
→ `is_dns_host` 豁免(删掉大写判据该测试仍绿)。改成正确归属,与
`lowercase_dotted_names_are_spared` / `the_uppercase_requirement_is_load_bearing` 的
doc 口径对齐(DNS 形靠主机名豁免、非 DNS 形靠必须含大写)。
|
2026-11-11 10:15:49 +08:00 |
|
AskaEth
|
66929786c0
|
docs(core): M240 复审 P2-1/P2-2/P2-3 —— 口径收窄、大写判据钉住、找回丢掉的负例
P2-1:「抹除能力没有下降」是绝对承诺,已改成准确口径——收窄的就是三类形状
(`=` 边界、填充不计段长、全小写点分串),每条都在 shape 模块文档里写明,并用
`known_narrowings_old_masked_new_keeps` 把「旧行为抹、新行为留」钉住。三条的旧行为
是用 M240 之前的真实代码(`git show 74be510:…` + rustc 探针)实测的,不是推断。
P2-2:「必须含大写」原先没有任何测试钉住(复审实测:删掉它 13 条仍全绿)。
查明原因:DNS 形状的全小写点分串早就被"首段是主机名"那条豁免救下,大写判据
只对**非 DNS 形状**的全小写点分串承担区分作用(标签带 `_`、标签超 63 字符、
`/` 前只有一段主机名)。选择保留(复审的 (b)),并补
`the_uppercase_requirement_is_load_bearing`(`sophisticated_backpacks.upgrades.reference`
等两条)——删掉判据 → 该测试与 `lowercase_dotted_names_are_spared` 红、其余 15 条绿。
同时改正事实错误:不是"第三段必为大写",而是头的 base64url **第三个字符**恒为
`ey` + `I/J/K/L`(`7B 22 xx` 的第三组 6 位 = `b1` 低 4 位 ++ `b2` 高 2 位 → 索引 8..11)。
P2-3:找回重构时丢掉的老负例 `dotted_token_rule_does_not_eat_normal_text`
(4 条用例 + 直接断言 `!has_dotted_token(...)`,含 `eyJhbGciOiJIUzI1NiJ9.short.abc`);
语料测试的 doc 注明它**只守一个方向**,不得当"正例仍被抹"的背书。
判据与阈值一个字没动(40 / 3×8 / 64),扫描器代码零改动,纯文档 + 测试。
|
2026-11-11 10:15:49 +08:00 |
|
AskaEth
|
43f77d8865
|
fix(core): 收窄日志脱敏的三类误杀(品牌串 / CDN 主机名 / 长路径)
M239 实测两条形状规则会误杀三类正常文本,被抹掉的正是用户要复制的面
(`--dry-run` 的 argv、`cmd_built.argv`、诊断规则引擎的输入)。修法不是抬阈值
(40 / 3×8 / 64 一个字没动),而是给每条规则加**必要条件**:
- `-D<key>=<品牌>` 被当三段式 token:`=` 是赋值边界、退出 dotted 字符集
(JWT 的 `=` 只可能是末尾填充),键里的点号不再把值拖进段数统计。
- CDN/镜像 URL 变成 `https:<redacted:token>`:加"必须含大写"与"首段不是 DNS
主机名"两条必要条件(JWT 头是 JSON,base64url(`{"`) = `eyJ` 必含大写;
base64url 不含 `/`)。扫描器先切主机名,路径那半继续判——JWT 放进 URL
路径里照样抹。
- ≥64 无点号长串吞掉整条路径:路径形状(被 `/` 切开且每段是词形名字)整条
保留,但**段本身**≥64 的照抹(`/…/objects/<64 位 hex>` 只抹那一段)。
真凭据的抹除能力不变:登记过明文值的替换路径没碰;正例(本仓 JWT / MSA JWT /
`-DaccessToken=` 粘着 / URL 路径里的 JWT / 标准 base64 三段式 / sha256 hex /
base64 ≥64 / `A`×64)全部仍被抹。回归语料(M239 的 68 文件 / 243 行)修前被改
1 行 → 修后 0 行。判据与扫描器拆成 redact.rs + redact/{shape,tests}.rs。
|
2026-11-11 10:15:48 +08:00 |
|