Skip to content

D8: split core into core, engine and api layers (#463) - #465

Merged
yifanxuaaa merged 10 commits into
mainfrom
issue463-layer-split
Oct 26, 2026
Merged

yifanxuaaa merged 10 commits into
mainfrom
issue463-layer-split

Conversation

@yifanxuaaa

Copy link
Copy Markdown
Contributor

Splits the Rust workspace into three layers, core < engine < api (D8, #446). The Cargo workspace moves to the repository root.

Closes #463.

Layout

  • core/crates/: content, history, storage and telemetry. They depend only on each other and don't use rusqlite.
  • engine/crates/: bridge, daemon, fuse, overlay, persistence, project, sandbox, store-service, workspace, and the SDK method surface layerfs-api and Node binding layerfs-node.
  • api/: only the published JavaScript packages: agent, cli, backend, runtime, docs, scripts.

The owner's gate decisions and the change to keep Rust out of api/ are recorded on #463. The full record is core/docs/issues/463/IMPLEMENTATION.md.

Commits

Commit What Production LOC
05db985693 Binding tests move out of production source 102158 -> 102158 (0)
2775b3fa44 Boundary guard enforces crate layers, dev dependencies and test includes 102158 -> 102158 (0)
0f442d5bb7 Store Init composition moves into layerfs-project 102158 -> 102162 (+4)
3cae8b1a1e SDK-facing daemon tests move to the API crate 102162 -> 102162 (0)
ce2c0eb411 Relocation: root workspace, engine crates, tools, CI 102162 -> 102162 (0)
ef4c26b02b CI: Linux writer admission goes in the slow lane 102162 -> 102162 (0)
853abcbe2b Layer guides and the implementation record 102162 -> 102162 (0)
ff7ccee2f6 FP-21 test: stage provider reads after reader cache warming 102162 -> 102162 (0)
b98ac4c2f7 Rust leaves api/; packages flattened 102162 -> 102162 (0)
f406638c80 Record the decision; fix moved links 102162 -> 102162 (0)

Every commit message gives its counting method. Reference absent (retired R9c; last counted 65,417 at tree 498dd1917812).

Checks

  • The boundary guard passes on 954 files.
  • Cargo metadata is unchanged: 15 packages with the same edges and targets.
  • cargo +1.85.1 check --workspace --all-targets and fmt --check pass.
  • 155 Python tool tests pass, and Ruff 0.17.2 reports no new findings.
  • The JavaScript stand-in tests pass (agent, backend, runtime, packaging, trace; CLI 70/70).
  • A sealed Linux release bundle builds from this branch. dsh ran all ten scenarios on it.
  • The migration commits record Clippy and the Linux test selections; the FP-21 failure there is fixed by ff7ccee2f6.

Not run here: three agent tests that need Linux and a real Store (binary_write, init_refusals, reinit). CI's fast and nightly lanes report after the push.

After merging

  • Open PRs that edit core/crates/* or api/ need a rebase. Git follows plain moves.
  • Each worktree's registered target moves to the root target/; prepare again after rebasing.
  • dsh-layerfs needs its path updates (branch layerfs-463-api-paths) and a pin bump.
  • layerfs-benchmark's common/lib.sh still uses core/target.

🤖 Generated with Claude Code

yifanxuaaa and others added 10 commits October 27, 2026 01:22
Remove test-only modules and private-helper includes from the Node binding.
Replace their eight tests with external Rust tests that load the built addon in
Node, using bounded Engine/daemon fixtures. Preserve the forked-child lock
regression through a test-only preload fixture; no product API is added.
Keep binding integration targets outside the owner-excluded CI lanes.

Deleted test locations (all behavior re-covered externally):
- discovery.rs::tests::a_dropped_guard_releases_the_lock_while_a_forked_child_holds_a_copy -> tests/native_discovery.rs
- tests/unit/admission.rs (2 tests) -> tests/native_admission.rs
- tests/unit/reclaim.rs (5 tests) -> tests/native_discovery.rs
The public admission deadline test exercises the full 60 s wait; private
300 ms deadline injection is no longer used.

Checks: Linux layerfs-node build and tests --no-run; native_discovery 6/6
(30 s ceiling); native_admission 2/2 (90 s ceiling, 60.52 s); node all-target
Clippy -D warnings; workspace fmt check; full boundary guard; core/tools
unittest suite 63/63 (120 s ceiling); Ruff 0.17.2 format; diff --check.
The tests use the registered private Linux Cargo home/target. Node 24 is
copied from the pinned runtime image into that target for these checks.
No Store profile executes in these fixtures. No production behavior changes.

Production LOC: 102158 -> 102158 (delta 0)
Scope: all first-party product scopes; core 96788, api 5370, dsh absent.
Method: tools/production_loc.py --json from exact parent and staged snapshots;
Rust-aware comments and inline test items excluded, shipped SQL and JS included.
Counter SHA256: 7e0c3c1ab2fd52895b484c56d18b907cc33b5adbe9a89f8eca77a21ad82b45f0
reference absent (retired R9c; last counted 65,417 at tree 498dd19).
Map all Rust crates (including layerfs-node) to core, engine and api. Check
production/build/dev edges, target-table aliases, resolved test path includes,
and the core rusqlite ban. Unknown crates fail closed. Keep layout knowledge
in one crate-root helper and preserve the production edge allowlist.

Temporary exception: daemon -> api dev dependency and only the six includes
from merge_report, concurrent_commits and lane_merge to the two named API
helpers. Step 2b removes the exception. DOMAIN_CRATES is unchanged for step 1.

G1: owner instructed proceeding with MIGRATION_SPEC.md; D8 accepted. Subsequent
owner answers allow project -> bridge, select engine, and authorize relocation
once branch conflicts are resolved. No push, PR or merge is authorized.

Checks: full-tree boundary guard PASS (953 production inputs); core/tools
unittest discovery 63/63 PASS under 120 s timeout (includes all guard tests);
Ruff 0.17.2 format and check PASS; git diff --check PASS.
Tooling only; Rust compilation/runtime tests are inapplicable to this step.
No tests deleted. The binding prerequisite is commit 05db985.

Production LOC: 102158 -> 102158 (delta 0)
Scope: all first-party product scopes, core 96788 and api 5370; dsh absent.
Method: tools/production_loc.py --json from exact parent and staged snapshots;
Rust-aware comments/inline tests excluded, shipped SQL and JS included.
Counter SHA256: 7e0c3c1ab2fd52895b484c56d18b907cc33b5adbe9a89f8eca77a21ad82b45f0
reference absent (retired R9c; last counted 65,417 at tree 498dd19).
Move the unchanged Init and install bodies into layerfs-project::store, retain
API re-exports, and call Project Init directly from the native binding. Approve
only the owner-selected Project bridge/persistence edges in the layer guard.

Checks: all fifteen packages all-target no-run build and Clippy -D warnings;
full boundary and fuser guards; 64 Python tool tests; fmt and Ruff format;
literal native build, paired Linux SDK build and sealed SDK Init refusal proof.
234 executable runs passed and 112 reuse completed unaffected proofs.

Verification exception explicitly authorized by owner: fp21_detach_stages
retains two FAILs on the unchanged warmed-reader/provider-gate assumption.
Failure output, original container, scratch and execution plan retained; no
assertion weakened and no rerun. No unrun executable selections remain.
No tests deleted or moved in this step.

Production LOC: 102158 -> 102162 (delta +4)
Scope: all first-party product implementation, including shipped SQL and JavaScript.
before: api 5370, core 96788.
after: api 5368, core 96794.
Method: tools/production_loc.py --json; exact parent and staged trees; inline tests/comments excluded; shipped SQL and JS included.
Counter SHA256: 7e0c3c1ab2fd52895b484c56d18b907cc33b5adbe9a89f8eca77a21ad82b45f0.
reference absent (retired R9c; last counted 65,417 at tree 498dd19).
Move mount_control, kernel_mount_routes, kernel_application,
observed_application, r5_1_actual_daemon_commit, merge_report,
concurrent_commits and lane_merge to layerfs-api/tests. Keep their assertions
and existing lower-layer fixtures; locate the ordinary separately built daemon
binary from three API tests. Install-only daemon tests, fixtures and the
resource_prepare example now use Project's Store entrypoint directly.

Remove daemon's API dev edge and every temporary upward guard exception.
API adds daemon, FUSE and the existing pinned nix as test dependencies only.
CI selection follows the moved heavy and Linux mount targets. No versions or
production behavior change. No tests deleted.

Checks: full fifteen-package all-target no-run compilation and Clippy -D warnings;
focused Project/API/daemon executable proofs; native binding build; full product
and fuser guards; 64 Python self-tests; Rust fmt, Ruff format and whitespace.
The unchanged FP-21 failure remains FAIL under the explicit owner exception;
its original proof is retained and is not rerun. Unaffected proofs are reused.

Production LOC: 102162 -> 102162 (delta +0)
Scope: all first-party product implementation, including shipped SQL and JavaScript.
before: api 5368, core 96794.
after: api 5368, core 96794.
Method: tools/production_loc.py --json; exact parent and staged trees; inline tests/comments excluded; shipped SQL and JS included.
Counter SHA256: 7e0c3c1ab2fd52895b484c56d18b907cc33b5adbe9a89f8eca77a21ad82b45f0.
reference absent (retired R9c; last counted 65,417 at tree 498dd19).
Keep the four algorithm crates in core, move nine engine crates and the Rust
API to their selected layers, and move the manifest, lock, nextest, tools and
authorized fuser package/provenance to their owning roots. Fix relative manifests,
test includes, launcher input/target paths, native scripts and CI path selection.
The fixture-seal test now locates the root manifest and scans the relocated
first-party roots; it retains every original assertion. Its initial path failure
is retained, the repaired case passes, and its five unchanged passing cases
reuse their initial proof.
The root LOC counter retains both layouts and assigns crates by name.

All 954 production files retain identical body bytes and per-file LOC; 572 are
relocated. Cargo metadata retains the same fifteen packages, first-party edges
(including kinds, targets and aliases), package/dependency features and versions.
The lockfile and third-party package bytes are unchanged. Only the redundant
core tooling counter is deleted. No tests deleted. All moved files are detected
as renames with -M20%; the two extensively rewritten path manifests fall below
Git's default similarity threshold. The fixture-seal scan-root assumption is an additional live path hit
found by execution beyond the spec's initial inventory. Other edits are in its
listed path/tool scope. Exact path, body, graph and LOC receipts are retained outside Git.

Checks: root all-target no-run build and warning-denying Clippy for all fifteen
packages; per-executable Linux tests under 100-second ceilings; native script and
paired daemon/addon SDK build; full boundary and fuser provenance guards; 154
combined Python self-tests; Rust fmt, pinned Ruff format and whitespace.
The unchanged FP-21 test retains its original FAIL under the explicit owner
verification exception and is not rerun.

Only this worktree's resource record was prepared for the new target path.
Other owners confirmed ready to rebase; their branches/records are untouched.

Production LOC: 102162 -> 102162 (delta +0)
Scope: all first-party product implementation, including shipped SQL and JavaScript.
before: api 7281, core 38241, engine 56640.
after: api 7281, core 38241, engine 56640.
Method: tools/production_loc.py --json; exact parent and staged trees; inline tests/comments excluded; shipped SQL and JS included.
Counter SHA256: 2903e1aed42f0eebb022b82b5fc2220278ccb870bbc06389adc79c4d340bad59.
The new counter classifies crates by name in both old and new layouts.
reference absent (retired R9c; last counted 65,417 at tree 498dd19).
The migration's full Linux functional run measured the existing
layerfs-persistence::writer_admission target at 9.74 seconds. Record that
observation in SLOW as the repository rule requires; keep this selection change
separate from the path relocation. This is test scheduling, not a performance
qualification or product change.

Checks: six CI-selection/executable self-tests under a 120-second ceiling;
pinned Ruff formatting and whitespace. No Rust product or test body changes,
so the completed relocation's Rust/runtime proofs remain applicable.
No tests deleted or moved.

Production LOC: 102162 -> 102162 (delta +0)
Scope: all first-party product implementation, including shipped SQL and JavaScript.
before: api 7281, core 38241, engine 56640.
after: api 7281, core 38241, engine 56640.
Method: tools/production_loc.py --json; exact parent and staged trees; inline tests/comments excluded; shipped SQL and JS included.
Counter SHA256: 2903e1aed42f0eebb022b82b5fc2220278ccb870bbc06389adc79c4d340bad59.
reference absent (retired R9c; last counted 65,417 at tree 498dd19).
Split the implementation guide into core, engine and API responsibilities,
retain common source/verification/profile rules, and update root workstream and
manifest navigation. Document Project Store composition and approved edges.
Update maintained guides, crate READMEs, current design navigation and SDK skill
references. Preserve historical claims/receipts, archived documents and the
reference retirement; link retired source at verified immutable commits.

Record every migration part, exact LOC methods, moved/re-covered tests, the
fixture-seal path repair, owner gates, branch rebases and external consumer
follow-ups. Retain the explicit FP21 FAIL exception and its original evidence.
Export and apply exact private cache releases after byte-verifying the archived
failure executable; preserve its scratch, sealed SDK bundles and diagnostics.

Checks: all 438 local links/anchors in 34 changed documents resolve; one status
banner per document; path/command and scope review; whitespace. Rust/runtime
checks do not apply to this docs-only change. Completed relocation verification
is recorded without turning the FP21 failure into PASS. No tests changed.

Production LOC: 102162 -> 102162 (delta +0)
Scope: all first-party product implementation, including shipped SQL and JavaScript.
before: api 7281, core 38241, engine 56640.
after: api 7281, core 38241, engine 56640.
Method: tools/production_loc.py --json; exact parent and staged trees; inline tests/comments excluded; shipped SQL and JS included.
Counter SHA256: 2903e1aed42f0eebb022b82b5fc2220278ccb870bbc06389adc79c4d340bad59.
reference absent (retired R9c; last counted 65,417 at tree 498dd19).
The caller preparation warms mounted-reader caches even when the shared
canonical cache is disabled. Both OPEN and READDIR could finish without
reaching the provider gate, so the fixture failed before any unmount attempt.

Displace prepared pooled inode values through the same Workspace's public
Store ports using 73 valid leaves with 7,300 distinct inline metadata values.
The count derives from the existing cache bound. Keep the default policy,
all product code, timeouts and original teardown assertions unchanged.
Report request, provider and caller state when staging fails.

Checks: diagnostic reproduces both original staging failures (10.13 s);
final Linux focused proof 2/2 (0.25 s, 90 s command ceiling); separate no-run
build; focused Clippy -D warnings; fmt; boundary guard (954 inputs), its
34 self-tests; fuser provenance; record links and whitespace. Original
migration failure evidence is retained. No tests deleted.

Production LOC: 102162 -> 102162 (delta 0)
Scope: all first-party product Rust, shipped SQL and JavaScript; core 38241,
engine 56640, API 7281. Method: tools/production_loc.py --root SNAPSHOT --json
on exact first-parent and staged-tree archives with the same counter;
comments and all tests excluded. Exact trees and counter hash retained in
registration artifacts/loc.json. Test/docs-only change.
Reference absent (retired R9c; last counted 65,417 at tree 498dd19).
api/ now holds only the published JavaScript packages; both Rust crates
join the engine.

Moves:
- api/layerfs-api  -> engine/crates/layerfs-api
- api/binding      -> engine/crates/layerfs-node (addon now builds at
                      engine/crates/layerfs-node/layerfs.node)
- api/agent/sdk    -> api/agent
- api/agent/cli    -> api/cli
- api/how-to-use   -> api/docs
api/backend, api/runtime, api/scripts and api/package.json stay.

Relative and repository paths are rewritten in code, manifests, tests,
skills, plugin install guides and maintained docs. Historical records
(core/docs/issues, docs/versioned, docs/release-notes, docs/research)
are left unchanged.

Tooling follows the move: Cargo members are core/crates/* and
engine/crates/*; the guard and the LOC counter classify layerfs-api and
layerfs-node as engine by crate name, in both the old and new layouts;
the sealed bundle (copy_sdk) mirrors the new api/ layout; CI path filters
exclude engine/crates/layerfs-node (the binding stays outside CI);
layer guides (root, engine, api AGENTS.md and api/README.md) updated.
fixture_seal's scan roots drop the old api crate paths, now under engine.

Verified: cargo metadata unchanged (15 packages, same edges/targets);
cargo +1.85.1 check --workspace --all-targets and fmt --check pass;
guard PASS (954 files, --skip-sdk 818); 155 Python tool tests pass;
Ruff 0.17.2 format clean, no new lint findings; agent, backend, runtime,
packaging (dispatch with the addon built at its new path), trace and
exec_output stand-in tests pass; CLI 70/70. NOT_RUN on macOS: agent
binary_write, init_refusals and reinit (Linux binding + Store needed).

Deleted tests: none.

Production LOC: 102162 -> 102162 (delta 0)
Scope and method: tools/production_loc.py --root on git archive of the
parent (ff7ccee) and of the staged tree; per-file paths and counts
match (954 files). Subtotals by the counter's name-based classification:
core 38,241, engine 61,288, api 2,633. The parent's own counter recorded
engine 56,640 / api 7,281; the difference is layerfs-api (1,913) and
layerfs-node (2,735) reclassified to engine, a relocation, not a
simplification. Reference absent (retired R9c; last counted 65,417 at
tree 498dd19).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add the 2026-10-27 owner decision to the #463 implementation record:
layerfs-api and the Node binding move to engine/crates and api/ holds only
the JavaScript packages (commit b98ac4c), with its LOC and checks. Fix
three record links to the binding tests (now engine/crates/layerfs-node/tests)
and three migration-spec links to tools/ that the relocation broke.

Checks: every local link in core/docs/issues/463 resolves; whitespace.
Docs only; Rust/runtime checks do not apply. No tests changed.

Production LOC: 102162 -> 102162 (delta 0)
Scope: all first-party product implementation (core 38241, engine 61288,
api 2633). Docs-only change; tools/production_loc.py counts no docs.
Reference absent (retired R9c; last counted 65,417 at tree 498dd19).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yifanxuaaa
yifanxuaaa merged commit 3e5f3a6 into main Oct 26, 2026
4 checks passed
@yifanxuaaa

Copy link
Copy Markdown
Contributor Author

Merged as 3e5f3a627c. The merge commit's message has no production LOC line, so here is the first-parent comparison:

Production LOC: 102158 -> 102162 (delta +4)
First parent c137267f59, merge tree 3e5f3a627c. Method: tools/production_loc.py --root (counter at 3e5f3a627c) on git archive of both. Subtotals by crate name: core 38241 -> 38241; engine 61284 -> 61288; api 2633 -> 2633. The +4 is commit 0f442d5bb7 (Store Init composition moved into layerfs-project); everything else is relocation. Reference absent (retired R9c; last counted 65,417 at tree 498dd1917812).

yifanxuaaa pushed a commit to ImmuneFOMO/layerfs that referenced this pull request Oct 26, 2026
…phemeral-AI-Lab#456)

Production LOC: 102162 -> 102162 (delta 0)
core 38241 -> 38241, engine 61288 -> 61288, api 2633 -> 2633; reference
absent (retired R9c; last counted 65,417 at tree 498dd19). Counted
with tools/production_loc.py; docs only. Rebased onto main after Ephemeral-AI-Lab#465
moved api/how-to-use/ to api/docs/.
yifanxuaaa added a commit that referenced this pull request Oct 26, 2026
…456)

Says where LAYERFS_STORE_PATH points on each route (ImmuneFOMO, 36cf50b):
on Linux this process opens the Store, on macOS and Windows the worker does,
so the path is inside the worker's Linux. runtime.md adds what an agent
process needs on the worker route. The contributor tested macOS with
OrbStack; the Windows paths are read from source, not run. Rebased by the
maintainer onto main after #465 moved api/how-to-use/ to api/docs/.

Production LOC: 102162 -> 102162 (delta 0)
  First-parent comparison (main 939d68c -> this merge). core 38241, engine
  61288, api 2633, unchanged; reference absent (retired R9c; last counted
  65,417 at tree 498dd19). Counted with tools/production_loc.py;
  docs only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
yifanxuaaa pushed a commit that referenced this pull request Oct 26, 2026
Render the retained HTTP error prefix in ErrorDiagnostic Debug output so
SDK failures include the daemon's reason. Keep the capture bound and
metadata, escape controls and replace invalid UTF-8. Cover version
refusals, truncation and encoding, and document the v1.54 requirement.

Related to #457.

Validation: 32 sandbox tests; 57 Python tooling tests; all-target Clippy
for sandbox/API/Node with -D warnings; workspace formatting; real SDK
before/after checks on Docker 29.1.3 and 29.9.2 passed. The full boundary
guard retains four unchanged baseline SDK violations; core scope passes.

Production LOC: 102162 -> 102164 (delta +2)
Scope: first-party core, engine and API production source.
Core: 38241 -> 38241; engine: 61288 -> 61290; API: 2633 -> 2633; dsh
absent (moved out, #428). Reference absent (retired R9c; last counted
65,417 at tree 498dd19).
Method: tools/production_loc.py --root <snapshot> (blob 3341e4d) on git
archive snapshots of the first parent and committed trees:
b8b8ff3 -> 9f65159. Recounted by the maintainer after the rebase
onto main a9d5add (#465 moved the sandbox crate to engine/).
yifanxuaaa added a commit that referenced this pull request Oct 26, 2026
#465 moved api/how-to-use/agent.md to api/docs/agent.md; the rebased
README paragraph still linked the old path.

Production LOC: 102164 -> 102164 (delta 0)
core 38241, engine 61290, api 2633, unchanged; reference absent (retired
R9c; last counted 65,417 at tree 498dd19). Counted with
tools/production_loc.py; docs only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
yifanxuaaa added a commit that referenced this pull request Oct 26, 2026
SDK failures for Docker HTTP errors now show the retained bounded error
prefix (at most 1024 bytes, lossy UTF-8, controls escaped), so callers see
the Engine's reason, such as an API v1.54 version refusal (#457). The
owner accepted dropping the old redacted Debug for these diagnostics; docs
71 and 72 are updated. Request paths and single-attempt behaviour are
unchanged; Docker servers that reject v1.54 still fail.

Rebased by the maintainer onto main after #465 moved the sandbox crate to
engine/ and api/how-to-use/ to api/docs/, with one fix commit for the
README link. layerfs-sandbox: 32 tests pass on macOS (cargo 1.85.1), fmt
and Clippy -D warnings clean.

Production LOC: 102162 -> 102164 (delta +2)
  First-parent comparison (main a9d5add -> this merge). core 38241,
  engine 61288 -> 61290, api 2633; reference absent (retired R9c; last
  counted 65,417 at tree 498dd19). Counted with tools/production_loc.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
yifanxuaaa pushed a commit that referenced this pull request Oct 26, 2026
fs.stat answered a symlink's size as the length of the canonical object
that stores its target: the object envelope plus the LFS4LNK header, 27
bytes more than the target (a link to `a.txt` reported 32). A mounted
Workspace reports the target's length (base/view.rs stat_resolved and the
FUSE attributes), so the two views of one symlink disagreed.

snapshot::size now decodes the stored target with SymlinkTarget::decode,
as the Workspace base view does, and returns its byte length. The
operator file history and stats use the same helper and now count a
symlink the same way.

Tests: new layerfs-api tests/stat_symlink.rs (now under engine/ after #465) (a link and a dangling
link); it fails on main with `left: Some(Int(32)) right: Some(Int(5))`
and passes with the fix. The fs.stat reference in the layerfs-js-sdk
skill now says what `size` means for each type.

Verification (Linux arm64 VM, Rust 1.85.1): cargo fmt --all --check;
clippy -p layerfs-store-service -p layerfs-api --all-targets
-D warnings; test -p layerfs-store-service and each of the 46
layerfs-api test targets under a 120 s timeout: all passed. check_product_boundary.py reports the
same 4 violations as main, none in the changed files.

No tests deleted.

Production LOC: 102164 -> 102165 (delta +1)
Scope: first-party core, engine and API production source. Core: 38241
-> 38241; engine: 61290 -> 61291 (layerfs-store-service 10009 -> 10010);
api: 2633 -> 2633. Method: tools/production_loc.py --root <extracted
tree> (blob 3341e4d) on git archive of the first parent and the
committed tree: 13477d9 -> bb3f715. Reference absent (retired
R9c; last counted 65,417 at tree 498dd19). Recounted by the
maintainer after the rebase onto main a944016 (#465 moved the crates
to engine/).
yifanxuaaa added a commit that referenced this pull request Oct 26, 2026
fs.stat answered a symlink's size as the length of the object storing its
target (target + 27 bytes); it now decodes the target and returns its
length, as a mounted Workspace's lstat does. The operator storage and
revision answers use the same helper, so they count a symlink the same
way. No stored format changes.

Rebased by the maintainer onto main after #465 moved the crates to
engine/; the new test moved with it to engine/crates/layerfs-api/tests/.
Checked on macOS (cargo 1.85.1): stat_symlink passes and fails on main's
snapshot.rs with 32 vs 5; layerfs-store-service tests; layerfs-api
sdk_method_golden, diff_resume, sdk_compare, write_refusals,
sdk_reads_during_publish and init; fmt and Clippy -D warnings clean.

Production LOC: 102164 -> 102165 (delta +1)
  First-parent comparison (main a944016 -> this merge). core 38241,
  engine 61290 -> 61291 (layerfs-store-service 10009 -> 10010), api 2633;
  reference absent (retired R9c; last counted 65,417 at tree
  498dd19). Counted with tools/production_loc.py.

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

The Store binds no busy handler, so reclaim_space can be refused Busy without effect while the concurrent reader holds its snapshot. The test unwrapped that outcome and failed intermittently (4 of 80 local runs, and PR #402 CI). Treat Busy as the documented refused job and run a later one, bounded by JOBS.

No tests deleted; no assertion changed.

Production LOC: 102165 -> 102165 (delta 0)
Test-only. core 38241, engine 61291, api 2633, unchanged; reference absent
(retired R9c; last counted 65,417 at tree 498dd19). Method:
tools/production_loc.py --root on git archive of the first parent and the
committed tree (bb3f715 -> 7cf6e7b). Added by the maintainer
after the rebase onto main 3828268 (#465 moved the crate to engine/).
yifanxuaaa added a commit that referenced this pull request Oct 26, 2026
…amation

The Store binds no busy handler, so reclaim_space can be refused Busy,
without effect, when it meets the concurrent reader; the test unwrapped
that and failed intermittently (seen in #402 CI). The test now treats Busy
as the documented refused job and sends a later one, at most JOBS times;
other errors still fail and no assertion changed. Test-only; the owner
accepted this caller-side retry in a test as consistent with the
reclaim_space contract (a refused job is never replayed automatically,
a caller may issue another).

Rebased by the maintainer onto main after #465 moved the crate to
engine/, and the missing LOC line added. Checked on macOS (cargo 1.85.1):
immutable_reads_under_reclamation passes (1147 jobs during 89 read
rounds); all 36 layerfs-persistence test binaries pass (118 tests);
fmt and Clippy -D warnings clean.

Production LOC: 102165 -> 102165 (delta 0)
  First-parent comparison (main 3828268 -> this merge). core 38241,
  engine 61291, api 2633, unchanged; reference absent (retired R9c; last
  counted 65,417 at tree 498dd19). Counted with
  tools/production_loc.py; test-only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
yifanxuaaa pushed a commit that referenced this pull request Oct 26, 2026
A write call naming a path below a regular file or a symlink (/a.txt/x,
/dlink/x) walked into it: the content layer answered WrongLogicalRole, the
write route mapped it to Unknown, and an Unknown keeps its Store call
Workspace. fs.write/rm/edit/cp/mv each then held one, and after as many
such calls as the Store has call Workspaces every write was refused
Capacity. Mutation::resolve now refuses a name below a non-directory as
NotDirectory at that entry, as fs.mkdir on the same route already does, so
the call is a known refusal and its Workspace is closed. The SDK guide
lists NotDirectory among the shared write refusals.

Test added: layerfs-api/tests/write_refusals.rs
a_write_through_a_non_directory_is_not_directory. No tests deleted.

Production LOC: 102165 -> 102171 (delta +6)
Scope: first-party core, engine and API production source. Core: 38241
-> 38241; engine: 61291 -> 61297 (layerfs-store-service 10010 -> 10016);
api: 2633 -> 2633. Method: tools/production_loc.py --root on git archive
of the first parent and the committed tree (7cf6e7b ->
5c005e0). Reference absent (retired R9c; last counted 65,417 at tree
498dd19). Test and docs are not counted. Recounted by the maintainer
after the rebase onto main 41a848f (#465 moved the crates to engine/).

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

A write call naming a path below a regular file or a symlink (/a.txt/x,
/dlink/x) was refused Unknown (Content WrongLogicalRole), and an Unknown
keeps its Store call Workspace: 16 such calls refused every later write
with Capacity until the Store reopened. Mutation::resolve now refuses a
name below a non-directory as NotDirectory at that entry, as fs.mkdir
already does, so the call is a known refusal and its Workspace closes.
Symlinks are not followed by Store calls, as before.

Rebased by the maintainer onto main after #465 moved the crates to
engine/; LOC recounted. Checked on macOS with the pinned cargo 1.85.1:
write_refusals passes and fails on main's mutation.rs with
("Unknown", "Content(WrongLogicalRole)"); layerfs-api concurrent_commits,
method_refusal_order, lane_merge, sdk_chunk_deltas, sdk_method_golden
(write_cost is ignored by default); layerfs-store-service tests; fmt and
Clippy -D warnings clean. Known follow-ups from review: other read-only
resolve errors still map to Unknown, and a symlink to a directory in the
middle of a path is refused although the mount follows it.

Production LOC: 102165 -> 102171 (delta +6)
  First-parent comparison (main 41a848f -> this merge). core 38241,
  engine 61291 -> 61297 (layerfs-store-service 10010 -> 10016), api 2633;
  reference absent (retired R9c; last counted 65,417 at tree
  498dd19). Counted with tools/production_loc.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jiangkoumo added a commit to jiangkoumo/layerfs that referenced this pull request Oct 27, 2026
jiangkoumo added a commit to jiangkoumo/layerfs that referenced this pull request Oct 27, 2026
jiangkoumo added a commit to jiangkoumo/layerfs that referenced this pull request Oct 27, 2026
…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)
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.

D8 · Refactor core/ into core, engine and api layers

1 participant