# xlings 2026.8.8.2 — 发布说明

**日期**: 2026-08-08
**类型**: 发布记录 (release notes)
**关联**: `2026-08-08-declared-vs-effective-open-defects-design.md`、
`xim-pkgindex#570`、`xim-pkgindex#573`、`xlings#506`

---

## 一句话

**本地索引拿回了它的 `libs/`,`remove` 不再和 `install` 不对称,一个空字符串在四个
后端里终于是同一个意思 —— 以及一条红了 16 次的 CI,前四次「修复」全在追一个从未发生
的编译失败。**

---

## 这一轮真正的发现

### `execve` 的 ENOENT 报的是二进制,不是缺失的 loader

aarch64 交叉构建红了 16 次。期间提交了四个修复:pin mcpp、pin 索引、去掉源码构建
mcpp、去掉载荷缓存。**四个全都没有改变任何东西**,输出一字不差:

```
build.mcpp compiling / build.mcpp running
error: posix_spawnp('.../xpkg@0.0.54/build.mcpp.bin') failed (error 2)
```

四次都是在 runner **之外**推断的。加一个 `if: failure()` 的诊断步骤之后,一轮出结果:

```
build.mcpp.bin	287360 bytes        ← 它一直都在,编译从未失败
interp = /home/xlings/.xlings_data/subos/linux/lib/ld-linux-x86-64.so.2
interp MISSING -> this is the ENOENT. The loader, not the binary.
```

`execve` 在 `PT_INTERP` 不存在时返回 ENOENT,并且**报的是二进制的名字**。所以这条
错误读起来永远像「构建脚本没产出来」。

根因是 **CI 的 bootstrap 客户端 `v0.4.69`** —— 旧版本号体系的产物,比整个
loader/INTERP 系列早几个月,跑不了 `gcc.lua` 里那个「mandatory」的 specs 改写,
于是 gcc 保留了打包机器的路径。换成 `v2026.8.8.1` 后转绿,而且是真绿:交叉构建、
`file` 断言 ARM/static、qemu 跑 `--version`、产物上传、依赖它的原生 aarch64
contracts job 全过。

**mcpp 不背锅**:它为项目目标在构建时生成链接参数,不读 gcc 的 specs;而且该 job
用的已经是最新的 mcpp(`2026.8.8.4`)。唯一吃 specs 的是宿主侧的 `build.mcpp` 编译 ——
恰好就是失败的那一步。

那个 `/home/xlings/.xlings_data/...` 和 2026.8.8.1 发布说明里写的、我们 `ld.so`
编进去的 cache 路径**是同一台打包机器**。同一个根:载荷里带着只在打包机上成立的绝对路径。

### 一个空字符串,四个后端三种错

§7 判定 `op = "set"` 本来就是条件式,#508 关闭,残留只是命名。命名确实误导人,
但底下压着真缺陷:四个后端对「空值算不算已设置」不一致。

| 后端 | `export FOO=` |
|---|---|
| POSIX `${VAR:=v}` | **覆盖** |
| pwsh `-not $env:VAR` | **覆盖** |
| 进程内 `existing.empty()` | **覆盖** |
| fish `set -q` | 保留 |

同一个声明,进 subos 的方式不同环境就不同,而且不出声。已统一为**空值算已设置,
用户的值赢**。

---

## 变更

| 项 | 内容 |
|---|---|
| `--add-xpkg` (D1) | 本地索引 symlink 主索引的 `libs/`,recipe 顶层的 `xim.pkgindex.*` 不再被宽容 stub 吞掉 |
| `remove` (D4, #506) | 「没注册任何 xvm 版本」的包移除成功为 no-op,并执行其 `uninstall()` 钩子 |
| `subos.env` | 空值算已设置;新增 `utils::env_is_set` |
| CI pins | `.xlings.json` mcpp → `2026.8.8.4`;`XIM_PKGINDEX_REF` → `3d800794`;`BOOTSTRAP_XLINGS_VERSION` → `v2026.8.8.1` |
| 文档 | `docs/spec/xlings-json-schema.md` 写明 op 语义;设计文档 §8 |

**测试**:`tests/e2e/subos_env_use_switch_test.sh`(新),`subos_env_declaration_test.sh`
加空值用例 —— **验证过可证伪**,对上一个构建跑会失败。

---

## 已知未完成

- **D5 的验收不成立,而且天真地测会假通过。** `libXtst` + `alsa-lib` 已打包
  (`xim-pkgindex#570`)且闭包自洽,但 JDK 的 `PT_INTERP` 仍是 `/lib64/ld-linux-x86-64.so.2`,
  **loader 还没切**,所以那两个库是**宿主**提供的(`/lib/x86_64-linux-gnu`)。
  在切换之前,任何「AWT 起来了」的验证验的都是宿主。
- **`xim-pkgindex#573`**:`gcc-specs-config` 不幂等 —— rpath 每次前插累积,
  dynamic-linker 只匹配原始字面量因而永不更新,两者漂移后报 `GLIBC_PRIVATE` 符号错误。
- **D4 的后续**:删掉 `xim-pkgindex/.github/scripts/posix-test.sh` 里那条
  `type = "config"` 容忍,要等这个发布成为 CI 装的版本之后。
- **D6**(rust 打包,`xim-pkgindex#569`)本轮不做。
