Repository navigation
Conversation
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: 109,056 -> 109,062 (delta +6) Scope and method: tools/production_loc.py --root on the parent and the staged tree; layerfs-store-service 10,009 -> 10,015; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Test and docs are not counted. 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>
Contributor
|
Merged into main as fbecc17, thanks! #465 moved the crates to |
yifanxuaaa
added a commit
that referenced
this pull request
Oct 26, 2026
A write call's view reads (resolve's stat and lookup, the file read of fs.edit, and the list, stat and readlink of recursive rm and cp) take the overlay read-only, so they change nothing. Their engine errors still went through failure() to Unknown, and files.rs keeps the call Workspace of an Unknown: 16 calls that failed reading the Store (a damaged object, for example) refused every later write with Capacity until the Store reopened. This is the class #435 fixed for one case (a name below a non-directory, now NotDirectory). read_failure() maps such an engine error to a known Failed, so files.rs closes the Workspace. An Unknown from a step that applies a change, sets up the call or reserves serials still keeps its Workspace. The layerfs-js-sdk fs.write guide lists Failed among the shared refusals. Test added: layerfs-api tests/write_refusals.rs a_failed_read_keeps_no_store_call_workspace damages the stored file contents of a closed test Store out of band (metadata, which a bind reads, shares one pack with the lookup's objects, so a lookup alone cannot be damaged), then makes 17 fs.edit calls: each is refused Failed for the read. On main it fails with Unknown, and without that first check call 16 is refused "Capacity: Store call Workspace capacity". No tests deleted. Checks on macOS with cargo 1.85.1: layerfs-api write_refusals, concurrent_commits, method_refusal_order, lane_merge, sdk_chunk_deltas, sdk_method_golden, diff_resume and sdk_compare; layerfs-store-service tests; fmt and Clippy -D warnings clean. Production LOC: 102171 -> 102187 (delta +16) Scope: first-party core, engine and API production source. Core: 38241 -> 38241; engine: 61297 -> 61313 (layerfs-store-service 10016 -> 10032: read_failure, and rustfmt splitting three calls that grew past the line width); api: 2633 -> 2633. Method: tools/production_loc.py --root on git archive of the first parent and the staged tree (5c005e0 -> 4ccb640). Reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
yifanxuaaa
added a commit
that referenced
this pull request
Oct 26, 2026
… no Store call Workspace Follow-up to #435. A write call's view reads (resolve, the fs.edit file read, and the list/stat/readlink of recursive rm and cp) change nothing, but their engine errors were Unknown, and an Unknown keeps the call's Store call Workspace: 16 calls that failed reading the Store refused every later write with Capacity until the Store reopened. They are now a known Failed, so the Workspace closes; an Unknown from a step that applies a change still keeps it. Checked on macOS with cargo 1.85.1: the new write_refusals test passes, and on main it fails with Unknown (call 16 Capacity); eight layerfs-api write-route targets and the layerfs-store-service tests pass; fmt and Clippy -D warnings clean. Production LOC: 102171 -> 102187 (delta +16) First-parent comparison (main fbecc17 -> this merge). core 38241, engine 61297 -> 61313 (layerfs-store-service 10016 -> 10032), 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #372. It was closed as completed on 2026-10-21, but it still reproduces on
main8e2fce26; #415 changes another branch offailure()(Capacity) and does not cover this path.Problem
A write call whose path goes below a regular file or a symlink (
/a.txt/x,/dlink/x, wheredlink -> dir) is refused as an engine failure:Mutation::resolvelooks a name up under whatever the previous entry was. The content layer answersWrongLogicalRole, andfailure()maps that toUnknown.files.rskeeps the Store call Workspace of anUnknown(by design, since its outcome is uncertain). As a result, each such call holds one slot. After as many of them as the Store has call Workspaces (OwnerConfig::default().namespaces, 16), everyfs.write/rm/mkdiris refusedFailed | Capacity: Store call Workspace capacity. Reads are unaffected and nothing is committed. (From readingfiles.rs, the slots are only returned when the Store is reopened; I did not run that part.)The same inputs already get a precise answer elsewhere.
fs.mkdiron the write route returnsNotDirectoryat the blocking entry, and reads returnNoSuchPath.Fix
Mutation::resolverefuses a name below an entry that is not a directory asNotDirectory, with the path of that entry as the message. This is exactly howfs.mkdiranswers on the same route. A symlink is not followed, as before. The call becomes a known refusal, sofiles.rscloses its Workspace. Production change: +6 lines inwrite/mutation.rs.Why
NotDirectory, notNoSuchPath:Mutation::optionalreadsNoSuchPathas "absent, create it". WithNoSuchPath,write /a.txt/xwould go on to the create path and fail differently depending on depth.NotDirectorymatches the existingfs.mkdiranswer for the same input on the write route, and POSIXENOTDIR.Not changed: a genuinely uncertain
Unknownstill keeps its Workspace. That custody rule is untouched. This PR only stops a certain refusal from being reported asUnknown.Tests
layerfs-api/tests/write_refusals.rs::a_write_through_a_non_directory_is_not_directory:fs.write,rm,mkdir,edit,cpandmvthrough a file or a symlink return("NotDirectory", "<blocking entry>").namespaces + 1more such calls that are notfs.mkdir(mkdir never keeps a Workspace), then requires a validfs.writeto succeed. Without the fix, these calls use up all 16 Workspaces and that write is refusedCapacity.On
main8e2fce26without the fix (only the new test added), it fails:With the fix, it passes.
Run locally on this branch, rebased on
main8e2fce26(Linux, Rust 1.95.0,--locked --offline, Disposable fixture, no mount):cargo test -p layerfs-store-service --all-targetspasses.cargo test -p layerfs-api --all-targetspasses (104 passed, 15 ignored).cargo clippy -p layerfs-api -p layerfs-store-service --all-targets --no-deps -- -D warnings -A clippy::result_large_err -A clippy::unnecessary_sort_bypasses. The two allowed lints are pre-existing (store_service.rs,examples/issue367_rm_probe.rs); neither is in the changed files.rustfmt --checkpasses on the changed files.The SDK guide (
fs-write.md) now listsNotDirectoryamong the shared write refusals.Not checked: the pinned Rust 1.85.1 (not installed here), the whole workspace,
cargo fmt --alland the boundary guard. Upstream CI has not run on this branch yet; all results above are local.Production LOC: 109,056 -> 109,062 (delta +6),
tools/production_loc.py --root.🤖 Generated with Claude Code