From db28e77c021e2493421199ae41279a75a224bb17 Mon Sep 17 00:00:00 2001 From: sunrisepeak Date: Thu, 17 Sep 2026 21:14:16 +0800 Subject: [PATCH] docs: the record of the 0.13 wave and the sandbox verification it ran What was released, where the execution departed from the design and why, what review changed, the verification (CI, a clean xlings sandbox over the CN mirror, the mcpp-index measurement), and the limits that remain open. --- ...026-09-17-openkal-0.13-ecosystem-record.md | 89 ++++++++ .../docs/2026-09-17-openkal-0.13-verify.sh | 203 ++++++++++++++++++ 2 files changed, 292 insertions(+) create mode 100644 .agents/docs/2026-09-17-openkal-0.13-ecosystem-record.md create mode 100644 .agents/docs/2026-09-17-openkal-0.13-verify.sh diff --git a/.agents/docs/2026-09-17-openkal-0.13-ecosystem-record.md b/.agents/docs/2026-09-17-openkal-0.13-ecosystem-record.md new file mode 100644 index 0000000..2351718 --- /dev/null +++ b/.agents/docs/2026-09-17-openkal-0.13-ecosystem-record.md @@ -0,0 +1,89 @@ +# openkal 0.13 生态执行记录 + +- 日期:2026-09-17 +- 依据:`2026-09-17-openkal-ecosystem-cross-repo-design.md`、`2026-09-17-openkal-0.13-ecosystem-execution-plan.md` +- 验证脚本:`2026-09-17-openkal-0.13-verify.sh` + +## 1. 发布 + +每个版本都是 GitHub 标签归档,并以相同字节镜像到 GitCode `mcpp-res/`,两端 sha256 已核对。 + +| 包 | 版本 | PR | sha256 | +| --- | --- | --- | --- | +| openkal | 0.13.0 | openkal#33 | `d8ef2871553d9fa3d90df2637c7dbd8410140234019963f17652bfaacd5c4ecf` | +| openkal-linux | 0.13.0 | openkal-linux#27 | `c794b08721297faefcb26eda238e32b350e3b94bc863a5c43b75c223dba482b0` | +| openkal-macos | 0.10.0 | openkal-macos#21 | `282d0aff75e803bfacc39af3f777933c557197f0238c9b7d2d0ea7b22c9bf439` | +| openkal-windows | 0.8.0 | openkal-windows#24 | `82151a7896007c21428f5c4162d72d131a31c71a3d9bb607d5188eee9e424b37` | +| openkal-emscripten | 0.2.0 | openkal-emscripten#2 | `3f2f00168888a0ff0371226ef351b42c0de7316a4223a8f6393dbb21cd5ddd6e` | +| openkal-opensbi | 0.7.0 | openkal-opensbi#16 | `4c1fa90f51b377aa30ff053ac6b814ab2eded41d4ccdbb8cfc438fa50fbbfb7e` | +| openkal-uefi | 0.7.0 | openkal-uefi#13 | `fa16ad19d13dae8509d90ec2fd3714503701c27f323233bdbd2ba32ca3653068` | +| std-freestanding-alloc-kal | 0.1.6 | std-freestanding-alloc-kal#6 | `c4d84ab4d9a4ed15ba440bfb1f97eda1c5ec82d2eae4ddfa6febf88e665252b6` | +| openkal-musl | 0.14.0 | openkal-musl#35 | `91e747f495e041846c48522a3a5a2061ce6fd8f81e4b4e60f54cfaec4358bd66` | +| openkal-llvm-runtime | 0.10.0 | openkal-llvm-runtime#23 | `3fbb5dcee3c62c74a07e76a7486d5e76d49af7c9ee5806ae4ee0545dfd47dd0e` | +| tinyhttps | 0.3.1 | tinyhttps#18 | `ba998b6053d4d662b119da308b8274058f7d9f7075c02ae0e87373a350f49ea5` | +| mcpp | 2026.9.17.3 | mcpp#664(#662)、mcpp#665 | 由 release.yml 发布;xim-pkgindex#857 | + +mcpp-index 分两次合并,原因是 openkal-emscripten 与 std-freestanding-alloc-kal 的 CI 要从索引解析 openkal 0.13.0: +- #437:已发布的 openkal 家族、tinyhttps、兼容性测量设施、zlib/mbedtls 适配; +- #438:其余两个包、mcpp 固定版本、libarchive 在 macOS 上的适配、测量结果。 + +## 2. 与设计稿的差异 + +| 项 | 设计 | 实际 | 原因 | +| --- | --- | --- | --- | +| 错误值名 | `kal_err_not_executable` | `kal_err_not_program` | 前者会被读成权限位(openkal-musl 09-04 分析 §3.2) | +| `kal_node_info` | 追加 `executable` | 追加 `executable` 与 `reserved`,大小 56 | 在把 64 位字段按 4 字节对齐的目标上保持 8 的倍数 | +| D4(GCC) | 在模型中拒绝 | 不改命令行,给出一次 degraded 提示 | 拒绝会发生在目标侧报告打印之前,违反"编译之前先报告"的约定(mcpp e2e 268/282/303) | +| M5 | 设计评审 | feature-deps 已经是私有的,补了 e2e 与文档 | 实测已满足 | +| M6 | 不立项 | 未立项 | zlib 在两种读法下 `z_off_t` 一致,由测试成员断言 | +| compat.mbedtls | 未列出 | `-U_WIN32 -D__unix__`,仅作用于包内单元 | tinyhttps 在 musl-Windows 上经由 mbedtls 引入 `windows.h` | +| compat.libarchive | 评估 | macOS 适配(`-U__APPLE__`);Windows 不适配 | Windows 目标的宽字符字面量是 16 位,musl 的 `wchar_t` 是 32 位,openkal-musl 有意拒绝这种混用 | +| #28.2 零句柄 | 可选 | 不做,SPEC §11 条目 19 记录 | 规范允许但不要求;musl 已显式拒绝 | + +## 3. 审查后的修正 + +- openkal-musl:修改目录执行位的 `chmod` 由 `EISDIR` 改为 `ENOSYS`;`stat` 与 `access(X_OK)` 对未回答位置的两种读法写入注释与 README。 +- openkal-macos:系统调用探针中 `select` 一行不传超时参数,在 macos-15-intel 上会永久等待,改为 `nfds=-1`。 +- openkal-windows:README 说明连接的 stream 跨 spawn 时是 overlapped 句柄。 +- mcpp-index:兼容性 workflow 只有提出结果的 job 有写权限。 +- 多个仓库 README 的安装片段版本与发布版本对齐。 + +## 4. 验证 + +**CI。** 每个 PR 合并前 CI 全绿。合并后 mcpp main 上 ci-macos 的 xcode-27 任务变红,原因是 runner 镜像换成 20260912,其 Command Line Tools SDK 的 `.tbd` 含 `arm64e.x1`,而 lld 22.1.8 无法解析(上游 llvm-project#222721)。该任务改用 mcpp 自己选择的 SDK(mcpp#665)后转绿。 + +**沙箱。** `xlings subos use v013 --sandbox`,CN 镜像,mcpp 2026.9.17.3,全部从已发布的索引解析: + +``` +ok: mcpp 2026.9.17.3 +ok: xlings mirror is CN +ok: set and read the executable record; a text file is kal_err_not_program +ok: chmod of the execute bits round-trips through stat and access, a partial mode is ENOSYS, posix_spawn is ENOEXEC +ok: linux: zlib and tinyhttps build above openkal, z_off_t agrees +ok: windows: zlib and tinyhttps cross-build above openkal +ok: the search list of a compat.zlib unit names no host directory +0 assertion(s) failed +``` + +**兼容性测量。** 宿主装有 mingw 头文件时,mcpp 2026.9.17.2 下 Windows 行大多以宿主 `corecrt.h` 失败,说明对照有效。mcpp 2026.9.17.3、openkal-llvm-runtime 0.10.0 下(run 35222831479,30 个成员): + +| 目标 | runs | fails | 失败原因 | +| --- | --- | --- | --- | +| x86_64-linux-gnu | 27 | 3 | curl、cmp-module(asio) 需要 Linux 内核头文件或 epoll;expat 调用 musl 未声明的 `arc4random_buf` | +| x86_64-windows-gnu(Wine) | 15 | 15 | 11 个是包在 `_WIN32` 下 include 平台头文件;eigen、fmt 两个是 clang 自带头文件按 MinGW 假定 C 库(`__mingw_aligned_malloc`、`#include_next `);archive、curl 两个卡在依赖链(xz 的 `_mingw.h`) | + +测量过程中发现并修正:`compat.py` 复制出的成员不继承 workspace 的 `[indices]`,原本测的是已发布的描述文件而不是 PR 中的描述文件。 + +## 5. 未关闭的限制 + +| 限制 | 所在层 | 状态 | +| --- | --- | --- | +| Windows 上主机名不解析(没有 `/etc/resolv.conf`,`EAI_AGAIN`) | openkal-musl | 写入限制表;规范不定义名字解析(§3.4、§11.5) | +| 纯 C 程序直接链接 openkal-musl,目标 `x86_64-windows-gnu` 时缺编译器 builtins | openkal-musl / 构建 | 写入限制表 | +| Windows 上宽字符字面量与 musl `wchar_t` 宽度不同 | openkal-musl | 有意拒绝(`musl/PATCHES.md`) | +| 平台库自行创建的线程回调进 C 库代码时没有 C 库状态(R2) | openkal-musl | 仅文档约束 | +| 事件循环依赖 `epoll`(cmp-module 等) | 按设计 | 测为失败,不推动 `openkal.event` | +| xz 去掉 `_WIN32` 后在 musl-Windows 上解码失败 | compat.xz | 不适配,测为失败 | +| xlings LLVM 包的默认 sysroot 在 macOS 27 上无法被 lld 22.1.8 链接 | xlings LLVM 包 | 等待打包带 llvm-project#222721 的 LLVM | +| 网络错误值在三个实现间不一致 | openkal | §5.2 的准入标准下暂不增加(缺少测量) | +| clang 自带头文件(`mm_malloc.h`、`intrin.h`)在 windows-gnu 三元组下假定 MinGW C 库 | openkal-musl 与编译器之间 | 待跟进:影响任何 include SSE/intrin 头文件的包,不是某一个包的问题 | diff --git a/.agents/docs/2026-09-17-openkal-0.13-verify.sh b/.agents/docs/2026-09-17-openkal-0.13-verify.sh new file mode 100644 index 0000000..4d01734 --- /dev/null +++ b/.agents/docs/2026-09-17-openkal-0.13-verify.sh @@ -0,0 +1,203 @@ +#!/usr/bin/env bash +# Ecosystem verification for the openkal 0.13 wave, resolved from the published +# index only. +# +# B64=$(base64 -w0 ) +# xlings subos new v013 && xlings subos use v013 --sandbox --cmd \ +# "echo $B64 | base64 -d > /tmp/v.sh && MCPP_VERIFY_VERSION= bash /tmp/v.sh" +# +# The sandbox has an empty $HOME and shares the xlings data directory, so mcpp +# is addressed by its store path. Every criterion reads back what it selected: +# a version, a result code, a search path. A step that cannot run here says so +# and is counted as not run, never as passed. +set -u + +VER="${MCPP_VERIFY_VERSION:?set MCPP_VERIFY_VERSION}" +STORE="${MCPP_VERIFY_BIN:-$HOME/.xlings/data/xpkgs/xim-x-mcpp/$VER/bin/mcpp}" +XL="${XLINGS_BIN:-$(command -v xlings)}" +RUNTIME="${OPENKAL_RUNTIME:-0.10.0}" +MUSL="${OPENKAL_MUSL:-0.14.0}" +SPEC="${OPENKAL_SPEC:-0.13.0}" +LINUX="${OPENKAL_LINUX:-0.13.0}" +TINYHTTPS="${TINYHTTPS_VERSION:-0.3.1}" + +fails=0; skipped="" +fail() { printf 'ASSERT-FAIL: %s\n' "$1"; fails=$((fails + 1)); } +ok() { printf 'ok: %s\n' "$1"; } +section() { printf '\n== %s ==\n' "$1"; } +skip() { printf 'NOT RUN: %s\n' "$1"; skipped="$skipped + - $1"; } + +work=$(mktemp -d) +trap 'rm -rf "$work"' EXIT + +# -- A. identity and mirror --------------------------------------------------- +section "A. identity and mirror" +got=$("$STORE" --version 2>&1 | head -1) +[ "$got" = "mcpp $VER" ] && ok "$got from $STORE" || fail "version is '$got' at $STORE" +"$STORE" self config --mirror CN >/dev/null 2>&1 || true +"$XL" config --mirror CN >/dev/null 2>&1 || true +xm=$(python3 -c "import json,os;print(json.load(open(os.path.expanduser('~/.xlings/.xlings.json'))).get('mirror',''))" 2>/dev/null) +[ "$xm" = "CN" ] && ok "xlings mirror is CN" || fail "xlings mirror is '$xm'" +"$STORE" index update >/dev/null 2>&1 || true + +# -- B. openkal itself: the executable record and kal_err_not_program --------- +section "B. openkal $SPEC over openkal-linux $LINUX" +mkdir -p "$work/spec/src" +cat > "$work/spec/mcpp.toml" < "$work/spec/src/main.cpp" <<'CPP' +import openkal.types; +import openkal.fs; +import openkal.stream; +import openkal.process; +import openkal.abort; +int main() { + const char* n = "v.txt"; const kal_uintptr l = 5; + kal_file f{}; + kal::fs::open_file(kal::fs::working(), n, l, + kal::fs::open::write | kal::fs::open::create | kal::fs::open::truncate, &f); + kal_stream_write(kal_fs_stream(f), "text\n", 5); + kal_fs_close_file(f); + int code = 0; + if (kal_fs_set_executable_at(kal::fs::working(), n, l, 1) != kal_ok) code |= 1; + kal_node_info i = kal::fs::info_for_caller(); + kal_fs_info(kal::fs::working(), n, l, 0, kal::fs::field::executable, &i); + if ((i.present & kal::fs::field::executable) == 0 || i.executable != 1) code |= 2; + kal_process p{}; + const char* argv[1] = { n }; const kal_uintptr lens[1] = { l }; + const kal_spawn how{ kal::fs::working(), kal::fs::working(), nullptr, nullptr, 0, 0 }; + if (kal_process_spawn(&how, n, l, argv, lens, 1, nullptr, nullptr, 0, nullptr, &p) + != kal_err_not_program) code |= 4; + kal_fs_remove(kal::fs::working(), n, l); + return code; +} +CPP +out=$(cd "$work/spec" && "$STORE" run 2>&1); rc=$? +if [ $rc -eq 0 ]; then ok "set and read the executable record; a text file is kal_err_not_program" +else fail "openkal $SPEC probe exited $rc"; printf '%s\n' "$out" | tail -8; fi + +# -- C. openkal-musl: chmod of the execute bits, stat, ENOEXEC ---------------- +section "C. openkal-musl $MUSL (C)" +mkdir -p "$work/musl/src" +cat > "$work/musl/mcpp.toml" < "$work/musl/src/main.c" <<'C' +#include +#include +#include +#include +#include +#include +extern char **environ; +int main(void) { + int code = 0; + int fd = open("m.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644); + write(fd, "text\n", 5); close(fd); + /* The port reports one read, one write and one execute property for all + * classes, so the reachable modes are 0666 and 0777 for a writable file. */ + struct stat st; stat("m.txt", &st); + mode_t m = st.st_mode & 0777; + if (chmod("m.txt", m | ((m & 0444) >> 2)) != 0) code |= 1; + stat("m.txt", &st); + if ((st.st_mode & 0111) != ((m & 0444) >> 2)) code |= 2; + if (access("m.txt", X_OK) != 0) code |= 4; + errno = 0; + if (chmod("m.txt", 0700) != -1 || errno != ENOSYS) code |= 8; + pid_t pid; char *av[] = { "m.txt", 0 }; + if (posix_spawn(&pid, "m.txt", 0, 0, av, environ) != ENOEXEC) code |= 16; + unlink("m.txt"); + printf("code=%d\n", code); + return code; +} +C +out=$(cd "$work/musl" && "$STORE" run 2>&1); rc=$? +if [ $rc -eq 0 ]; then ok "chmod of the execute bits round-trips through stat and access, a partial mode is ENOSYS, posix_spawn is ENOEXEC" +else fail "openkal-musl $MUSL probe exited $rc"; printf '%s\n' "$out" | tail -8; fi + +# -- D. compat.zlib and tinyhttps on openkal, Linux and Windows --------------- +section "D. packages from the index on openkal-llvm-runtime $RUNTIME" +mkdir -p "$work/pk/tests" +cat > "$work/pk/mcpp.toml" < "$work/pk/tests/zlib.cpp" <<'CPP' +#include +import std; +import mcpplibs.tinyhttps; +int main() { + const unsigned long lib = ((zlibCompileFlags() >> 6) & 3u) == 2 ? 8 + : ((zlibCompileFlags() >> 6) & 3u) == 1 ? 4 : 0; + std::println("z_off_t library={} consumer={}", lib, sizeof(z_off_t)); + return lib == sizeof(z_off_t) ? 0 : 1; +} +CPP +out=$(cd "$work/pk" && "$STORE" test 2>&1); rc=$? +if [ $rc -eq 0 ]; then ok "linux: zlib and tinyhttps build above openkal, z_off_t agrees" +else fail "linux: package probe exited $rc"; printf '%s\n' "$out" | tail -10; fi +out=$(cd "$work/pk" && "$STORE" build --target x86_64-windows-gnu 2>&1); rc=$? +if [ $rc -eq 0 ]; then ok "windows: zlib and tinyhttps cross-build above openkal" +else fail "windows: cross build exited $rc"; printf '%s\n' "$out" | tail -10; fi + +# The closure: the compiler's own search list for a unit of that build names no +# host directory. Read from the build database, as the compiler receives it. +db=$(cd "$work/pk" && "$STORE" emit build-database --spec compile-commands --target x86_64-windows-gnu 2>/dev/null) +cmd=$(printf '%s' "$db" | python3 -c " +import json,sys,shlex +try: d=json.load(sys.stdin) +except Exception: sys.exit(0) +for u in d: + f=u.get('file','') + a=u.get('arguments') or shlex.split(u.get('command','')) + if 'compat-x-zlib' in f and f.endswith('.c') and a: + out=[]; skip=False + for x in a: + if skip: skip=False; continue + if x=='-o': skip=True; continue + if x=='-c': continue + out.append(x) + print(shlex.join(out[:1]+['-v','-fsyntax-only']+out[1:])); break +" 2>/dev/null) +if [ -z "$cmd" ]; then + skip "D: no compat.zlib C unit in the build database" +else + search=$(cd "$work/pk" && eval "$cmd" 2>&1 | sed -n '/search starts here/,/End of search list/p') + if printf '%s' "$search" | grep -qE '^ /usr/'; then + fail "a host directory is on the search list of a compat.zlib unit"; printf '%s\n' "$search" + elif [ -z "$search" ]; then + skip "D: the compiler printed no search list" + else ok "the search list of a compat.zlib unit names no host directory"; fi +fi + +section "summary" +printf '%d assertion(s) failed\n' "$fails" +[ -n "$skipped" ] && printf 'not run:%s\n' "$skipped" +[ $fails -eq 0 ]