r1 复审(reviewer-updatee2e-r1,p2-5items,均不挡合并)逐条落地: P2-3(本轮主产出)守门把「E2E 进 CI」这条路堵死了:`scan_enabling_text` 对 workflow 里任何该 feature 字面量判红,于是 `cargo test --release … --features update-test-source --test update_e2e_test` 这种**合法调用**也红,自己登记的 follow-up 无法实现。现在: - `ALLOWED_STEP = {workflow: .github/workflows/ci.yml, name: update-e2e, must_contain: --test update_e2e_test}`;按 `- name:` 把 workflow 切成步骤块再判,文件名 / 步骤名 / 命令行**三者任一不符即红**(job 名不参与判据:改 job 名不该解锁新能力)。 - `ci.yml` **新增一个独立 job** `update-e2e`(windows-latest、timeout 45m、release 档;现有 job 一行未动,纯追加 27 行)。只在 Windows:夹具的「真形状」是裸 `.exe` + `portable-exe`,Linux/macOS 的真实产物是 `.tar.gz` + `portable-zip`(会被 `ensure_self_replaceable` 直接拒),在 ubuntu 上跑绿 是假绿。不降 debug:`debug_assertions`/优化/panic 行为都不同。 - self-test 覆盖 allowlist 六向量(允许 / 换文件 / 换步骤名 / 缺 `--test` / 发布工作流 / ci 内另一步)。 P2-2 守门 8 条绕过:注释剥离改**引号感知**(`echo "#"; cargo …` 不再吃掉真命令);扫描面扩到 `.github/actions/**`、`packaging/**` 递归子目录、`Makefile|justfile|Taskfile` 第二构建入口; `--all-features` 之外补 `cargo hack --feature-powerset|--each-feature|--all`。大小写/下划线变体按 复审结论写进「抓不住」表(cargo feature 名敏感,写错编不过,不算漏检)。 P2-1 GUI 人工验收命令根本跑不起来:`-p unlucky-launcher-iced` 是 bin 名,改成 package 名 `-p unlucky-app-iced`(app-iced / presenter 两处,并写明 `-p` 只认 package)。 P2-5 夹具缺「不给 --dest 就 panic」的结构性保险:`Fixture::run` 加两条断言——`apply` 必带 `--dest`、`--dest` 不许等于本进程 exe 的 canonical 路径。抓得住「以后有人手写 fx.run(&["apply"])」, 抓不住「绕开 Fixture 直接调 unlucky_cli::run」。 P2-4 夹具「真形状」只在 Windows 成立:写进 `tests/support/update_e2e.rs` 文件头(说明 Linux 上跑绿 是假绿、CI 因此只在 Windows),**不改断言**。另按复审 ⑤ 补一行:失败路径三例抓不住回滚/备份 (摘要失败发生在 install_staged 之前),回滚靠正路备份断言 + install_cases。 跨卷替换 bug 按 tower 指令**未动**(另开 mission)。 验证:守门正式扫描 + self-test 绿(14 红 / 3 绿);真仓注入 6 组(基线绿、步骤改名红、缺 --test 红、 搬进 release-build 红、改 --all-features 红、还原绿);E2E debug 6 绿、**release 6 绿**; `cargo clippy -p unlucky-cli --all-targets --features update-test-source -- -D warnings` 干净; `cargo fmt --all -- --check` 干净;全仓 400 行扫描无超限(守门脚本 379 行、ci.yml 317 行)。 ⚠ 未验:CI 里 E2E 真的跑起来这件事本机验不了——只证明 workflow 写对 + 守门放行,首次真实运行要等 推送后看 Actions。
§6 欠账:更新链路的 GUI 端从来没验过,而 M167 发现它根本没法在本机验——`ULL_UPDATE_DIR` 只在 `debug_assertions` 下生效,发布产物却是 release;`apply` 又会替换本进程 exe,于是没人敢跑。 判据换档(tower 定案 B):`cfg!(debug_assertions)` → 编译期 cargo feature `update-test-source` (core 定义,cli/presenter/app-iced 链式转发,**默认关**)。带 feature 的构建里还要两档环境变量 (`ULL_UPDATE_DIR` + `ULL_UPDATE_TEST_SOURCE=1`),只给路径什么都不会发生。官方发布二进制里那段 分支根本编不进来——README「release builds do not contain that path at all」这句因此继续为真。 - core: `job::default_source` 的本地源分支整段 `#[cfg(feature = "update-test-source")]`;判据抽成 纯函数 `test_source_root_from` 并逐条单测(半开一律关、空路径、承认值只认 `1`); `the_test_channel_is_a_compile_time_opt_in` 在**两种构建下各断各的**(带 feature ⇒ 换源; 不带 ⇒ 环境变量设满也走 GitHub)。 - cli: `update check/apply` 的 `--json` 增加 `test_source` 字段(判据派生自实际用了哪个源,不是回读 环境变量),人读输出多一行点名;`help.rs` 的 `ULL_UPDATE_DIR` 说明按新判据重写。 - 验收:`crates/cli/tests/update_e2e_test.rs` + `update_e2e_tamper_test.rs` + 共用夹具 `tests/support/update_e2e.rs`。临时数据根 + 本地源 + 真形状清单(与 `manifest.golden.json` 同 字段)+ 真 PE 产物(现场构建的 ull.exe 前 1 MiB,理由写在 `PAYLOAD_LIMIT`)。断言覆盖:发现 新版本(source=file:… / test_source=true / size / notes)、下载 → 双检 → 就位(dest 字节、旧字节 进备份、暂存无残留)、重复 apply 的幂等终态、篡改 sha256(清单撒谎 / 包体被换)与截断都必须 拒绝且不动现有安装、修复后能重装。**永远带 `--dest`**:不带会真的改名换掉测试二进制。 - 守门:`packaging/check_test_source_off.py`(stdlib-only)扫 `.github/workflows/**`、 `packaging/*.py|*.sh` 与各 `Cargo.toml` 的 `default`,任何一处打开 feature(或 `--all-features`) 就红;`--self-test` 注入 5 条真实回归证明每条都会红。CI 挂在 release-guard.yml 两步。 验证(本机):core update 单测 80 绿 / 带 feature 6 绿;E2E debug 6 绿; `cargo test --release --features update-test-source` 6 绿(tower 要求 release 档验收); `cargo test -p unlucky-cli -p unlucky-presenter` 全绿;守门 self-test + 正式扫描绿(真注入后红)。 已知没验:真 GitHub Release 下载、GUI 点击与重启替换(人工);跨卷替换(另开 finding)。
Rust 原生单文件 Minecraft 启动器:CLI 完整可用(install/launch/accounts/java/diagnose/agent), MCP server 接入外部 Agent 工具,GUI 开发中。