本轮对全部 26 个 open issue 逐条落到当前代码做了核验(基线 main@80291ca / v2026.8.8.4,xlings 2026.8.9.2,xim-pkgindex c0aded29,mcpp-index b86fc7c)。
本 issue 是那次核验的汇总与收口:它接住所有被关闭 issue 的残留项,并登记核验中新发现、目前无处安放的缺陷。
A. 本轮关闭的 issue,以及残留项落在哪
| 关闭 |
理由 |
残留 → 去处 |
| #289 |
三条核心主张全部被 PR #378(fdad165)消除 |
两条新缺口 → 本 issue C-5 / C-6 |
| #371 |
核心前提被证伪;验收标准由 PR #387 承接 |
AC1/AC5 的取舍决定 → 本 issue D-1 |
| #382 |
主症状已由 xim-pkgindex #563/#565 修复 |
op="set" 语义 → 本 issue C-7 |
| #313 |
7 条建议:3 条已交付,其余各自归位 |
建议 3/7 → 本 issue D-2;建议 2 → #276;建议 5 → #260 |
| #177 |
与 #144/#276 是同一缺口的三个面 |
合并进 #276 |
| #144 |
同上 |
合并进 #276 |
合并后的承载点是 #276 —— 「mcpp 缺少一个申报『外部已经存在之物』的入口」这条 roadmap,涵盖外部工具链、外部 sysroot、外部第三方库三轴。
B. 新发现的缺陷 —— 核验中撞出来、当前没有任何 issue 覆盖
以下每条都在 main@80291ca 上复核过,行号为实读。
C-1 🔴 P0 · fast path 命中时 compile_commands.json 永不重建
src/build/execute.cppm:757-764 的 try_fast_build 新鲜度门只比对 build.ninja 的 mtime 与 mcpp.toml/源码,不检查任何产物是否存在;命中后 :768-774 直接 run_ninja_fast + return 0,NinjaBackend::build() 整个不执行,而 write_compile_commands 全仓唯一调用点就在那里(src/build/ninja_backend.cppm:1549)。
$ mcpp new demo && cd demo && mcpp build # 成功,CDB 生成
$ rm compile_commands.json
$ mcpp build ; echo $? # "Finished dev in 0.00s" ; 0
$ ls compile_commands.json # No such file ← 此后每次都如此
触发条件很日常:mcpp new 写的 .gitignore 只有 target/ 和 .mcpp/,CDB 未被忽略 ⇒ git clean -fd 删掉它、保留 target/ ⇒ 下一次 mcpp build 命中 fast path ⇒ CDB 再也回不来。
而 mcpp-vscode(src/extension.ts:105,128-136)的流程是「CDB 不存在 → 提示 Run mcpp build → 用户点 Build → 再检查」。这个组合让扩展陷入死循环:构建每次都成功,提示每次都还在。
同一形状在 try_fast_run(execute.cppm:788 起)也在。
真正该问的不是「补一次产物存在性检查」,而是:fast path 的新鲜度门缺的是一整类判据 —— 输入新鲜 ≠ 输出齐全。
C-2 🔴 P1 · 模块扫描器的 strip 顺序本身是一个假阴性
src/modgraph/scanner.cppm:588-589 先 strip_raw_strings 后 strip_line_comment。于是只要源文件的 // 注释里出现一个未闭合的 R"((写文档解释 raw-string、贴一段带 raw-string 的示例,都会产生),in_raw 就悬挂到文件末尾,后面所有 import / export module 全部消失。
实测两例:真的 export module mylib; 被吞 ⇒ 消费者收到 module 'mylib' imported but not provided(一个明明存在的模块);import totally.real.module; 被吞 ⇒ 一路到 g++ 才报 failed to read compiled module。
这比 #373 报告的块注释问题更重:那个是假阳性(多报一条 warning),这个是假阴性(真声明消失)。两者同源,必须一起修。
修复本身还有一个必踩的地雷:普通 "..." 字面量里的 /* —— mcpp 自己的树里就有,src/build/distribution.cppm:417 的 "/* Generated by mcpp. …",配对 " */\n" 在 :426。一个不认识字符串字面量的块注释 pass 会把 417–426 整段吞掉。安全阀:普通字符串状态必须行内即抛、绝不跨行。
C-3 🟠 P1 · --no-color 在 TTY 下是空操作
- src/cli.cppm:107 — --no-color → mcpp::ui::disable_color()(argv 预扫描,早于任何输出)
- src/ui.cppm:239 — void disable_color() { g_color = false; } ← 只改 g_color,不置 g_inited
- src/ui.cppm:233-237 — void init() { if (g_inited) return; g_color = detect_color(); g_inited = true; }
- 每个输出入口(status/info/finished/warning/error/diagnostic)都先调 init()
时序:--no-color 把 g_color 置 false → 第一条 ui::status() 调 init(),发现 g_inited 仍是 false → 重新 detect_color() → 在 TTY 上又变回 true。
一直没被发现的原因:CI 与所有 e2e 都通过管道捕获输出,detect_color() 在非 TTY 下本来就返回 false ⇒ 「无色」这个结果是对的,但原因是错的,测试测不出这个开关有没有生效。MCPP_NO_COLOR / NO_COLOR 走 detect_color() 内部,不受影响;只有 --no-color 这一条路径坏了。
修法一行:{ g_color = false; g_inited = true; }。测试必须直连 ui:: 断言输出不含 \033——靠现有 e2e 是假绿。
C-4 🟠 P1 · [modules].exports 校验对任何设了 namespace 的包是静默空操作
src/modgraph/validate.cppm:98 比的是 u.packageName == manifest.package.name,而 src/modgraph/scanner.cppm:789-791 给 u.packageName 填的是限定名:
const std::string qualifiedName =
manifest.package.namespace_.empty()
? manifest.package.name
: manifest.package.namespace_ + "." + manifest.package.name;
于是 namespace="acme" / name="lib1" 时 "acme.lib1" != "lib1",actual 恒为空集,整个 exports 校验(含 strict)一条都不触发。实测:
- [package] name="lib1", namespace="acme" + exports = ["totally.not.this"] + export module acme.lib1; → mcpp build 通过,零诊断
- 删掉 namespace = "acme" 这一行,同一工程立刻报 module 'acme.lib1' is exported by code but not listed in [modules].exports
成因可追:scanner.cppm:786-788 的注释说这个限定名是为了让「模块名必须以包名为前缀」的检查工作,而那条检查在 0b8b81b 里被删了,留下的 :98 就悬空了。
⚠️ 修 #374 之前必须先看懂这条:如果 #374 的新谓词照抄 u.packageName == manifest.package.name,就会对所有 namespaced 包判成「没有模块接口」,把 warning 全局静默 —— 症状看起来像「issue 修好了」,实际是又踩了同一个坑。
相关:限定包名至少三处独立推导且算法不一致(scanner.cppm:789-791 无守卫 / plan.cppm:264-271 多一个 starts_with 守卫 / #374 将新增的第三处)。namespace="acme" + name="acme.lib1" 时 scanner 产出 acme.acme.lib1 而 plan 产出 acme.lib1,plan.cppm:1054 的 devDepPackages.contains(cu.packageName) 会静默失配。
C-5 🟠 P1 · Windows 上 xlings 版本门整套空转(#289 的残留之一)
src/fallback/xlings_binary.cppm:160 把 2>/dev/null 硬编码进一条经 cmd.exe /c 执行的命令 —— cmd 解析不了这个重定向,命令不执行,函数返回空串。空串在两个消费点都被当成「读不出来,不作判断」:acquire_xlings_binary 退回旧的 early return,doctor 退化成一条 warn。
也就是说 #289 点名的那个受害平台(Windows CI),恰恰是 PR #378 的修复不生效的平台。
仓库里已有 mcpp::platform::null_redirect(src/platform/common.cppm:37-41)就是为这件事准备的,这里没用。import mcpp.platform 在 xlings_binary.cppm:16 已在 ⇒ 一行改动。
守卫必须是「在本平台真 spawn 一次探针」,而不是再加一条 version_is_older 的字符串用例 —— 后者在 Linux 上照样绿。
C-6 🟠 P1 · 发布包自带的 xlings 没接进候选链(#289 的残留之二)
candidate_source_version()(src/fallback/xlings_binary.cppm:214-224)只看 MCPP_VENDORED_XLINGS 和 which xlings。而:
- release 顶层 wrapper 是 .github/workflows/release.yml:351-354 的 exec "$(dirname "$0")/bin/mcpp" "$@",不导出 MCPP_VENDORED_XLINGS(只有 AUR 三个包导出);
- src/home.cppm:92-99 把 data/xpkgs/ 下的 mcpp 显式取消自包含资格 ⇒ xlings install mcpp 装出来的 mcpp,MCPP_HOME 回落 ~/.mcpp,自带副本永远在视野外。
而正是「机器上的 xlings 很旧」这个前提,决定了 which xlings 在最需要自愈的场景里也是旧的 ⇒ 走进 candidate.empty() || !version_is_older(...) 分支,打一行 Note 就放弃 —— 尽管一份与 pin 逐字一致的 xlings 就躺在 <install>/registry/bin/xlings(本机实测 2026.8.8.4 包内为 xlings 2026.8.8.1)。
顺带:src/doctor.cppm:411-417 的「It is replaced automatically on the next mcpp self init」在这个分支里是错的。
C-7 🟠 P2 · mcpp 把 subos 的 op="set" 读成无条件覆盖(#382 的残留)
src/xlings/subos_info.cppm:256-281 取了 ambient 值但对 set 完全不用:
auto amb = ambient_of(d.var);
if (d.op == "set") { … out.emplace_back(d.var, value); continue; } // amb 被丢弃
而 xlings 的四个后端全是条件赋值:
| 后端 |
代码 |
位置 |
| POSIX |
: "${VAR:=value}"; export VAR; |
xlings/src/core/subos.cppm:1012 |
| fish |
if not set -q VAR; set -gx VAR …; end |
:1120 |
| pwsh |
if (-not $env:VAR) { … } |
:1143 |
| in-process |
else if (existing.empty()) { set_env_variable(...) } |
:1166 |
后果:同一个 subos,xlings subos use 进去用户的 export 保留,mcpp run 进去被覆盖 —— 正是 subos_info.cppm:262-272 那段注释自己声称要避免的分歧,而分歧是 mcpp 造成的。
注释给的两条「不修」理由,一条错一条反过来支持修:(a)「subos 需要 set 表达用户不得覆盖」在当前生态零实例 —— 整个 xim-pkgindex 里 op="set" 只有 pkgs/w/wsl-gl-host-link.lua 一处,其余全是 prepend,而唯一那处恰恰是用户需要能覆盖的那个;(b)「消费方不得给 op 加第二种含义」是对的,但它指向的是现在的代码。
修法:改成 xlings 的两段式 —— 先纯 manifest 折叠(set 在则 set 赢、prepends 丢弃;否则按 provider 逆序去重拼接),再对 ambient 应用(prepend 接在前;set ambient 存在就整个让位)。判据用「有没有」而不是「空不空」(两个消费点的 lambda 正好是 std::getenv 的 optional,execute.cppm:367-374 / :888-896),对齐 xlings 的 utils::env_is_set。
⚠️ 不要照抄 73ab179 那版的 amb && !amb->empty() —— 那对应 xlings 2026.8.6.1 的旧语义。而 subos_info.cppm:287/289 的 prepend 分支今天正是这个旧写法,顺带一起收敛。
⚠️ 这个误读已经来回过三次(73ab179 两半都修 → 1cc1052 撤回 set → c1e9360 合入只剩 prepend)。修的时候请把上表四个行号写进注释和测试 —— 光写「set 是默认值」没用,上一版就是这么写的。
C-8 · 两个「看起来在做事」的空转循环
src/toolchain/lifecycle.cppm:559-564 与 src/build/prepare.cppm:1668-1670 的 sysroot 预装循环传的是无版本的 "xim:glibc" / "xim:linux-headers",而 src/pm/package_fetcher.cppm:930-935 一进门就是:
if (parsed.packageName.empty() || parsed.version.empty())
return std::unexpected(CallError{"invalid xpkg target ... expected `<name>@<version>`"});
⇒ 自引入至今一次都没装成过任何东西。一处 (void) 丢弃返回值,一处只进 log::debug,所以从来没人看见。glibc 实际是靠 gcc 的 xim 依赖顺带装上的,症状被遮住了。
建议直接删除(mcpp 没有绕过 xlings 的依赖解析,即使能跑也是重复劳动;而在 mcpp 里钉死 glibc 版本常量会立刻和上游的 latest 2.39→2.44 漂移打架)。
C-9 · 其余已登记、优先级较低的发现
展开(11 条)
| 归属 |
发现 |
| #393 |
MCPP_OUT_DIR/MCPP_MANIFEST_DIR 用 .string()(build_program.cppm:230-231),而 a.command 从不过归一化层(directives.cppm:668-670 只绝对化 inputs/outputs)⇒ 一个从不写 ${mcpp.…} 的 build.mcpp,照样在 Windows 上把混合分隔符塞进命令行 |
| #393 |
含 ${mcpp. 的 role="source" 输出被 prepare.cppm:2969 整体排除在源集外 ⇒ 产物永远不会被编译且零诊断,而 docs/07-build-mcpp.md:222-224 承诺「畸形 action 是硬错误,绝不静默跳过」 |
| #386 |
prepare.cppm:3210 的默认 glob(4 项)已与 toml.cppm:1476(7 项)漂移 ⇒ 走多版本 mangling staging 的包今天就漏掉汇编源 |
| #380 |
template.cppm:142-144 三遍独立全文扫描 ⇒ 占位符串扰(实测 projectName = "{{self.name}}" 时 name = "{{project.name}}" 渲染成 name = "imgui",静默错值);template.cppm:156 的 ec 接住从不判读 ⇒ 模板目录读不了时零迭代、返回成功、打印 "Created";mcpp new foo.bar rc=0 且能构建,但违反 docs/spec/package-identity.md §3.2 ⇒ 生成本地能编、一发布就不合规的 manifest |
| #304 |
flags.cppm:884-885 把 runtime_dirs 拼在 user_ldflags 之前 ⇒ 用户显式写的 -Lvendor 也被压过;macOS 上 [runtime] library_dirs 完全是死的(flags.cppm:873-874 既不发 -L 也不发 -rpath),而 execute.cppm:1370-1377 还在警告「dependencies must be reachable through the binary's rpath」 |
| #370 |
mcpp add <pkg>@'*' 静默写进一个永不可解析的依赖(commands.cppm:55 对任何范围直接早退) |
| #290 |
target_cfg 里写 cfg(version=…) 能过解析但 match_kv(prepare.cppm:152-158)不认 ⇒ 永远不生效且不报错 |
| #256 |
canary tests/e2e/150_...sh 三个洞:control 只 precompile 不 import(:92-95)、ACTUAL 只看退出码(:104,而 22.1.8 上 SIGSEGV 经 driver 映射后退出码是 1 不是 139)、未登记大版本 exit 0(:108-112,实测构造 23.0.0 payload 后打印 no recorded expectation 然后 RC=0 通过) |
| #215 |
cppfly.cppm:161-164 的 gate 循环只比 family 就 break,与 latest_std_canonical(:142-148)语义不一致 ⇒ 给 Clang 加第二行的那一刻它会静默不可达;100_cppfly_reflection.sh:16-19 是会自我关闭的守卫(GCC 17 改名或默认开启 -freflection ⇒ 唯一那条硬路径 e2e 静默停跑) |
| #259 |
ci-linux-e2e.yml:104-166 的 hermetic job 缓存 ~/.mcpp 且带 restore-keys ⇒ 冷装路径跑绿一次后再也不被覆盖;lifecycle.cppm:576-582 安装后只 exists(bin) 不 exec |
| #379 |
xpkg parse --format json 失败契约自相矛盾:坏 descriptor 时 stdout 是合法信封但错误在 data.error 里是人类文本、diagnostics 为 [];另三个失败分支在 --format json 下 stdout 一字不写 ⇒ 客户端把「descriptor 写错了」误判成「这个 mcpp 太老」。另有第四种机器输出拼写 mcpp test --message-format json,契约文档零提及,而它恰恰是 CI 最可能消费的出口 |
C. 从关闭 issue 转移过来的决定项
D-1 · #371 的两块「明确不做」需要留痕
#379 §四已经否掉 ide 独立命名空间、artifact 状态枚举 / phase 状态机 / 快照 ID 体系、NDJSON 事件流。这是一个决定而不是待办,但这个决定从没写在 #371 上。已在 #371 的关闭留言里写明。
#371 的 AC2/AC3/AC4 由 PR #387(feat: generate compile database without building)承接 —— 该 PR 已实现 --configure-only、CDB 原子发布(platform::fs::replace_file,POSIX rename / Windows MoveFileExW)、tests/** 与 dev-dependency 覆盖。当前状态:OPEN / CHANGES_REQUESTED / 2 条红 e2e(e2e 1/2 linux 与 e2e 1/2 windows)。
注意 C-1 与 PR #387 正交但同文件:#387 让 configure-only 绕开 fast path,但没有修 normal build 的 fast path 漏 CDB。两者都改 src/build/execute.cppm,需要排序(建议 #387 先落地)。
D-2 · #313 未交付的两条
- 建议 3(help 一级标题着色):src/cli.cppm:50 的 print_usage()。若要做,色码应只有一个产地(src/ui.cppm 加 heading(),复用 with_color),且必须先修 C-3,否则 mcpp --help --no-color 仍会出色。
- 建议 7(CMake ↔ mcpp 互转译):按维护者结论走 Agent Skill(.agents/skills/ 下今天只有 mcpp-usage / mcpp-contributing / mcpp-release),不做 CMake 解析器。可参考 增设关闭src自动扫描字段 - mcpp.toml 使用问题 #386 报告人正在做的 cmake2mcpp。
D. 保持 open 的 18 条
#396 #393 #392 #386 #380 #379 #374 #373 #370 #304 #293 #290 #284 #283 #276 #259 #256 #215
(另有 #43、#260 两条按约定保留,不在核验范围内。)
核验结论:没有一条是「已修复」或「过时」。逐条的根因、当前代码状态(带 file:line)、修法与回归判据在 .agents/docs/2026-08-09-issue-triage-full-sweep.md。
几条判定要点:
E. 方法与边界
27 个 agent、1594 次工具调用;每条事实主张落到当前 file:line,issue 原文的行号平均漂移两到三个版本,一律重新定位。对判为「可关闭」的结论各派两名独立反驳者(代码事实 + 报告人视角),不确定默认推翻 —— 上表 6 条关闭全部是在残留项有了本 issue 这个去处之后才成立的。
未核项:#290 的 check_llamacpp_snapshot.py 主张(未 checkout mcpplibs/llama.cpp-m);#382 的 GPU 加速路径(无 WSL2 / 无 Intel GPU,mesa 25.0.7.2 的 d3d12/iris 在 payload 里但从未被执行过)。
/cc @Sunrisepeak
A. 本轮关闭的 issue,以及残留项落在哪
合并后的承载点是 #276 —— 「mcpp 缺少一个申报『外部已经存在之物』的入口」这条 roadmap,涵盖外部工具链、外部 sysroot、外部第三方库三轴。
B. 新发现的缺陷 —— 核验中撞出来、当前没有任何 issue 覆盖
以下每条都在 main@80291ca 上复核过,行号为实读。
C-1 🔴 P0 · fast path 命中时 compile_commands.json 永不重建
src/build/execute.cppm:757-764 的 try_fast_build 新鲜度门只比对 build.ninja 的 mtime 与 mcpp.toml/源码,不检查任何产物是否存在;命中后 :768-774 直接 run_ninja_fast + return 0,NinjaBackend::build() 整个不执行,而 write_compile_commands 全仓唯一调用点就在那里(src/build/ninja_backend.cppm:1549)。
触发条件很日常:mcpp new 写的 .gitignore 只有 target/ 和 .mcpp/,CDB 未被忽略 ⇒ git clean -fd 删掉它、保留 target/ ⇒ 下一次 mcpp build 命中 fast path ⇒ CDB 再也回不来。
而 mcpp-vscode(src/extension.ts:105,128-136)的流程是「CDB 不存在 → 提示 Run mcpp build → 用户点 Build → 再检查」。这个组合让扩展陷入死循环:构建每次都成功,提示每次都还在。
同一形状在 try_fast_run(execute.cppm:788 起)也在。
C-2 🔴 P1 · 模块扫描器的 strip 顺序本身是一个假阴性
src/modgraph/scanner.cppm:588-589 先 strip_raw_strings 后 strip_line_comment。于是只要源文件的 // 注释里出现一个未闭合的 R"((写文档解释 raw-string、贴一段带 raw-string 的示例,都会产生),in_raw 就悬挂到文件末尾,后面所有 import / export module 全部消失。
实测两例:真的 export module mylib; 被吞 ⇒ 消费者收到 module 'mylib' imported but not provided(一个明明存在的模块);import totally.real.module; 被吞 ⇒ 一路到 g++ 才报 failed to read compiled module。
这比 #373 报告的块注释问题更重:那个是假阳性(多报一条 warning),这个是假阴性(真声明消失)。两者同源,必须一起修。
修复本身还有一个必踩的地雷:普通 "..." 字面量里的 /* —— mcpp 自己的树里就有,src/build/distribution.cppm:417 的 "/* Generated by mcpp. …",配对 " */\n" 在 :426。一个不认识字符串字面量的块注释 pass 会把 417–426 整段吞掉。安全阀:普通字符串状态必须行内即抛、绝不跨行。
C-3 🟠 P1 · --no-color 在 TTY 下是空操作
时序:--no-color 把 g_color 置 false → 第一条 ui::status() 调 init(),发现 g_inited 仍是 false → 重新 detect_color() → 在 TTY 上又变回 true。
一直没被发现的原因:CI 与所有 e2e 都通过管道捕获输出,detect_color() 在非 TTY 下本来就返回 false ⇒ 「无色」这个结果是对的,但原因是错的,测试测不出这个开关有没有生效。MCPP_NO_COLOR / NO_COLOR 走 detect_color() 内部,不受影响;只有 --no-color 这一条路径坏了。
修法一行:{ g_color = false; g_inited = true; }。测试必须直连 ui:: 断言输出不含 \033——靠现有 e2e 是假绿。
C-4 🟠 P1 · [modules].exports 校验对任何设了 namespace 的包是静默空操作
src/modgraph/validate.cppm:98 比的是 u.packageName == manifest.package.name,而 src/modgraph/scanner.cppm:789-791 给 u.packageName 填的是限定名:
于是 namespace="acme" / name="lib1" 时 "acme.lib1" != "lib1",actual 恒为空集,整个 exports 校验(含 strict)一条都不触发。实测:
成因可追:scanner.cppm:786-788 的注释说这个限定名是为了让「模块名必须以包名为前缀」的检查工作,而那条检查在 0b8b81b 里被删了,留下的 :98 就悬空了。
相关:限定包名至少三处独立推导且算法不一致(scanner.cppm:789-791 无守卫 / plan.cppm:264-271 多一个 starts_with 守卫 / #374 将新增的第三处)。namespace="acme" + name="acme.lib1" 时 scanner 产出 acme.acme.lib1 而 plan 产出 acme.lib1,plan.cppm:1054 的 devDepPackages.contains(cu.packageName) 会静默失配。
C-5 🟠 P1 · Windows 上 xlings 版本门整套空转(#289 的残留之一)
src/fallback/xlings_binary.cppm:160 把 2>/dev/null 硬编码进一条经 cmd.exe /c 执行的命令 —— cmd 解析不了这个重定向,命令不执行,函数返回空串。空串在两个消费点都被当成「读不出来,不作判断」:acquire_xlings_binary 退回旧的 early return,doctor 退化成一条 warn。
也就是说 #289 点名的那个受害平台(Windows CI),恰恰是 PR #378 的修复不生效的平台。
仓库里已有 mcpp::platform::null_redirect(src/platform/common.cppm:37-41)就是为这件事准备的,这里没用。import mcpp.platform 在 xlings_binary.cppm:16 已在 ⇒ 一行改动。
守卫必须是「在本平台真 spawn 一次探针」,而不是再加一条 version_is_older 的字符串用例 —— 后者在 Linux 上照样绿。
C-6 🟠 P1 · 发布包自带的 xlings 没接进候选链(#289 的残留之二)
candidate_source_version()(src/fallback/xlings_binary.cppm:214-224)只看 MCPP_VENDORED_XLINGS 和 which xlings。而:
而正是「机器上的 xlings 很旧」这个前提,决定了 which xlings 在最需要自愈的场景里也是旧的 ⇒ 走进 candidate.empty() || !version_is_older(...) 分支,打一行 Note 就放弃 —— 尽管一份与 pin 逐字一致的 xlings 就躺在 <install>/registry/bin/xlings(本机实测 2026.8.8.4 包内为 xlings 2026.8.8.1)。
顺带:src/doctor.cppm:411-417 的「It is replaced automatically on the next mcpp self init」在这个分支里是错的。
C-7 🟠 P2 · mcpp 把 subos 的 op="set" 读成无条件覆盖(#382 的残留)
src/xlings/subos_info.cppm:256-281 取了 ambient 值但对 set 完全不用:
而 xlings 的四个后端全是条件赋值:
后果:同一个 subos,xlings subos use 进去用户的 export 保留,mcpp run 进去被覆盖 —— 正是 subos_info.cppm:262-272 那段注释自己声称要避免的分歧,而分歧是 mcpp 造成的。
注释给的两条「不修」理由,一条错一条反过来支持修:(a)「subos 需要 set 表达用户不得覆盖」在当前生态零实例 —— 整个 xim-pkgindex 里 op="set" 只有 pkgs/w/wsl-gl-host-link.lua 一处,其余全是 prepend,而唯一那处恰恰是用户需要能覆盖的那个;(b)「消费方不得给 op 加第二种含义」是对的,但它指向的是现在的代码。
修法:改成 xlings 的两段式 —— 先纯 manifest 折叠(set 在则 set 赢、prepends 丢弃;否则按 provider 逆序去重拼接),再对 ambient 应用(prepend 接在前;set ambient 存在就整个让位)。判据用「有没有」而不是「空不空」(两个消费点的 lambda 正好是 std::getenv 的 optional,execute.cppm:367-374 / :888-896),对齐 xlings 的 utils::env_is_set。
C-8 · 两个「看起来在做事」的空转循环
src/toolchain/lifecycle.cppm:559-564 与 src/build/prepare.cppm:1668-1670 的 sysroot 预装循环传的是无版本的 "xim:glibc" / "xim:linux-headers",而 src/pm/package_fetcher.cppm:930-935 一进门就是:
⇒ 自引入至今一次都没装成过任何东西。一处 (void) 丢弃返回值,一处只进 log::debug,所以从来没人看见。glibc 实际是靠 gcc 的 xim 依赖顺带装上的,症状被遮住了。
建议直接删除(mcpp 没有绕过 xlings 的依赖解析,即使能跑也是重复劳动;而在 mcpp 里钉死 glibc 版本常量会立刻和上游的 latest 2.39→2.44 漂移打架)。
C-9 · 其余已登记、优先级较低的发现
展开(11 条)C. 从关闭 issue 转移过来的决定项
D-1 · #371 的两块「明确不做」需要留痕
#379 §四已经否掉 ide 独立命名空间、artifact 状态枚举 / phase 状态机 / 快照 ID 体系、NDJSON 事件流。这是一个决定而不是待办,但这个决定从没写在 #371 上。已在 #371 的关闭留言里写明。
#371 的 AC2/AC3/AC4 由 PR #387(feat: generate compile database without building)承接 —— 该 PR 已实现 --configure-only、CDB 原子发布(platform::fs::replace_file,POSIX rename / Windows MoveFileExW)、tests/** 与 dev-dependency 覆盖。当前状态:OPEN / CHANGES_REQUESTED / 2 条红 e2e(e2e 1/2 linux 与 e2e 1/2 windows)。
D-2 · #313 未交付的两条
D. 保持 open 的 18 条
#396 #393 #392 #386 #380 #379 #374 #373 #370 #304 #293 #290 #284 #283 #276 #259 #256 #215
(另有 #43、#260 两条按约定保留,不在核验范围内。)
核验结论:没有一条是「已修复」或「过时」。逐条的根因、当前代码状态(带 file:line)、修法与回归判据在 .agents/docs/2026-08-09-issue-triage-full-sweep.md。
几条判定要点:
E. 方法与边界
27 个 agent、1594 次工具调用;每条事实主张落到当前 file:line,issue 原文的行号平均漂移两到三个版本,一律重新定位。对判为「可关闭」的结论各派两名独立反驳者(代码事实 + 报告人视角),不确定默认推翻 —— 上表 6 条关闭全部是在残留项有了本 issue 这个去处之后才成立的。
未核项:#290 的 check_llamacpp_snapshot.py 主张(未 checkout mcpplibs/llama.cpp-m);#382 的 GPU 加速路径(无 WSL2 / 无 Intel GPU,mesa 25.0.7.2 的 d3d12/iris 在 payload 里但从未被执行过)。
/cc @Sunrisepeak