Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions .agents/docs/2026-09-17-openkal-0.13-ecosystem-record.md
Original file line number Diff line number Diff line change
@@ -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/<repo>`,两端 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 <intrin.h>`);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 头文件的包,不是某一个包的问题 |
203 changes: 203 additions & 0 deletions .agents/docs/2026-09-17-openkal-0.13-verify.sh
Original file line number Diff line number Diff line change
@@ -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 <this file>)
# xlings subos new v013 && xlings subos use v013 --sandbox --cmd \
# "echo $B64 | base64 -d > /tmp/v.sh && MCPP_VERIFY_VERSION=<mcpp> 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" <<TOML
[package]
name = "okv"
version = "0.1.0"
[dependencies]
openkal = "$SPEC"
[target.'cfg(os = "linux")'.dependencies]
openkal-linux = "$LINUX"
TOML
cat > "$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" <<TOML
[package]
name = "okm"
version = "0.1.0"
[dependencies]
openkal-musl = "$MUSL"
[build]
cxx_runtime = "host-coupled"
[targets.okm]
kind = "bin"
main = "src/main.c"
[toolchain]
default = "llvm@22.1.8"
TOML
cat > "$work/musl/src/main.c" <<'C'
#include <errno.h>
#include <fcntl.h>
#include <spawn.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
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" <<TOML
[package]
name = "okp"
version = "0.1.0"
[dependencies]
openkal-llvm-runtime = "$RUNTIME"
tinyhttps = "$TINYHTTPS"
[dependencies.compat]
zlib = "1.3.2"
[toolchain]
default = "llvm@22.1.8"
TOML
cat > "$work/pk/tests/zlib.cpp" <<'CPP'
#include <zlib.h>
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 ]
Loading