Skip to content

B3:macOS 原生接入 Runtime(立在 B1 上,包含 #427) - #525

Closed
jiangkoumo wants to merge 81 commits into
Ephemeral-AI-Lab:mainfrom
jiangkoumo:b3-macos-native-v3
Closed

jiangkoumo wants to merge 81 commits into
Ephemeral-AI-Lab:mainfrom
jiangkoumo:b3-macos-native-v3

Conversation

@jiangkoumo

Copy link
Copy Markdown
Collaborator

一句话

macOS 上不装 Docker 也能跑 Sandbox:LayerFS 用本机的 loopback NFSv3 把 Workspace 挂成普通目录,命令以当前用户身份直接执行。这个 PR 只填 macOS 平台相关的那部分代码和 #427 自带的内容,跨平台共用的实现已经在 B1 里了。

先把口径说清楚:平台无关的部分都在 B1

上一版把三个后端放在同一个分支(#498)。这次拆成四个 PR,全部立在 B1(b1-interface,a32da5732,父提交是 upstream/main 35d36575c)上,各 PR 只改自己那份:

内容 归属
engine/crates/layerfs-sandbox/src/backend/native/*(约 990 行,Linux/macOS 共用的本地进程生命周期)+ identity.rs / listener.rs / streams.rs B1
Runtime / Sandbox 枚举、provider 选项、能力标签(含 MountDriver::LoopbackNfs)、错误与请求类型、layerfs-api 的 SandboxApi(含本机资源归属校验) B1
layerfs-node/src/{local,discovery,runtime,sandbox,workspace,exec,registry,lib}.rs 本地部署接入、api/agent/src/{client,index}.js 设置名、api/docs/agent.md B1
Linux 平台事实 + Linux 门的测试 B2
macOS 平台事实 + #427 自带内容 + 5 个共用测试 本 PR(B3)
Namespaces(helper、rootfs、VM 适配器) #515

这样 B2 / B3 / #515 三个 PR 彼此不叠、两两合并 0 冲突、可以按任意顺序合入。已预演:三者全合后的树与原 #498 头 9aa10e65c 逐字节一致。所以本 PR 里看不到任何平台无关的重构,只有 macOS 那一支和 #427 的东西。

改了什么

1. macOS 平台事实(B3 真正的“新代码”,很小)

  • engine/crates/layerfs-sandbox/src/backend/macos/darwin.rs:Darwin 的进程与挂载事实(proc_pidinfo / sysctl 读进程、启动时间、挂载表、强制卸载)。这是本 crate 里唯一经过审计的 unsafe,边界守卫已把它登记为审计位置。
  • engine/crates/layerfs-sandbox/src/backend/macos/mounts.rs:从挂载表里找 Workspace 的挂载点,用 umount -f 卸掉。
  • 这两个文件把 backend/native/*(B1)里的 platform 别名接上,于是 Runtime::MacosNative(NativeMacosProvider) 这一支就跑起来了;能力表里 MountDriver::LoopbackNfs 就是这一支用的(全仓只有 native/provider.rs 一处使用)。
  • B1 里原来是同签名、返回 Unsupported 的桩,本 PR 用真实实现整文件替换。

2. #427 自带的内容(随真 merge 进来)

#427 一共 78 个提交,原样进入历史,作者(liuyuchen 9 个、jiangkoumo 69 个)不变。它带来:

  • engine/crates/layerfs-nfs/:31 个文件(loopback NFSv3 服务端、协议编解码、属性/目录/句柄、AppleDouble 处理,以及 8 个测试二进制)。
  • engine/crates/layerfs-daemon/:19 个文件(NFS 导出控制面 control/{export,export_helper,unmount,…}、application/*、control/*)。
  • engine/crates/layerfs-store-service/:3 个文件(macOS 上把 Store 装到本地、kernel guard)。
  • engine/crates/layerfs-overlay/Cargo.toml、layerfs-persistence/Cargo.toml:把 rusqlite 的 bundled 从“仅 Linux”扩到 macOS——macOS 原生路线要在本地打开 Store,这是必需的,不是残留。
  • engine/crates/layerfs-node/:Cargo.toml、src/dispatch.rs、src/unmount.rs(真正的按 Workspace 卸载租约)、tests/native_local.rs;tools/ci_tests.py、Cargo.lock。
  • api/agent/test/verify/native_unmount_real.mjs:真实卸载并发回归脚本。
  • 文档:core/docs/architecture/nfs-native-lifecycle.md、engine/crates/layerfs-node/tests/README.md,以及 feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 最后三个提交带来的 noatime/WAL 说明(api/docs/agent.md、docs/README.md、docs/general/sqlite-on-network-filesystems.md)。

3. 测试

为什么这样合并(审阅时请重点看这里)

  1. 真 merge feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 的分支头 fa02a1b2f,不是摘提交、不是 rebase。这样 feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 合进 main 之后,本 PR 里属于 feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 的那部分 diff 会自动消失,审阅者只需要看 B3 自己的东西。

  2. 16 个冲突文件按事先约定(P2-A)处理:

  3. feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 的 backend/local/ 9 个文件被删除:它被 backend/native/(共用)+ backend/macos/(macOS)+ B1 的 backend/linux/(B2 填)取代。backend/local/ 任何残留都算拆分失败,本分支已确认不存在。

  4. 与预演分支 rehearsal-b3(fee449554)的差异只有 5 个文件,全部来自 feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 在“拆分比对基点 251876979”之后的三个提交(noatime 挂载参数、SQLite WAL 文档):

    文件 差异
    engine/crates/layerfs-daemon/src/control/export.rs macOS 默认挂载参数加 noatime
    engine/crates/layerfs-nfs/tests/mounter.rs 新增:断言默认挂载参数带 noatime、actimeo=60 不变
    api/docs/agent.md 增加 noatime 说明与 WAL 文档链接(叠加在 B1 文本上)
    docs/README.md 增加一行文档索引
    docs/general/sqlite-on-network-filesystems.md 新增:macOS NFS 上 SQLite WAL 的说明

    这 5 个文件是 feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 自己的内容,删掉等于在一次 merge 里悄悄丢掉上游刚合进来的提交,所以保留。除这 5 个文件外,git diff rehearsal-b3 HEAD 为空。

#427 还没合进 main 之前,本 PR 会同时带着 #427 的 diff(78 个提交)。 这是有意的:先把历史上摆对,避免以后再合一次。若先合 #427,则本 PR 的 diff 只剩 B3 自己那份。

平台策略

怎么验证(macOS 27.2 / ARM64,Rust 1.85.1,--locked,仓库 ARM64 编译参数)

每个 Rust 测试命令都在 --no-run 预编译后执行,显式 wall timeout ≤120 秒。

项目 命令 结果
格式 cargo +1.85.1 fmt --all -- --check 退出 0,无 diff
编译 cargo +1.85.1 check --locked --workspace --all-targets 退出 0
Clippy(全仓) cargo +1.85.1 clippy --locked --workspace --all-targets -- -D warnings 退出 0,0 warning / 0 error
Clippy(Linux 目标) cargo +1.85.1 clippy --locked --target x86_64-unknown-linux-gnu -p layerfs-sandbox --all-targets -- -D warnings 退出 0
边界守卫 python3 -B tools/check_product_boundary.py PASS: scanned 1009 production Rust/SQL/JavaScript files
工具自测 python3 -B -m unittest discover -s tools -p 'test_*.py' 155 tests OK
layerfs-sandbox cargo +1.85.1 test --locked -p layerfs-sandbox 50 passed / 0 failed / 1 ignored(13 个 target;ignore 的是 local_stop_mount,需要人工提供可丢弃的挂载点)
layerfs-api --test local_install --test provider_ownership --test sandbox_operations 10 passed
layerfs-daemon cargo +1.85.1 test --locked -p layerfs-daemon 37 passed / 0 failed / 3 ignored
layerfs-nfs cargo +1.85.1 test --locked -p layerfs-nfs 18 passed(含 helper_deadline 3 例,60.1 秒)
layerfs-nfs(#427 新增) --test mounter host_mount_disables_access_time_updates 通过
layerfs-node cargo +1.85.1 test --locked -p layerfs-node 0 tests:tests/native_*.rs 全是 #![cfg(target_os = "linux")],macOS 上整份 cfg 掉(#427 也是如此)
原生 smoke 48 项 LAYERFS_SANDBOX=native node smoke.mjs h43c <seed>(release addon + release daemon,CARGO_PROFILE_RELEASE_STRIP=none) 48/48,结束检查 Z1 m=0 d=0 e=0(无残留挂载、daemon、目录)
卸载并发回归 node api/agent/test/verify/native_unmount_real.mjs <root> <addon> <daemon> UNMOUNT_CONCURRENCY PASS(16 个计时断言全过)
CLI node --test api/cli/test/cli.test.mjs 75/75
Agent 调用 node --test api/agent/test/{calls,exec_output}.mjs 2/2
部署参数校验 node --test api/agent/test/native_deployment.mjs 1/1

smoke 跑的 48 项是这条链路的端到端行为:LayerFS.init → sandbox.create(只用 1 个 daemon)→ workspace.mount(内核挂载点真的出现)→ exec(写文件、改文件、软链、HOME 落在 Sandbox 的 tmp、非零退出码、3 MiB stdout)→ Commit → 多 Workspace 并发挂载、合并安装后另一个 Workspace 立刻看到新树 → 分支合并与冲突拒绝 → 卸载后挂载点消失 → sandbox.close → 再建一次 → SIGKILL daemon 后挂载点不吊死、exec 被拒绝、重新 reclaim → 未提交的写入丢弃、已提交的保留。

未跑:

  • Docker 模式 smoke:本机没有 Docker Engine/CLI。Docker 路的行为由 Runtime::Docker 身份跑的 mock 引擎测试(docker_provider 7 例)、lifecycle/keepalive/protocol 测试和 layerfs-api 的 7 例覆盖;本 PR 没有改 Docker 分支的代码。
  • local_stop_mount(#[ignore],需要人工给的挂载点)。
  • PATH 策略脚本 pathcheck.mjs:本次没有保留该脚本(不属于必需项),PATH 的 inherit 分支由 smoke 的 exec 用例覆盖。
  • Linux 真机:layerfs-node 的 Linux 绑定测试要在 Linux 主机上跑(本机只有 macOS 目标)。
  • Durable 存储 profile:按 owner 决定全局禁用,本次只跑 Disposable。Durable 未运行(NOT_RUN,owner 明确禁用直到重新授权)。

与 B2 的依赖(2026-10-31 补,H43e + H43f):tests/local_runtime.rs 属于 B2(Linux 原生后端),B3 不带——B3 的 backend/linux/ 与 B1 占位逐字相同,带着它单独上 Linux CI 会 9 例全红(Io(Unsupported, "Linux native process table"),H43e §3)。删掉后 B3 单独应能过 Linux CI:H43e 实测 B3 的 1182 例里那 9 例是唯一失败项,且同机 B2+B3 合并树(5c6a238b)1182/1182 全过;“删后 1173/1173”是按此推断、未在 Linux 复跑(H43f 只在本机 Mac 验证)。B2+B3 合并后 1182/1182(H43e §1),H43f 另证明删除后 merge-tree b2 b3 仍等于该参照树、local_runtime.rs 与 B2 逐字相同。

风险与未完成

  1. feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 未合入前本 PR 的 diff 会很大(含 feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 的 78 个提交、31 个 layerfs-nfs 文件)。按“先合 feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 再合 B3”的顺序最省事。
  2. macOS 上 umount 偶发 Resource busy:历史上出现过(H35 记录过),本 PR 只是原样搬 feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 的卸载路径与租约实现。本次 smoke 48/48 与并发回归都干净,但这个残留风险没有消除。
  3. D2 尚未闭合:macOS 本地 Store 与根 AGENTS.md 的“Store 只在 Linux 本地文件系统”冲突,本 PR 沿用 feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 的行为,不代表决策已批准。
  4. layerfs-node 在 macOS 上 0 个测试真的跑:macOS 侧行为靠 smoke 48/48 与并发回归脚本覆盖,不是 Rust 单测。
  5. R5 共用文件:layerfs-sandbox/tests/{local_search_path,local_stop_mount}.rs、native_provider.rs 与 B2 逐字相同,api/agent/test/native_deployment.mjs 与 docs(512): propose a VM Sandbox backend (Sandboxes as namespaces in one VM per Store) #515 逐字相同。任何一侧单独修改都会破坏“任意顺序合并 0 冲突”。例外是 tests/local_runtime.rs:它属 B2,B3 不带(见「测试」一节),删除它是让合并树与 B2 一致、而不是破坏 R5。
  6. layerfs-nfs 的 LOC 归属:tools/production_loc.py 把它算进 core 层,边界守卫把它算 engine 层,两种口径都记录在提交信息里;本 PR 没有改这个工具。
  7. 后台进程:macOS 上后台进程读 /proc 不可用(feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 文档已写明),smoke 不覆盖这条。
  8. 没有基准测试/性能测量:本 PR 只做了正确性验证,未跑 benchmark。

审阅建议

…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: core/crates/layerfs-sandbox/Cargo.toml (not production (test, manifest or docs)).
No tests changed or deleted.

Production LOC: 108,451 -> 108,451 (delta 0)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: core/crates/layerfs-sandbox/src/backend/local/mod.rs (production).
No tests changed or deleted.

Production LOC: 108,451 -> 108,460 (delta +9)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: core/crates/layerfs-sandbox/src/backend/local/process.rs (production).
No tests changed or deleted.

Production LOC: 108,460 -> 108,505 (delta +45)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: core/crates/layerfs-sandbox/src/backend/local/mounts.rs (production).
No tests changed or deleted.

Production LOC: 108,505 -> 108,576 (delta +71)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: core/crates/layerfs-sandbox/src/backend/local/create.rs (production).
No tests changed or deleted.

Production LOC: 108,576 -> 108,834 (delta +258)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: core/crates/layerfs-sandbox/src/backend/local/sandbox.rs (production).
No tests changed or deleted.

Production LOC: 108,834 -> 109,057 (delta +223)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: core/crates/layerfs-sandbox/src/backend/local/exec.rs (production).
No tests changed or deleted.

Production LOC: 109,057 -> 109,166 (delta +109)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: core/crates/layerfs-sandbox/src/backend/mod.rs (production).
No tests changed or deleted.

Production LOC: 109,166 -> 109,167 (delta +1)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: core/crates/layerfs-sandbox/tests/local_runtime.rs (not production (test, manifest or docs)).
No tests changed or deleted.

Production LOC: 109,167 -> 109,167 (delta 0)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: sdk/js/native/src/runtime.rs (production).
No tests changed or deleted.

Production LOC: 109,167 -> 109,274 (delta +107)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: sdk/js/native/src/local.rs (production).
No tests changed or deleted.

Production LOC: 109,274 -> 109,527 (delta +253)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: sdk/js/native/src/registry.rs (production).
No tests changed or deleted.

Production LOC: 109,527 -> 109,527 (delta 0)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: sdk/js/native/src/discovery.rs (production).
No tests changed or deleted.

Production LOC: 109,527 -> 109,644 (delta +117)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: sdk/js/native/src/exec.rs (production).
No tests changed or deleted.

Production LOC: 109,644 -> 109,633 (delta -11)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: sdk/js/native/src/sandbox.rs (production).
No tests changed or deleted.

Production LOC: 109,633 -> 109,641 (delta +8)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: sdk/js/native/src/workspace.rs (production).
No tests changed or deleted.

Production LOC: 109,641 -> 109,636 (delta -5)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: sdk/js/native/src/lib.rs (production).
No tests changed or deleted.

Production LOC: 109,636 -> 109,638 (delta +2)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: sdk/js/native/tests/unit/runtime.rs (not production (test, manifest or docs)).
No tests changed or deleted.

Production LOC: 109,638 -> 109,638 (delta 0)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: sdk/js/src/client.js (production).
No tests changed or deleted.

Production LOC: 109,638 -> 109,656 (delta +18)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: sdk/js/src/index.js (production).
No tests changed or deleted.

Production LOC: 109,656 -> 109,658 (delta +2)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: sdk/js/test/local_deployment.mjs (not production (test, manifest or docs)).
No tests changed or deleted.

Production LOC: 109,658 -> 109,658 (delta 0)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…=local, for a VM or CI box without Docker

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole branch (or the single-commit patch).
This commit: sdk/js/README.md (not production (test, manifest or docs)).
No tests changed or deleted.

Production LOC: 109,658 -> 109,658 (delta 0)
Scope: combined core+sdk+dsh, tools/production_loc.py, base 5fb68dd.
…l-AI-Lab#402)

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole set (local commit 9334dbd22, one patch).
This commit: core/crates/layerfs-sandbox/src/backend/local/process.rs (production).
No tests changed or deleted except additions.

Production LOC: 109,658 -> 109,696 (delta +38)
Scope: combined core+sdk+dsh, tools/production_loc.py, parent 85255766e.
…l-AI-Lab#402)

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole set (local commit 9334dbd22, one patch).
This commit: core/crates/layerfs-sandbox/src/backend/local/groups.rs (production).
No tests changed or deleted except additions.

Production LOC: 109,696 -> 109,818 (delta +122)
Scope: combined core+sdk+dsh, tools/production_loc.py, parent 85255766e.
…l-AI-Lab#402)

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole set (local commit 9334dbd22, one patch).
This commit: core/crates/layerfs-sandbox/src/backend/local/exec.rs (production).
No tests changed or deleted except additions.

Production LOC: 109,818 -> 109,835 (delta +17)
Scope: combined core+sdk+dsh, tools/production_loc.py, parent 85255766e.
…l-AI-Lab#402)

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole set (local commit 9334dbd22, one patch).
This commit: core/crates/layerfs-sandbox/src/backend/local/sandbox.rs (production).
No tests changed or deleted except additions.

Production LOC: 109,835 -> 109,868 (delta +33)
Scope: combined core+sdk+dsh, tools/production_loc.py, parent 85255766e.
…l-AI-Lab#402)

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole set (local commit 9334dbd22, one patch).
This commit: core/crates/layerfs-sandbox/src/backend/local/create.rs (production).
No tests changed or deleted except additions.

Production LOC: 109,868 -> 109,882 (delta +14)
Scope: combined core+sdk+dsh, tools/production_loc.py, parent 85255766e.
…l-AI-Lab#402)

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole set (local commit 9334dbd22, one patch).
This commit: core/crates/layerfs-sandbox/src/backend/local/mounts.rs (production).
No tests changed or deleted except additions.

Production LOC: 109,882 -> 109,870 (delta -12)
Scope: combined core+sdk+dsh, tools/production_loc.py, parent 85255766e.
…l-AI-Lab#402)

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole set (local commit 9334dbd22, one patch).
This commit: core/crates/layerfs-sandbox/src/backend/local/mod.rs (production).
No tests changed or deleted except additions.

Production LOC: 109,870 -> 109,871 (delta +1)
Scope: combined core+sdk+dsh, tools/production_loc.py, parent 85255766e.
…l-AI-Lab#402)

Part of one change pushed file by file through the GitHub contents API; the reviewable unit is the whole set (local commit 9334dbd22, one patch).
This commit: core/crates/layerfs-sandbox/src/backend/mod.rs (production).
No tests changed or deleted except additions.

Production LOC: 109,871 -> 109,872 (delta +1)
Scope: combined core+sdk+dsh, tools/production_loc.py, parent 85255766e.
jiangkoumo and others added 20 commits October 27, 2026 01:14
…meral-AI-Lab#402)

Upstream now keeps the Node binding's tests outside the crate source: they
load the ordinary built addon and call its exported methods, never including
private binding source (engine/AGENTS.md, engine/crates/layerfs-node/tests).
The Ephemeral-AI-Lab#402 unit tests that were included by runtime.rs and sandbox.rs
(tests/unit/{runtime,reclaim}.rs before D8) are rewritten as
engine/crates/layerfs-node/tests/native_local.rs, which writes a Sandbox
record as an earlier call would and calls the public
`runtime.sandbox.reclaim` with a local deployment:

- a local record naming any other directory (a data directory, another
  LAYERFS_LOCAL_DIR, another Sandbox or name, a relative or `..` path, `/`)
  is refused InvalidInput, "neither used nor deleted", and nothing is
  removed;
- a local record is used only with an absolute, plain deploy.localDir;
- a stopped local Sandbox where this deployment puts it is deleted and its
  number freed; a record from before boot ids is read without one; a
  damaged boot or port is refused and removes nothing;
- a Docker record is not read as a local Sandbox.

The Docker reclaim cases of the old reclaim.rs are covered by upstream's
native_discovery.rs. Selection of a misspelled deploy.sandbox is refused only
by sandbox.create after the project's Store is installed, so it is not
reachable without a Store and is left to the SDK test (local_deployment.mjs).

Production LOC: 103762 -> 103762 (delta 0)
(tools/production_loc.py --root ., core+engine+api; test-only commit)
…e built addon) into PR Ephemeral-AI-Lab#427 (local port)

Brings Ephemeral-AI-Lab#402's engine/crates/layerfs-node/tests/native_local.rs. One
conflict: api/binding/tests/unit/runtime.rs, deleted on Ephemeral-AI-Lab#402 and kept by
Ephemeral-AI-Lab#427's D8 merge, is deleted here too. api/binding/ no longer exists after
D8, the file was included by nothing, and its cases move to native_local.rs
in the next commit.

Production LOC: first-parent comparison 1931032 -> this merge, see
tools/production_loc.py --root .; tests only, delta 0.
Production LOC: 103762 -> 103762 (delta 0)
(tools/production_loc.py --root ., core+engine+api; formatting of a test file only)
…fter D8 (Ephemeral-AI-Lab#427)

- engine/crates/layerfs-node/tests/native_local.rs gains
  local_and_native_sandboxes_are_never_mixed: through the built addon's
  public runtime.sandbox.reclaim, a native record is refused (InvalidInput,
  "never mixed") by a local deployment and a local record by a native one,
  nothing is removed, and a native deployment reclaims its own stopped
  record. It replaces the native cases of the old included
  api/binding/tests/unit/runtime.rs (removed in the previous merge).
- The real native SDK regression of the unmount lease moves from
  api/binding/tests/unmount-concurrency.mjs (api/binding/ is gone after D8)
  to api/agent/test/verify/native_unmount_real.mjs, next to upstream's other
  *_real.mjs verifications, with its imports updated to D8's layout
  (api/agent/src, api/backend/src). It stays macOS-only and explicit.
- discovery.rs: the "never mixed" refusal lost its line continuation in the
  v1.1 port and carried a run of spaces inside the message; restored.

Production LOC: 108595 -> 108596 (delta +1)
(tools/production_loc.py --root ., core+engine+api; the restored message
continuation splits one source line in two)
…Ephemeral-AI-Lab#427)

D8 (Ephemeral-AI-Lab#465) made tools/check_product_boundary.py fail closed on any LayerFS
crate missing from its core/engine/api layer map. Ephemeral-AI-Lab#427's layerfs-nfs (the
loopback NFSv3 export, beside layerfs-fuse under engine/crates) was not in
it, so the full and the CI (--skip-sdk) runs reported
"crate missing from layer map: layerfs-nfs" for layerfs-nfs and for
layerfs-daemon, which depends on it. It is an engine crate like
layerfs-fuse; its dependencies (content, overlay, workspace, fuse) are at
or below that layer.

Production LOC: 108596 -> 108596 (delta 0)
(tools/production_loc.py --root ., core+engine+api; development tool only)
…-Lab#402)

Production LOC: 103762 -> 103762 (delta 0)
(tools/production_loc.py --root ., core+engine+api; documentation only)
…nto PR Ephemeral-AI-Lab#427 (local port)

Production LOC: 108596 -> 108596 (delta 0), first-parent comparison
(tools/production_loc.py --root ., core+engine+api; documentation only)
…b#427)

Production LOC: 108596 -> 108596 (delta 0)
(tools/production_loc.py --root ., core+engine+api; documentation only)
…-AI-Lab#427)

Brings main up to 582bc78 (Ephemeral-AI-Lab#488, Ephemeral-AI-Lab#481 forced unmount after a stalled
Join, Ephemeral-AI-Lab#479, Ephemeral-AI-Lab#471, Ephemeral-AI-Lab#473, Ephemeral-AI-Lab#445 fs modes, Ephemeral-AI-Lab#475, Ephemeral-AI-Lab#478, Ephemeral-AI-Lab#476 Docker API
negotiation, Ephemeral-AI-Lab#396, Ephemeral-AI-Lab#419, Ephemeral-AI-Lab#418, Ephemeral-AI-Lab#415, Ephemeral-AI-Lab#477, Ephemeral-AI-Lab#468).

Two conflicts, both from Ephemeral-AI-Lab#481 (forced unmount of a normal unmount stalled
at Join):
- engine/crates/layerfs-daemon/src/control/force.rs: guard() keeps this
  branch's ExportKept check (an NFS export kept after a failed teardown
  answers with its stored custody first) and takes main's comment and its
  forceable() exception for a retained FUSE session. The non-Linux
  `let _ = token;` stays removed: ExportKept uses the token on every
  platform.
- engine/crates/layerfs-node/src/workspace.rs: unmount() keeps this
  branch's per-Workspace unmount lease (repeat a published outcome, drop
  the admission guard before the daemon call, publish the outcome before
  cleanup) and takes main's `force` option inside it: forced, one
  api.unmount(token, true) whose None answer is Unmounted::Closed;
  otherwise unmount_checked with `discard` as before.

Production LOC: first-parent 9478190 108596 -> 109158
(tools/production_loc.py --root .; the delta is main's own changes)
…er selectable (Ephemeral-AI-Lab#427)

Ephemeral-AI-Lab#427 is the macOS native adaptation. The local-daemon runtime it builds
on (first proposed in the closed Ephemeral-AI-Lab#402) is now part of this PR, and its
only selectable form is `native`: on macOS a daemon of this user serves
its Workspaces over loopback NFS to the host's own client. Linux keeps
the Docker route exactly as on main.

- deploy.sandbox (LAYERFS_SANDBOX) accepts unset, `docker` or `native`;
  `local` is refused InvalidInput like any other value, in the SDK
  (api/agent/src/client.js) and in the binding (runtime::local).
- local::start refuses `native` off macOS as Unsupported before any
  effect, so no deployment starts a local daemon on Linux. The root
  check and the Local::new (Linux FUSE) branch go: native always takes
  Local::native, LAYERFS_KERNEL=nfs and this user's identity.
  discovery::root() had no other caller and goes too.
- Records: a new record's runtime is always `native`. A native record is
  used only by a native deployment; a record of the former `local`
  runtime (an earlier, unmerged build) is used by no deployment, refused
  InvalidInput ("never mixed", "neither used nor deleted"), and nothing
  it names is deleted.
- Tests: engine/crates/layerfs-node/tests/native_local.rs writes native
  records (record handling is the same code on Linux) and gains
  a_record_of_the_former_local_runtime_is_never_used_nor_deleted;
  api/agent/test/local_deployment.mjs becomes native_deployment.mjs with
  `local` among the refused values.
- api/docs/agent.md drops the "Local Sandboxes" section and keeps its
  layout, record and reclaim facts under "Native Sandboxes (macOS)".

The local backend in layerfs-sandbox (engine/crates/layerfs-sandbox/src/
backend/local) is kept as it is: native uses its process, exec, group,
mount and record code; Local::new and its Linux tests stay as tests of
that shared code.

Production LOC: 109158 -> 109142 (delta -16)
(tools/production_loc.py --root .)
Poll endpoint through the Sandbox that owns the Child before asserting that no zombie remains. Bound the observation by WAIT and preserve the stopped and not-gone assertions without changing product behavior.

Validation: cargo +1.85.1 fmt --all -- --check; cargo +1.85.1 clippy --locked -p layerfs-sandbox --all-targets -- -D warnings. Both passed on macOS with this worktree private target. Linux-only runtime tests were not executed; Hark owns Linux verification.

Production LOC: 109210 -> 109210 (delta +0). Scope: core 41400, engine 65131, API 2679; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Tests, tools, docs, dependencies and generated artifacts excluded; inline test code excluded by the counter.

Method: tools/production_loc.py at Git blob 3341e4d; export parent 3147916 and final staged tree d97b40d using git archive, then run python3 -B tools/production_loc.py --root <each-export> --json. Both invocations exited 0.
Mount the macOS native export with noatime. The export drops SETATTR's
access time and exposes the stored mtime for all three timestamps, so
client access-time updates add work without persisting a distinct atime.
Keep the Linux mount options unchanged. Document the native default and
cover the macOS helper option while preserving actimeo=60.

Exploratory Git scans reduced per-command SETATTR from 4831 to 2; wall
time was noisy and does not establish a speedup. Imported Git index stat
mismatches and attribute-less READDIRPLUS remain separate limitations.
No Git stat checks, stored timestamps, inode identities or formats change.

Validation: fmt; locked warning-denying Clippy for layerfs-daemon and
layerfs-nfs all targets; NFS tests (17 passed before adding the helper
test); final helper test (1 passed); product boundary guard; native mount
observed noatime. No tests deleted. Linux native execution not run.

Production LOC: 109210 -> 109210 (delta +0)
Scope: core 41400, engine 65131, API 2679; dsh absent.
Method: tools/production_loc.py at parent 2518769, identical counter on
git archive of HEAD and staged tree 86a1874;
nonblank/noncomment product Rust, runtime SQL and JS, excluding tests.
reference absent (retired R9c; last counted 65,417 at tree 498dd19).
Document the system SQLite VFS restriction, safe pre-import journal conversion and cache placement. Link the guide from the SDK and documentation index. System nfsd comparison remains blocked by unavailable administrator authentication.

Production LOC: 109210 -> 109210 (delta +0). Method: python3 -B tools/production_loc.py --json, counter blob 3341e4d. First-parent and staged product trees are identical; only documentation changes. Counter scopes: core 41400, engine 65131, API 2679; layerfs-nfs is classified under core by this pinned counter. Reference absent (retired R9c; last counted 65,417 at tree 498dd19).
Production LOC: 109210 -> 109210 (delta +0). First-parent comparison; core 41400, engine 65131, API 2679. Reference absent (retired R9c; last counted 65,417 at tree 498dd19).

Method: tools/production_loc.py blob 3341e4d; git archive of first parent 2518769 and final staged tree 86a1874, then python3 -B tools/production_loc.py --root <export> --json. Both exited 0; tests, docs, tools and inline test code excluded.
Production LOC: 109210 -> 109210 (delta +0). First-parent comparison; core 41400, engine 65131, API 2679. Reference absent (retired R9c; last counted 65,417 at tree 498dd19).

Method: tools/production_loc.py blob 3341e4d; git archive of first parent 212ac9aec31642138b6afa617178eb1f18bc04e5 and final staged tree cc1e2d1, then python3 -B tools/production_loc.py --root <export> --json. Both exited 0; tests, docs, tools and inline test code excluded.
…der (Area B1)

Extract the complete Sandbox runtime interface and shared native/node plumbing from 9aa10e6 onto upstream/main (35d3657), implementing the Docker backend and leaving same-signature Unsupported slots for backend/{linux,macos,namespaces}/ so B2, B3 and Ephemeral-AI-Lab#515 only replace their own backend directories.

In layerfs-sandbox:
- Define concrete Runtime (Docker, MacosNative, LinuxNative, Namespaces, Windows) and Sandbox (Docker, Native, Namespaces) enums in backend/runtime.rs.
- Add provider capabilities (including LoopbackNfs, MicroVm, RemoteCloud), LaunchPhase/LaunchFailure, ProviderLaunchOptions (Local, Namespaces, Docker) and SandboxRecord (Local, Namespaces, Docker).
- Add types::Image and cross-backend ExecRequest; move shared json and strings helpers to backend/.
- Implement DockerProvider and DockerInstance over existing Docker Engine lifecycle and streaming execution.
- Include shared backend/native/* lifecycle, identity, listener and streams modules under cfg(any(linux, macos)), with same-signature Unsupported stubs in backend/{linux,macos,namespaces}/.

In layerfs-api:
- Route SandboxApi through Arc<Runtime> via with_runtime and create_provider_file, retaining local resource ownership and command identity validation.
- Share TCP/Noise/Hello and Store install pipeline in sandbox/connection.rs.

In layerfs-node and @layerfs/agent:
- Take verbatim 9aa10e6 shared binding modules (local, discovery, runtime, sandbox, workspace, exec, registry, lib) with a no-op unmount slot for B3.
- Expose deploy.sandbox, deploy.localDir, deploy.sandboxBackend and deploy.imageArchive settings in @layerfs/agent.

Production LOC: 102831 -> 106022 (delta +3191)
First-parent comparison: upstream/main 35d3657 (tree ddea2b5) -> committed tree ae48e29.
Scopes:
  - core:   38395 -> 38395 (delta 0)
  - engine: 61777 -> 64938 (delta +3161)
    * layerfs-sandbox: 3417 -> 5526 (delta +2109)
    * layerfs-api:     1940 -> 2081 (delta +141)
    * layerfs-node:    2744 -> 3655 (delta +911)
  - api:     2659 ->  2689 (delta +30)
    * @layerfs/agent:   849 ->  879 (delta +30)
  - dsh:        0 ->     0 (delta 0)
  - reference: absent (retired R9c; last counted 65,417 at tree 498dd19)
Method: tools/production_loc.py (blob 3341e4d), --root <exact exported tree> --json for both snapshots; first-party production Rust, runtime SQL and JavaScript; tests, docs, tooling, generated and third-party files excluded.
…ntime enum (Area B3)

Integrate Ephemeral-AI-Lab#427 at its branch head fa02a1b, keeping its 78 commits and
their authorship, and place the macOS native backend on B1's single
Runtime/Sandbox enum as Runtime::MacosNative(NativeMacosProvider). This is
the B3 slice of the Ephemeral-AI-Lab#498 split: the platform-independent implementation
(backend/native/*, the node pipeline, the api/agent settings) is already in
B1 a32da57, so this merge only adds macOS platform facts plus Ephemeral-AI-Lab#427 itself.

Conflicts (16 files) follow the H42a-2 P2-A rule: layerfs-node/src's shared
files (local, discovery, runtime, sandbox, workspace, exec, registry) take
the B1 side, unmount.rs takes the Ephemeral-AI-Lab#427 side, and every other file takes the
9aa10e6 text, which is what B3 is specified to add. Ephemeral-AI-Lab#427's
backend/local/ (9 files) is removed: native/ + macos/ + B1's linux/ replace
it. The merged tree equals the rehearsal-b3 tree (fee449554, tree
ed2356142) except for five files that come from Ephemeral-AI-Lab#427's three commits after
the split's comparison base 2518769: the noatime mount option and its
mounter test in layerfs-daemon/layerfs-nfs, and the noatime/WAL
documentation in api/docs/agent.md, docs/README.md and
docs/general/sqlite-on-network-filesystems.md. Those are Ephemeral-AI-Lab#427 content and
stay; they disappear from this PR's diff once Ephemeral-AI-Lab#427 is on main.
Per-workspace unmount leases, NFS export control, layerfs-nfs, the macOS
Store install and kernel guard are Ephemeral-AI-Lab#427 unchanged.

D2 macOS stays as Ephemeral-AI-Lab#427 implements it (Store on the host, loopback NFSv3
mount); D13 is NFSv3 now with FSKit as follow-up. No Durable execution.
Deleted tests: none. Retained Ephemeral-AI-Lab#427 test targets: layerfs-nfs's 8 binaries
and mounter, layerfs-node's Linux-gated native_* tests (0 tests on macOS).

Validation: macOS 27.2 ARM64, Rust 1.85.1, locked dependencies, repository
ARM64 flags, every Rust test command under a 120 s wall timeout after a
--no-run build. cargo fmt --check, cargo check --workspace --all-targets,
Clippy -D warnings for the workspace and for layerfs-sandbox at
x86_64-unknown-linux-gnu, product-boundary guard (1009 files) and the 155
tooling self-tests passed. layerfs-sandbox 45 passed/1 ignored
(local_stop_mount needs a real mount), layerfs-api 10 passed, layerfs-daemon
6 passed/2 ignored, layerfs-nfs 18 passed, layerfs-node 0 tests on macOS
(Linux-gated), api/cli 75 passed, agent calls/exec_output and
native_deployment passed. LAYERFS_SANDBOX=native release smoke (addon and
daemon built with CARGO_PROFILE_RELEASE_STRIP=none): 48/48, Z1 m=0 d=0 e=0.
Disposable only; Durable NOT_RUN (owner-disabled). Docker route NOT_RUN on
this host (no Docker Engine); its behaviour is covered by the mock-engine
and lifecycle tests above, which run as Runtime::Docker. No benchmark.

Production LOC: 106022 -> 110634 (delta +4612)
First-parent comparison: b1-interface a32da57
to prepared merge tree fcd69b9.
Scopes: core 38395 -> 41400 (+3005, layerfs-nfs; the counter files it under
core, the boundary guard calls it engine), engine 64938 -> 66545 (+1607:
daemon +1122, node +231, sandbox +226, store-service +28), api 2689 -> 2689
(0), dsh 0 -> 0.
Method: tools/production_loc.py blob 3341e4d,
--root <exact exported tree> --json for both snapshots; identical counter and
scope for both; first-party production Rust, runtime SQL and JavaScript;
tests, docs, tooling, fixtures, generated and third-party files excluded.
Reference absent (retired R9c; last counted 65,417 at tree 498dd19).
…anch (Area B3)

engine/crates/layerfs-sandbox/tests/local_runtime.rs arrived in B3 with the
Ephemeral-AI-Lab#427 merge (fa02a1b). It is gated with #![cfg(target_os = "linux")] and
tests the Linux native process table, which B3 does not carry: B3's
backend/linux/ is byte-identical to B1's placeholder, so all nine cases fail
on Linux CI with Io(Unsupported, "Linux native process table"). The real
implementation and the file itself belong to B2 (b2-linux-native-v2), and
merging B2 into B3 keeps B2's copy unchanged, so removing it here neither
loses coverage nor changes the merged tree.

local_search_path.rs and local_stop_mount.rs are macOS-only and stay.

References: none in Cargo.toml, tools/ci_tests.py, workflows or docs. The only
remaining "local_runtime" text is layerfs-node's
a_record_of_the_former_local_runtime_is_never_used_nor_deleted test, which
covers former `local` sandbox records and is unrelated to this target.

Deleted tests: local_runtime (9 cases on Linux); the behaviour they check is
covered by the same file on B2.

Validation (macOS 27.2 ARM64, Rust 1.85.1, this worktree's own target):
cargo fmt --all --check exit 0; cargo clippy --locked --workspace --exclude
layerfs-node --all-targets -- -D warnings exit 0. tools/ci_tests.py targets
fast no longer emits --test local_runtime (259 -> 258 targets) and filter
fast is byte-identical. merge-tree --write-tree of b2-linux-native-v2 and
this branch is conflict-free and keeps local_runtime.rs identical to B2; the
resulting tree diffs empty against the H43e reference 5c6a238b.

Production LOC: 110634 -> 110634 (delta 0)
Scope: first-party production Rust, runtime SQL and published JavaScript;
tests, docs, tooling, fixtures and generated files excluded. Method:
tools/production_loc.py --root <exported parent tree | this tree> --json,
identical counter and scope for both snapshots. Parent comparison:
1dba37b to this commit. The change deletes one test file only.
Reference absent (retired R9c; last counted 65,417 at tree 498dd19).
Copilot AI balanced review requested due to automatic review settings October 31, 2026 14:14

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

yifanxuaaa added a commit that referenced this pull request Oct 31, 2026
…fs_v2_0_0

B2 (layerfs-connect#46): native Linux Sandbox, filling B1's Linux
process and mount-table backend (engine/crates/layerfs-sandbox/src/
backend/linux/) and adding its tests. Merged at PR head 45556f3 onto
the B1 merge d39bb6c; no conflicts. One merge adjustment: B2's new
native_provider test gives its Docker record `Some(...)` for the
volume, which is optional on this branch (#506).

Verified on macOS (Rust 1.85.1): workspace warning-denying Clippy, all
targets, and format check (only the existing minio.rs drift).
layerfs-sandbox tests: 49 passed, 2 failed, 1 ignored. The two failures
(local_search_path, native_provider) stop at B1's macOS placeholder
"macOS native process and mount table" (Unsupported), which B3 (#525)
replaces; B2's own head has the same placeholder, so they cannot pass on
macOS before B3. layerfs-node's tests are Linux-only. Not run: Linux
build and tests, where B2's code runs (no Linux C toolchain on the
host, Docker not running). Disposable only; Durable NOT_RUN, disabled
by owner.

Production LOC: 115107 -> 115210 (delta +103)
First-parent comparison: layerfs_v2_0_0 d39bb6c (tree cae5bb4) to
prepared merge tree c17f04f.
Scopes: core 38400 -> 38400 (+0), engine 73412 -> 73515 (+103),
api 3295 -> 3295 (+0). By crate: layerfs-sandbox +103.
Method: tools/production_loc.py (blob 686d269)
--root <exported tree> --json for both snapshots; first-party production
Rust, runtime SQL and JavaScript; tests, docs, tooling, generated and
third-party files excluded.
Reference absent (retired R9c; last counted 65,417 at tree 498dd19).
Deleted tests: none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
yifanxuaaa added a commit that referenced this pull request Oct 31, 2026
…fs_v2_0_0

B3 (layerfs-connect#47): native macOS Sandbox on B1's interface,
including #427 (daemon-served loopback NFSv3 export, mounted by the user
with mount_nfs; new crate layerfs-nfs). Merged at PR head fccce8f
onto the B2 merge 7d55f02. Conflicts resolved:

- layerfs-store-service install.rs: B3 gives installer_user a macOS
  variant (no root daemon to match) and gates the Linux one; v2 made it
  pub(crate) for remote/pull.rs (#506). Both variants are pub(crate).
- layerfs-sandbox tests/native_provider.rs: keeps the optional
  `Some(...)` Store volume from the B2 merge.
- layerfs-node tests/README.md: lists both v2's native_connection_wait
  and B3's native_local.

Verified on macOS (Rust 1.85.1): workspace warning-denying Clippy, all
targets; format check (only the existing minio.rs drift). Tests, each
group under a 120 s cap: layerfs-nfs, -sandbox and -node 69 passed,
0 failed, 1 ignored (B2's local_search_path and native_provider now
pass with B3's macOS process facts); layerfs-daemon and -store-service
50 passed, 0 failed, 3 ignored; layerfs-overlay and -persistence 200
passed, 0 failed, 2 ignored. Boundary guard PASS with self-tests;
tools/test_ci_tests.py OK; agent calls, exec_output and packaging
surface/sandbox/lifecycle PASS. Not run: Linux build and tests (no
Linux C toolchain on the host, Docker not running), a live mount_nfs
mount, Docker and FUSE E2E. Disposable only; Durable NOT_RUN, disabled
by owner.

Production LOC: 115210 -> 119822 (delta +4612)
First-parent comparison: layerfs_v2_0_0 7d55f02 (tree c17f04f) to
prepared merge tree 63d9a3c.
Scopes: core 38400 -> 41405 (+3005), engine 73515 -> 75122 (+1607),
api 3295 -> 3295 (+0). By crate: layerfs-nfs +3005 (new; the counter
does not yet know this crate, so it falls in core by its default, though
it sits in the engine layer), layerfs-daemon +1122, layerfs-node +231,
layerfs-sandbox +226, layerfs-store-service +28. Matches the +4612 in
#525's description.
Method: tools/production_loc.py (blob 686d269)
--root <exported tree> --json for both snapshots; first-party production
Rust, runtime SQL and JavaScript; tests, docs, tooling, generated and
third-party files excluded.
Reference absent (retired R9c; last counted 65,417 at tree 498dd19).
Deleted tests: none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yifanxuaaa

Copy link
Copy Markdown
Contributor

@jiangkoumo Merged into layerfs_v2_0_0 at this PR's head fccce8fb88 (merge commit a1cd85c), including #427. Conflicts resolved: installer_user keeps your macOS and Linux variants, both pub(crate) because v2's remote pull also calls it; the node tests README lists both native_connection_wait and native_local. layerfs-nfs, layerfs-daemon and layerfs-store-service tests pass on macOS and Linux. Closing here because this PR targets main. Thank you!

已合入 layerfs_v2_0_0,合入的是本 PR 的头 fccce8fb88(合并提交 a1cd85c),包含 #427。冲突处理:installer_user 保留你的 macOS 和 Linux 两个版本,两者都设为 pub(crate),因为 v2 的 remote pull 也会调用它;node 测试的 README 同时列出 native_connection_wait 和 native_local。layerfs-nfs、layerfs-daemon 和 layerfs-store-service 的测试在 macOS 和 Linux 上均通过。本 PR 的目标分支是 main,因此在此关闭。感谢!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants