Repository navigation
B3:macOS 原生接入 Runtime(立在 B1 上,包含 #427) - #525
jiangkoumo wants to merge 81 commits into
Conversation
…=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.
…) into PR Ephemeral-AI-Lab#427 (local port)
…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).
There was a problem hiding this comment.
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.
…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>
…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>
|
@jiangkoumo Merged into 已合入 |
b3-macos-native-v2b1-interfacea32da5732(它的父提交是upstream/main35d36575c)1dba37b06(merge 提交,第二个父是 feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 的fa02a1b2f)rehearsal-b3相对 B1 是 71 个;多出的 4 个全部是 feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 在拆分基点之后新加的 noatime/WAL 内容,见下文);Production LOC106022 → 110634(+4612)一句话
macOS 上不装 Docker 也能跑 Sandbox:LayerFS 用本机的 loopback NFSv3 把 Workspace 挂成普通目录,命令以当前用户身份直接执行。这个 PR 只填 macOS 平台相关的那部分代码和 #427 自带的内容,跨平台共用的实现已经在 B1 里了。
先把口径说清楚:平台无关的部分都在 B1
上一版把三个后端放在同一个分支(#498)。这次拆成四个 PR,全部立在 B1(
b1-interface,a32da5732,父提交是upstream/main35d36575c)上,各 PR 只改自己那份:engine/crates/layerfs-sandbox/src/backend/native/*(约 990 行,Linux/macOS 共用的本地进程生命周期)+identity.rs/listener.rs/streams.rsRuntime/Sandbox枚举、provider 选项、能力标签(含MountDriver::LoopbackNfs)、错误与请求类型、layerfs-api的SandboxApi(含本机资源归属校验)layerfs-node/src/{local,discovery,runtime,sandbox,workspace,exec,registry,lib}.rs本地部署接入、api/agent/src/{client,index}.js设置名、api/docs/agent.md这样 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一处使用)。Unsupported的桩,本 PR 用真实实现整文件替换。2. #427 自带的内容(随真 merge 进来)
#427 一共 78 个提交,原样进入历史,作者(
liuyuchen9 个、jiangkoumo69 个)不变。它带来: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. 测试
engine/crates/layerfs-sandbox/tests/{native_provider,local_search_path,local_stop_mount}.rs:驱动平台无关的本地启动/停止、PATH 策略、以及需要人工挂载点的强制卸载。这 3 个文件与 B2 的逐字相同(拆分规则 R5),所以两边合并不冲突;改其中一侧必须同步另一侧。tests/local_runtime.rs(9 例,#![cfg(target_os = "linux")])不属于 B3:它测的是 B2 的 Linux 原生进程表,B3 的backend/linux/是 B1 占位,带进来只会在 Linux CI 上全红。本 PR 已git rm该文件(提交fccce8fb8),合并 B2 时由 B2 原样提供,合并树里与 B2 逐字相同。api/agent/test/native_deployment.mjs:与 docs(512): propose a VM Sandbox backend (Sandboxes as namespaces in one VM per Store) #515 逐字相同(同样按 R5 规则)。为什么这样合并(审阅时请重点看这里)
真 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 自己的东西。16 个冲突文件按事先约定(P2-A)处理:
layerfs-node/src的 7 个共用文件(local、discovery、runtime、sandbox、workspace、exec、registry)取 B1 一侧——B1 里已是这一批文件在本系列里的最终形态(平台差异全写成#[cfg],一次写全);layerfs-node/src/unmount.rs取 feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 一侧(B1 里是 35 行的过渡桩,这里是真正的租约实现;它与9aa10e65c的版本一致);layerfs-sandbox的Cargo.toml、backend/mod.rs、src/lib.rs,两个tools/check_*守卫、api/agent/src/*.js、api/docs/agent.md)取 B1/9aa 文本。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/任何残留都算拆分失败,本分支已确认不存在。与预演分支
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.rsnoatimeengine/crates/layerfs-nfs/tests/mounter.rsnoatime、actimeo=60不变api/docs/agent.mddocs/README.mddocs/general/sqlite-on-network-filesystems.md这 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 自己那份。
平台策略
AGENTS.md现在写的是“Store 调用只在 Linux 本地文件系统上”,feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 把这条放宽到了 macOS。这属于 D2 的决策点,本 PR 只是把 feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 已经实现的行为带回来,不代替决策;如果 D2 最终不批准 macOS 本地 Store,这条路线要重新设计。怎么验证(macOS 27.2 / ARM64,Rust 1.85.1,
--locked,仓库 ARM64 编译参数)每个 Rust 测试命令都在
--no-run预编译后执行,显式 wall timeout ≤120 秒。cargo +1.85.1 fmt --all -- --checkcargo +1.85.1 check --locked --workspace --all-targetscargo +1.85.1 clippy --locked --workspace --all-targets -- -D warningscargo +1.85.1 clippy --locked --target x86_64-unknown-linux-gnu -p layerfs-sandbox --all-targets -- -D warningspython3 -B tools/check_product_boundary.pyPASS: scanned 1009 production Rust/SQL/JavaScript filespython3 -B -m unittest discover -s tools -p 'test_*.py'layerfs-sandboxcargo +1.85.1 test --locked -p layerfs-sandboxlocal_stop_mount,需要人工提供可丢弃的挂载点)layerfs-api--test local_install --test provider_ownership --test sandbox_operationslayerfs-daemoncargo +1.85.1 test --locked -p layerfs-daemonlayerfs-nfscargo +1.85.1 test --locked -p layerfs-nfshelper_deadline3 例,60.1 秒)layerfs-nfs(#427 新增)--test mounterhost_mount_disables_access_time_updates通过layerfs-nodecargo +1.85.1 test --locked -p layerfs-nodetests/native_*.rs全是#![cfg(target_os = "linux")],macOS 上整份 cfg 掉(#427 也是如此)LAYERFS_SANDBOX=native node smoke.mjs h43c <seed>(release addon + release daemon,CARGO_PROFILE_RELEASE_STRIP=none)Z1 m=0 d=0 e=0(无残留挂载、daemon、目录)node api/agent/test/verify/native_unmount_real.mjs <root> <addon> <daemon>UNMOUNT_CONCURRENCY PASS(16 个计时断言全过)node --test api/cli/test/cli.test.mjsnode --test api/agent/test/{calls,exec_output}.mjsnode --test api/agent/test/native_deployment.mjssmoke 跑的 48 项是这条链路的端到端行为:
LayerFS.init→sandbox.create(只用 1 个 daemon)→workspace.mount(内核挂载点真的出现)→ exec(写文件、改文件、软链、HOME落在 Sandbox 的 tmp、非零退出码、3 MiB stdout)→ Commit → 多 Workspace 并发挂载、合并安装后另一个 Workspace 立刻看到新树 → 分支合并与冲突拒绝 → 卸载后挂载点消失 →sandbox.close→ 再建一次 →SIGKILLdaemon 后挂载点不吊死、exec被拒绝、重新 reclaim → 未提交的写入丢弃、已提交的保留。未跑:
Runtime::Docker身份跑的 mock 引擎测试(docker_provider7 例)、lifecycle/keepalive/protocol 测试和layerfs-api的 7 例覆盖;本 PR 没有改 Docker 分支的代码。local_stop_mount(#[ignore],需要人工给的挂载点)。pathcheck.mjs:本次没有保留该脚本(不属于必需项),PATH 的 inherit 分支由 smoke 的 exec 用例覆盖。layerfs-node的 Linux 绑定测试要在 Linux 主机上跑(本机只有 macOS 目标)。与 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 逐字相同。风险与未完成
layerfs-nfs文件)。按“先合 feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 再合 B3”的顺序最省事。umount偶发Resource busy:历史上出现过(H35 记录过),本 PR 只是原样搬 feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 的卸载路径与租约实现。本次 smoke 48/48 与并发回归都干净,但这个残留风险没有消除。AGENTS.md的“Store 只在 Linux 本地文件系统”冲突,本 PR 沿用 feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 的行为,不代表决策已批准。layerfs-node在 macOS 上 0 个测试真的跑:macOS 侧行为靠 smoke 48/48 与并发回归脚本覆盖,不是 Rust 单测。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。layerfs-nfs的 LOC 归属:tools/production_loc.py把它算进 core 层,边界守卫把它算 engine 层,两种口径都记录在提交信息里;本 PR 没有改这个工具。/proc不可用(feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 文档已写明),smoke 不覆盖这条。审阅建议
engine/crates/layerfs-sandbox/src/backend/macos/(2 个实现文件 + 1 行模块声明,共 263 行生产代码)。git diff a32da5732 1dba37b06 --stat(75 个文件)。git grep -n 'NamespaceProvider\|backend::namespaces\|HelperSpec\|sandbox-helper\|LinuxNative\|backend::linux' HEAD -- $(git diff --name-only a32da5732 HEAD)。实跑只有 2 行命中,都是预期内的:api/docs/agent.md里 B1 原有的 namespaces 文档段;tests/native_provider.rs里#[cfg(target_os = "linux")]门住的Runtime::LinuxNative分支(该文件与 B2 逐字相同,见风险 5)。git diff rehearsal-b3 1dba37b06(只有上面表格里的 5 个 feat: macOS 原生适配——daemon 内置 loopback NFSv3 导出,普通用户 mount_nfs 挂载 Workspace(无 Docker / 无 macFUSE / 无 root) #427 文件)。