Repository navigation
Area P: complete developer-facing interface and protocol suite (P1-P15) - #521
Draft
Mola-maker wants to merge 63 commits into
Draft
Mola-maker wants to merge 63 commits into
Mola-maker wants to merge 63 commits into
Conversation
Archive the approved design, execution ledgers, ultragoal plan, Docker inputs and current build MCP work in progress. Command adapter tests pass; stdio initialization remains unresolved and Task 2 is not complete. Production LOC: 102763 -> 102763 (delta +0); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Code <noreply@anthropic.com>
LayerFS.open(name, options?) and LayerFS.init(name, folder, timeout?, options?) accept one trailing options object. The constructor stays factory-gated. Settings resolve once at construction: explicit option wins, else LAYERFS_* env, else the built-in default; the result is a deep snapshot, so mutating the caller's object or the environment after construction changes nothing. deployment() no longer re-reads the environment. LAYERFS_TRACE_STEP and LAYERFS_TRACE_START stay per-call. An object in the timeout position stays refused, as do extra positional arguments; malformed options are refused InvalidInput before any binding call. Production LOC: 102763 -> 102817 (delta +54); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Code <noreply@anthropic.com>
…orrect) list_workspaces lost the Branch past 64 because the branch-name map in operator/inspect.rs was built from a single history page; the fix follows history continuations to build the complete map, including main, preserving the first-page output and moreBranches behavior. list_branches short pages were investigated and did not reproduce: the existing next_page (pos + items returned) was already correct, with a 1000-item request returning 85 items, hasMore true and nextPagePos 85, and continuation yielding all 130 branches exactly once, so no branch production code changed. Production LOC: 102763 -> 102779 (delta +16); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Code <noreply@anthropic.com>
Add branch.graph and branch.changed_paths across the JS SDK, CLI, and Rust service. graph returns the branch item, its forkPoint verbatim, and the bounded own-commit walk (stopping before a Commit forkPoint; a Layer or null forkPoint runs to the root), with per-commit published derived from main's current Layer stack, never stored. changed_paths returns one commit's path changes against its first parent, its base Layer for a parentless merge, or an empty tree for a true root commit. Unknown commits refuse NoSuchCommit; malformed or Layer identities refuse InvalidInput. No changes to list_commits, diff_commits, or diff_branches. Production LOC: 102763 -> 103075 (delta +312); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Code <noreply@anthropic.com>
P1: Node SDK history calls (layerfs#53)
P2: JS SDK constructor options (layerfs#484)
# Conflicts: # api/agent/test/area-p.mjs # api/agent/test/fixture/cases-area-p.mjs
P14: resolve list_workspaces Branch past 64 (branch paging verified correct)
workspace.commit accepts trailing { author?, labels? }: one author identity
slot plus free-form labels (order and duplicates preserved), bounded by
author/label 4096 bytes, at most 64 labels and 4096 bytes of labels JSON,
refused InvalidInput before any binding call. History reads return author
(null when unset) and labels (always an array); both are creation-time
annotations outside Commit identity, so verify_commit excludes them like
time_ms and a repeated derived id keeps its original attribution. The
commit table appends nullable author (index 17) and labels (index 18)
through an additive writable-open migration, and the per-item budget cost
becomes message + author + labels JSON + 1024 in list_commits and graph.
CommitRecord MAXIMUM_ENCODED_BYTES is corrected to the codec-true 12608
(the old 4510 doc figure overstated the old layout by 100), pinned by a
maximal-record width test.
Production LOC: 103145 -> 103518 (delta +373); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19).
Co-Authored-By: Claude Code <noreply@anthropic.com>
Upstream main advanced 9 commits from the 582bc78 baseline (stalled unmount recovery with exact abort custody, #500, plus mounted-branch snapshot resolution). Merged with 582bc78 as the content base (the private branch shares no git history with upstream); the single content conflict in operator/inspect.rs snapshot() combines both sides: the P14 full-history-continuation names map plus upstream's mounted-branch resolution for branches outside the page. Production LOC: 103145 -> 103213 (delta +68); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Code <noreply@anthropic.com>
P7: Commit details: author and labels (layerfs-connect#58)
fs.list/stat/read now work at a Commit or Layer identity, not only at a Branch head, via the existing branch wire arg; the binary flag returns bytes instead of text under the same line window and 48 KiB budget; the line scanner keeps raw byte prefixes with UTF-8 conversion only in text mode; refusal codes are exact (bad-tag InvalidInput, 66-nonhex NoSuchBranch, unknown Commit/Layer); the no-mount invariant is asserted in JS and real-store tests. Production LOC: 103586 -> 103642 (delta +56); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Code <noreply@anthropic.com>
P8: Read files at any Commit or Layer (layerfs-connect#59)
…fix P14 snapshot continuation
Retro finding 1: commit reply estimates omitted LWW decisions and merge
units, so a page could pass the 49,152-byte estimate and fail the control
codec, and an oversized unit was silently omitted. This adds a shared
recursive cost helper, charges actual encoded sizes on list_commits,
graph and the P14 snapshot, adds a single-item fallback for cursor
pagers, and discloses unitsTruncated when units are omitted wholesale.
It also fixes the review blocking finding: a rejected snapshot commit
previously pushed both restart (Null) and the provider cursor for the
same branch, a duplicate map key the control decoder rejects; it now
pushes exactly one continuation, with a regression test that fails on
the old code with ControlError("SDK field repeated").
Production LOC: 103642 -> 103679 (delta +37); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19).
Co-Authored-By: Claude Code <noreply@anthropic.com>
fix(store-service): charge LWW and merge units in commit reply cost; fix P14 snapshot continuation
A snapshot used to build the full Branch-name map before answering, following every Branch continuation page, so a cheap status call paid one provider page per ~81 Branches. Names are now seeded from the displayed page only (first 64 Branches); anything outside the page resolves lazily at the single site that needs it, through one new branch_name helper in operator/inspect.rs serving all three sites (mounted Workspaces, budgeted Layer sources, returned Commits' meta.branch). The memo lives for one call only: no persistent cache, no invalidation rule, no schema or transport change. Also carries the CI-hygiene fixes the private line needs before the next upstream batch, all zero-behavior: 1.85.1 rustfmt tree formatting (whitespace only), bool_assert_comparison lints in the P1/P14 tests, and the lineage-backfill old-store simulator updated for P7's commit author/labels columns. Production LOC: 103679 -> 103671 (delta -8); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Code <noreply@anthropic.com>
Retro finding 2: lazy Branch-name resolution in snapshots
Implements the approved P9 rev2 contract: 9 native event types on lf.events (commit/fs/branch/workspace/sandbox, same-client queue, Condvar long poll, 1024 overflow with stale resync) plus the runtime.project.subscribe_init Init-job stream. Explicitly does NOT fully close connect#60 (trace tail stays until P9-W1, macOS fine phases need P9-I1, no CLI stream); connect-side integration is a separate later step. Fresh review REVISE findings all fixed: explicit null refused for poll numerics, fake admission saturation asserted, native 1002-branch snapshot crosses the page boundary. Production LOC: 103671 -> 104551 (+880); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Code <noreply@anthropic.com>
P9: live event stream (native + runtime Init jobs)
graph(main) walked every Layer page and fetched every source commit before paging; non-main graphs built a history-sized HashSet first. Main now streams Layer pages with skip applied before commit fetch (only emitted records plus the more-triggering record are fetched); non-main uses a fallible Page::push with a bounded per-record HistoryCatalog::is_published check (exact source via layers_source, current-chain membership via lineage heights; absorbed parents stay published=false). Skip, limit, budget and more preserved; no API, schema or transport change. Fresh diff review APPROVE, zero BLOCKING. Production LOC: 104551 -> 104564 (+13); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Code <noreply@anthropic.com>
Retro finding 3: stream graph history instead of materializing it
Provider pages previously packed by the constant maximal encoded width (12,608 bytes), so every page held at most one Commit record. Pack by each record's exact encoded width, computed by the same helper the codec uses, with 12,608 kept as the validation maximum. Page assembly stops before the 16,208-byte available budget; the overflowing record is excluded with a valid continuation. Mid-page snapshot rejection now resumes at the rejected Commit instead of restarting the Branch, since multi-record pages make finding-1's restart-only path repeat already-emitted records. First-record rejection still returns exactly the Null restart continuation. Evidence: EXPLAIN gives SEARCH commit USING PRIMARY KEY (commit_id=?); tiny 16 records 16 -> 1 provider pages (statements 110 -> 20, rows 78 -> 18); mixed 4 records 4 -> 2 pages (statements 26 -> 14, rows 18 -> 10); maximal records still one page each. Production LOC: 104564 -> 104711 (delta +147); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Code <noreply@anthropic.com>
Retro finding 4: pack Commit-history provider pages by actual encoded size
The shared difference engine already computed a keyset cursor, but changed_paths exposed only numeric nextPagePos, so multi-page enumeration rewalked from the start: O(C^2/page-size) walked changes. changed_paths now uses the shared diff_page cursor parsing and returns nextCursor, mirroring diff_commits; Rust and JS accept an optional cursor, and the CLI passes --cursor through. Precedence when both are given: cursor wins, matching diff_commits through the existing emit behavior; numeric-only calls keep their previous items, hasMore, and nextPagePos. Malformed cursors refuse exactly as diff_commits refuses them; a well-formed unknown absolute path is not a refusal in diff_commits and is not one here either. Evidence: nested-fixture visited-work proof — cursor object batches/IDs 36/36 -> 71/71 (linear, 71 <= 2x36); pagePos 58/58 -> 147/147 (superlinear, 147 > 2x58). Flat cursor counters retained (statements 39 -> 65, rows 24 -> 40, object reads 9 -> 15). Cursor precedence verified with nonzero conflicting pagePos 14. Production LOC: 104711 -> 104700 (delta -11); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Code <noreply@anthropic.com>
Retro finding 5: changed_paths keeps its keyset cursor
Launch prompts for everything after retro finding 5 (finding 6, finding 7 design, finding 8, P10-P15, connect integration plan, final local-test gate) plus the remaining-task list, so the work can continue from the repo itself without relying on local brief files. Finding 7 is design-only; P15 is preparation-only (no publication); connect integration needs a separate user approval; P3 push/pull stays blocked by R7 #42 and must never be simulated. Production LOC: 104700 -> 104700 (delta 0, docs only); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Code <noreply@anthropic.com>
The read scanner visited lines only after LF or EOF completed them, and fs.read checked endLine and remaining reply bytes only inside that visitor, so once earlier output filled the 48 KiB budget the scanner still read the whole following line. A new crate-local lines_until predicate stops unreturned lines against endLine and remaining reply bytes, at new lines and during incomplete-line scans; the existing lines behavior remains as a wrapper so blame and other callers are unchanged. Lines being returned truncated are still fully classified: budget stopping engages only once spent > 0. Evidence: terminated huge next line 4,079,997 bytes / 17 reads -> 753,896 / 3; unterminated 4,079,990 / 17 -> 753,896 / 3; replies remain first\n, endLine=1, nextLine=2, correct truncation flags. Counters sum encoded pack/payload bodies and scoped acquisition bytes, not logical reply content; the 256 KiB delivery block and whole-pack accounting explain the fixed number. Production LOC: 104700 -> 104739 (delta +39); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Code <noreply@anthropic.com>
Retro finding 6: fs.read stops scanning once the reply budget is full
Numeric line-only reads rescan from byte zero on every page, so sequential traversal costs O(B^2/W) content bytes. An additive fs.read offset cursor (lfr1.<mode>.<line>.<byteOffset>.<contentRootHex>.<pathHex>) lets each page start at the first unreturned line's byte, with nextOffset returned beside nextLine. Binding (after snapshot/path resolution): normalized path, immutable content root, text/binary mode, offset <= file size; same path and content continue across a Commit or Layer, other path/changed content/mode swap/oversize offset refused with exact InvalidInput messages and null detail. A valid cursor takes precedence over startLine while numeric arguments stay validated; endLine stays absolute. Suffix scans keep absolute 16 KiB window numbering, so later-page NUL/invalid UTF-8 still returns exact Binary. No Store schema, transport, node, Python, or MCP change; no cross-call cache. Evidence: 2 MiB traversal 43 pages / 1,663,637 bytes, 4 MiB 86 pages / 3,221,324 bytes (<= 3x), 4 MiB final page 15,327 bytes (< 2,000,000 bound). Traversal bytes flow via range acquisition, so the reads ratio (0 <= 0) is vacuous; bytes ratio and late-page bound carry the proof. Production LOC: 104739 -> 104896 (delta +157); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Code <noreply@anthropic.com>
Per-suite PROGRESS logs (finding 6, 7, 8 and earlier slices) archived under logs/; the approved finding-7 design doc archived alongside the briefs. README pipeline and work log updated: user ordered kimi STOPPED 2026-10-30, so all further slices proceed commander-direct with no kimi sessions until the user re-enables kimi. Raw kimi session logs stay local only. Production LOC: 104899 -> 104899 (delta 0); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19).
client.results(after?) exposes each call's method, client-side duration, error code, and for writes the Commit, bytes added or refusal. One record per call a trace file would record (agent, backend, init, open lookup, unsubscribe), appended in completion order at the traced() boundary, whether or not LAYERFS_TRACE is set. The client keeps its most recent 1024 records, drops oldest without reusing sequence numbers, and reports evicted plus stale when a cursor reaches into dropped records. Reading is local and synchronous: no binding call, readable after close(). Arguments, result ids, queue attribution and daemon telemetry stay in the trace file. No CLI command (one process per command, like the event stream). Contract beside the P6 cases in api/agent/test/fixture/area-p-contracts.md; docs in agent.md. Contract review: commander self-review per the kimi-stop pipeline. Production LOC: 104899 -> 104954 (delta +55); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Kimi Code <noreply@anthropic.com>
P10: per-call results as an API (layerfs-connect#61)
…erfs-connect#62)
backend.store.project.get_storage gains two additive figures on the
existing call. `disk: { main, wal, total }` measures the Store's main
SQLite file and its -wal file from the filesystem (two stats, O(1);
the transient -shm index is excluded). `get_storage({ commits: true })`
adds `commits: { Workspace, Files, Merge }`, each with count and the
sums of every Commit's own newCanonical/newStored/newMetadata, by
CommitVia over the whole commit catalog: ONE grouped SQL scan,
O(number of commits), never presented as cheap. Absent kinds are
zero-filled; per-commit snapshot `logical` is deliberately not summed.
Unknown option keys, a non-object options argument and a non-boolean
`commits` refuse InvalidInput at both the JS wrapper and the operator.
Figures are computed fresh from the real Store every call, never
estimated, sampled or cached; an unmeasurable Store file fails the
call rather than returning partial statistics. No Store schema change:
one new query file. Contract beside the P6 cases in
api/agent/test/fixture/area-p-contracts.md; docs in api/docs/backend.md.
Contract review: commander self-review per the kimi-stop pipeline.
Production LOC: 104954 -> 105106 (delta +152); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19).
Co-Authored-By: Claude Code <noreply@anthropic.com>
P11: store statistics: disk, WAL and per-Commit-kind byte totals (layerfs-connect#62)
…t#63)
backend.store.project.get_summary returns a cheap, bounded Store
summary companion to P11's get_storage. Exactly five fields:
{ logical, branches, commits, mainLayer, lastCommit }. Computed
fresh from the real Store under a single read transaction via five
index-backed queries (store_totals rowid lookup, branch_names
covering index count, commit_identity covering index count, main
branch point lookup, and main commit point lookup). SummaryBound
invariant: exactly 7 SQLite statements (BEGIN, 5 queries, COMMIT)
and 5 returned rows regardless of store size or history depth.
Refuses every options argument with InvalidInput. Contract beside
the P6 cases in api/agent/test/fixture/area-p-contracts.md; docs in
api/docs/backend.md.
Production LOC: 105106 -> 105250 (delta +144); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19).
Co-Authored-By: Claude Code <noreply@anthropic.com>
P12: storage summary: cheap project summary companion (layerfs-connect#63)
…c cwd (layerfs-connect#64)
LayerFS.open(name, { scope: { folder, branch? } }) confines a
client to a project-root relative directory and a dedicated branch
namespace prefix. File read/write/edit/mkdir/chmod/rm and two-path
cp/mv operations refuse InvalidInput ("out of scope: path") when any
path escapes folder. Branch operations compose as <prefix>.<name>
and refuse InvalidInput when given a prefixed name or an attempt
to escape. History-returning operations, event subscriptions, and
backend handles refuse InvalidInput. workspace.mount binds
<prefix>.main and workspace.exec injects folder as cwd. Cwd
resolution in layerfs-api validates path boundaries and rejects
escapes. Contract beside P6 cases in
api/agent/test/fixture/area-p-contracts.md; docs in api/docs/agent.md.
Production LOC: 105106 -> 105284 (delta +178); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19).
Co-Authored-By: Claude Code <noreply@anthropic.com>
P13: folder-scoped client: path confinement, branch namespace and exec cwd (layerfs-connect#64)
…protocol (layerfs-connect#55) Implements Python SDK per design decision D10 with exact parity across all public LayerFS operations (project, branch, fs, workspace, backend). Dual transport: direct Store access on Linux via PyO3 0.30.1 extension (_native) opening StoreService directly in-process; authenticated loopback worker protocol on macOS/Windows. P6 contract parity suite passes against Python client; 10/10 unit tests pass on real Linux ext4 Store (/var/tmp) under Disposable profile. Includes client.results per-call tracking and get_storage options. Contract beside P6 cases in api/agent/test/fixture/area-p-contracts.md. Production LOC: 105106 -> 105766 (delta +660); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Code <noreply@anthropic.com>
P4: Python SDK: dual transport, PyO3 native Linux binding and worker protocol (layerfs-connect#55)
…layerfs-connect#56) Exposes the complete LayerFS surface over stdio JSON-RPC per design decision D11 using @modelcontextprotocol/server 2.4.0 and zod 4.6.7 pinned without registry access. Exposes 50 strict-schema tools covering agent, backend store, and backend daemon operations with complete input/output Zod schemas. Enforces single active client lifecycle, rejects unmounted workspaces, cleans up server-created workspaces on close or EOF, and normalizes errors onto typed LayerFSError codes. Passes 8/8 protocol tests and 1/1 real-Store Disposable scenario on Linux ext4 (/var/tmp). Contract beside P6 cases in api/agent/test/fixture/area-p-contracts.md; docs in api/docs/mcp.md. Production LOC: 105106 -> 105456 (delta +350); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Code <noreply@anthropic.com>
P5: MCP server: stdio JSON-RPC server and typed tools over Node SDK (layerfs-connect#56)
…ll verification (layerfs-connect#66) Prepares @layerfs/* packages for npm publication (metadata, versioning 1.1.0, licenses, READMEs, files manifests, and offline tarball smoke tests) without performing publication or accessing the npm registry. Configures unified package versions and repository metadata; removes redundant api/cli/package-lock.json; adds api/scripts/prepare-publish.mjs which validates tarball file manifests, installs from tarballs into a temporary directory offline, verifies module entrypoint imports and CLI executable execution. Documents coordinated versioning policy in docs/general/release-policy.md. Production LOC: 105106 -> 105106 (delta 0); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Code <noreply@anthropic.com>
P15: npm packaging preparation: metadata, licenses, READMEs and tarball verification (layerfs-connect#66)
Production LOC: 106438 -> 106438 (delta 0); core+engine+api via python -I tools/production_loc.py --json; reference absent (retired R9c; last counted 65,417 at tree 498dd19). Co-Authored-By: Claude Code <noreply@anthropic.com>
… path handling - engine/crates/layerfs-python: add get_summary() to StoreProject for P12 summary API parity across Node and Python SDKs. - engine/crates/layerfs-python/tests: activate real-Store summary verification on Linux (test_project_summary: 10/10 tests PASS). - tools/check_product_boundary.py: normalize path comparisons to POSIX and use explicit UTF-8 encoding across Windows and POSIX hosts. - .gitignore: ignore transient .omc directory across all worktrees. Production LOC: 106438 -> 106438 (delta 0) Reference absent (retired R9c; last counted 65,417 at tree 498dd19) Scope: production Rust/SQL/JS counted via tools/production_loc.py Co-Authored-By: Claude Code <noreply@anthropic.com>
…mentation - docs/general/pull-request-area-p.md: document full architecture, public interfaces, and verification evidence across Node, Python, MCP, and CLI. - detail persistence invariants (Disposable/WAL/OFF), schema stability, and real ext4 POSIX locking test runs. - record upstream PR submission readiness and #505 merge gate status. Production LOC: 106438 -> 106438 (delta 0) Reference absent (retired R9c; last counted 65,417 at tree 498dd19) Scope: production Rust/SQL/JS counted via tools/production_loc.py Co-Authored-By: Claude Code <noreply@anthropic.com>
…namespacing (P13 parity) - engine/crates/layerfs-python/python/layerfs/_scope.py: scope validation and per-call confinement shared by named methods and client.call, mirroring @layerfs/agent: canonical folder subpaths, scoped/<prefix>/<branch> refs, Commit/Layer identity preservation, history and events refusal, workspace mount/exec binding and namespace-filtered Branch listing. - _client.py: accept scope in LayerFS.open options, refuse it in init, hide backend on scoped clients, and page the complete Branch namespace before applying the caller's page. - _methods.py: scoped workspace.mount selects namespace main and refuses any caller-supplied Branch; workspace.exec dispatches through the scoped path. - tests/python/test_scope.py: 9 scenarios over the shared fake binding. Verified (WSL2 Linux, py314): - python3 -m unittest discover tests/python: 19/19 PASS. - node api/agent/test/area-p13-scope.mjs: 14/14 PASS. - node api/agent/test/area-p.mjs: 241 cases PASS; calls.mjs: 66/66 PASS. - python -B tools/check_product_boundary.py: PASS (982 files). Production LOC: 106438 -> 106438 (delta 0) Reference absent (retired R9c; last counted 65,417 at tree 498dd19) Scope: production Rust/SQL/JS counted via tools/production_loc.py; the Python SDK product source under layerfs-python/python is outside the pinned counter and this change is Python-only. Co-Authored-By: Claude Code <noreply@anthropic.com>
…R brief - describe the three resolved review findings: scoped workspace.mount raw-dispatch parity, Python SDK folder-scope support, and the MCP/Python P12/P13 contract reconciliation. - update the verification matrix with the Python scope suite and 19/19 Linux results, and correct the P13 namespace form to <prefix>.<branch>. Production LOC: 106438 -> 106438 (delta 0) Reference absent (retired R9c; last counted 65,417 at tree 498dd19) Scope: production Rust/SQL/JS counted via tools/production_loc.py; docs-only change. Co-Authored-By: Claude Code <noreply@anthropic.com>
- replace the #505 merge gate note with the actual submission state: the branch is pushed to Ephemeral-AI-Lab/layerfs and tracked as draft PR #521 while batch 1 (#505) remains open. Production LOC: 106438 -> 106438 (delta 0) Reference absent (retired R9c; last counted 65,417 at tree 498dd19) Scope: production Rust/SQL/JS counted via tools/production_loc.py; docs-only change. Co-Authored-By: Claude Code <noreply@anthropic.com>
- use host-resolved path for init fixture in cases.mjs so SDK and CLI agree on Windows and Linux - use pathToFileURL for dynamic fake native imports in packaging.mjs and runtime.mjs to support Windows absolute paths Production LOC: 106438 -> 106438 (delta 0) Reference absent (retired R9c; last counted 65,417 at tree 498dd19) Scope: production Rust/SQL/JS counted via tools/production_loc.py; test-only change. Co-Authored-By: Claude Code <noreply@anthropic.com>
…t results - document pass counts for 44 Area P tests in layerfs-api, 24 SDK fast targets, 36 persistence targets, and 9 daemon fast lane targets - record 7/7 Docker node:24 packaging tests and 13/13 WSL2 runtime cases - state explicit NOT RUN rationales for FUSE mount lane, Durable profile, push/pull synchronization, and Windows real workspace mount Production LOC: 106438 -> 106438 (delta 0) Reference absent (retired R9c; last counted 65,417 at tree 498dd19) Scope: production Rust/SQL/JS counted via tools/production_loc.py; docs-only change. Co-Authored-By: Claude Code <noreply@anthropic.com>
Fixes the format-lane failure reported on PR #521: wrap two over-length comprehension/call lines to the 120-column limit with ruff==0.17.2. Production LOC: 106438 -> 106438 (delta 0) Reference absent (retired R9c; last counted 65,417 at tree 498dd19) Scope: production Rust/SQL/JS counted via tools/production_loc.py; tooling-only change. Co-Authored-By: Claude Code <noreply@anthropic.com>
- publish.yml: packs and publishes @layerfs/{agent,backend,runtime,cli} via
npm trusted publishing (OIDC, no NPM_TOKEN), gated on the existing
prepare-publish offline tarball verification; tag or manual trigger,
idempotent on already-published versions.
- claude-review.yml: advisory automated PR review with
anthropics/claude-code-action, skipping drafts and bot actors; reports
comments only, never a required check (AGENTS.md section 4).
Production LOC: 106438 -> 106438 (delta 0)
Reference absent (retired R9c; last counted 65,417 at tree 498dd19)
Scope: production Rust/SQL/JS counted via tools/production_loc.py; CI-only change.
Co-Authored-By: Claude Code <noreply@anthropic.com>
Windows builds failed with E0433/E0599: two unguarded std::os::unix imports in install.rs and unconditional symlink_metadata/metadata reads in Backing::delete. Gate the Unix-only code behind cfg(unix)/cfg(not(unix)) exactly like the neighbouring claim() function, keep the non-Unix branches refusing with their platform reason (the kernel guard already refuses before them), and scope DAEMON_UID and the delete() body's identity checks to Unix. No Linux behaviour change: store-service tests and clippy -D warnings pass in WSL2 unchanged. Production LOC: 106438 -> 106450 (delta +12) Reference absent (retired R9c; last counted 65,417 at tree 498dd19) Scope: production Rust/SQL/JS counted via tools/production_loc.py. Co-Authored-By: Claude Code <noreply@anthropic.com>
…o unix The whole backend module is cfg(unix), so on Windows the re-export of failure::STOPPED and CommandIdentity::check are dead code and fail a warning-denying build. Gate both with cfg(unix) like their only callers, matching lib.rs's existing cfg(unix) on the backend module. Types stay portable; Linux behaviour unchanged (clippy -D warnings passes in WSL2). Production LOC: 106450 -> 106454 (delta +4) Reference absent (retired R9c; last counted 65,417 at tree 498dd19) Scope: production Rust/SQL/JS counted via tools/production_loc.py. Co-Authored-By: Claude Code <noreply@anthropic.com>
…cation The daemon binary failed to compile on Windows: config.rs imported std::os::unix and nix unconditionally. Split read() into a Linux implementation and a non-Linux refusal (the whole application is a Linux production surface, matching filesystem.rs's existing assemble pattern), scope protected_file/private_directory/secure_process/invalid and the Unix imports to Linux, and let cli.rs's run() surface the refusal. Linux behaviour unchanged: clippy -D warnings passes in WSL2. Production LOC: 106454 -> 106461 (delta +7) Reference absent (retired R9c; last counted 65,417 at tree 498dd19) Scope: production Rust/SQL/JS counted via tools/production_loc.py. Co-Authored-By: Claude Code <noreply@anthropic.com>
Mola-maker
force-pushed
the
area-p/complete-p1-p15
branch
from
October 31, 2026 04:49
4e3cae4 to
a9f818f
Compare
Completes the Windows rust-lsp/rustc 1.85.1 compatibility pass over the workspace (check --workspace: 0 errors, 0 warnings). The same items stay fully live on Linux; every cfg is proven by Linux clippy -D warnings: - layerfs-project import: refused()/BATCH/record_new_bindings/entry()/built() and the hints.rs imports, all called only by the unix-gated Init path - layerfs-api Control::clear_io_waits: called only by the unix-gated sandbox create handshake - layerfs-overlay Allocation fields: read by the macos/linux-only observation methods; the non-Unix constructor refuses - layerfs-store-service Backing device/inode: checked by the unix-only delete identity fence Checks: Windows cargo check (workspace excl. layerfs-node/-python) clean; Linux clippy -D warnings (workspace excl. layerfs-node/-python) clean; fmt --check clean; project+overlay test suites pass. Production LOC: 106461 -> 106480 (delta +19) (tools/production_loc.py, first-party core+engine+api product src; reference absent (retired R9c; last counted 65,417 at tree 498dd19); delta is the cfg attributes and inline gate comments, cfg-gating adds no new logic) Co-Authored-By: Claude Code <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.
Area P: Complete Developer-Facing Public Interface & Protocol Suite (P1–P15)
Overview & Executive Summary
This Pull Request integrates the complete Area P development track into
Ephemeral-AI-Lab/layerfs(main). Area P brings the LayerFS developer experience to full production readiness across Node.js, Python, Model Context Protocol (MCP), and the command-line interface (CLI).Across 15 planned increments (P1–P15) and targeted architectural refactors, Area P introduces:
@layerfs/agent), Python SDK (layerfs), and backend operator facades (@layerfs/backend).@layerfs/mcp) exposing 50 typed tools with runtime Zod schema validation over real Linux ext4 Stores.@layerfs/cli) with 106 verified commands matching SDK behavior verbatim.<prefix>.<branch>), and execution directory binding on@layerfs/agentand the PythonlayerfsSDK._native.so) and Node addon (layerfs.node) with dual-mode Linux ext4 and WSL2/worker loopback fallbacks.All operations preserve the invariant Disposable / WAL /
synchronous=OFFpersistence profile and strictly conform to SQLite schema identity and POSIX multi-process locking rules.Complete Increment Breakdown (P1–P15)
@layerfs/agent.layerfs-python) and HTTP loopback fallback.@layerfs/mcp)fs.read,fs.list,fs.stat) across historical Commit IDs and Layer IDs.bytesRead,bytesWritten,statements,costUnits).branches,commits,mainLayer,lastCommit,logicalBytes).@layerfs/agentand mirrored in the PythonlayerfsSDK.Architectural Guarantees & Constraints
synchronous=OFFis preserved universally across all newly introduced APIs.idx_commits_branch,idx_layers_commit).as_posix(),Path.resolve()).--no-runprecompilation.Code Review Resolutions
Three findings from the cross-language review are resolved in this branch:
workspace.mountraw-dispatch parity.client.call("workspace.mount", { branch: "main" })was previously accepted on scoped@layerfs/agentclients even though the named method and the P13 contract refuse every caller-supplied Branch.scopeArgsnow refuses any supplied branch other than the implicitmainand re-namespaces it to<prefix>.main; the same rule is applied in the Python SDK, including an explicitnull. Both named and raw paths raiseInvalidInput: out of scope: branchbefore dispatch.layerfsSDK now acceptsLayerFS.open(name, { scope: { folder, branch? } })with the same semantics as@layerfs/agent: canonical folder confinement for every path-bearing method,<prefix>.<branch>refs with Commit/Layer identity preservation, refusal of path-returning history calls andevents.subscribe,backendhidden on scoped clients,workspace.mountselecting the namespacemain,workspace.execbindingcwd, and namespace-filtered Branch listing that pages the complete namespace before applying the caller's page.LayerFS.initrefusesscope.backend.store.project.get_summary()on both@layerfs/agentand the Python SDK.@layerfs/mcpmirrors the@layerfs/backendoperator surface (get_history,get_storage,get_comparable,list_overwritten, …) and deliberately adds no separate summary tool; P13 scope is a client-construction option that MCP's strictoptionsschema does not accept, so an MCP request carryingscopeis refused at schema validation rather than silently ignored. MCP and CLI therefore remain unscoped clients by contract, not by accident.Production LOC Accounting
Production source code lines measured using native Windows Python and
tools/production_loc.py:498dd1917812)Verification Evidence & Test Results
1. Rust Workspace Test Results (WSL2 Linux ext4 / mold 2.40.4)
layerfs-api/tests/area_p*.rs): 44/44 tests PASS (0.10s–0.39s each):area_p1_history: 8/8 PASSarea_p1_graph: 2/2 PASSarea_p7_attribution: 11/11 PASSarea_p8_reads: 8/8 PASSarea_p11_statistics: 3/3 PASSarea_p12_summary: 4/4 PASSarea_p14_paging: 8/8 PASSlayerfs-api/tests/sdk_*.rs): 24/24 target suites PASS.layerfs-persistence/tests/*.rs): 36/36 test targets PASS (includingwriter_admission11/11,sqlite_lock_custody,serverless_store).layerfs-daemon/tests/*.rs): 9/9 fast targets PASS (file_digests,host_handoff,host_install,installed_store,rename_predecessor,shared_cache_pressure,store_reader_lifecycle,telemetry_recording,workspace_databases).layerfs-content,layerfs-storage,layerfs-history,layerfs-telemetry): 100% PASS.layerfs-overlay,layerfs-workspace,layerfs-project,layerfs-sandbox,layerfs-bridge,layerfs-store-service,layerfs-fuse): 100% PASS.cargo +1.85.1 fmt --all -- --check: PASS (clean).cargo +1.85.1 clippy --locked --workspace --exclude layerfs-node --all-targets -- -D warnings: PASS (0 warnings across workspace).2. JavaScript Suite & Package Verification
api/agent/test/area-p.mjs): 241/241 cases PASS.api/agent/test/calls.mjs): 66/66 calls PASS.api/cli/test/cli.test.mjs): 106/106 commands PASS (verified on Windows & WSL2 Linux).api/mcp/test/stdio.test.js): 8/8 tests PASS.api/mcp/test/real-store.test.js): 1/1 test PASS on real SQLite Store.area-p10-results.mjs,area-p11-storage.mjs,area-p12-summary.mjs,area-p13-scope.mjs).api/agent/test/packaging.mjs): 7/7 cases PASS innode:24.21.0-bookwormcontainer (surface,lifecycle,options,copy,sandbox,worker-route,transport-order).api/runtime/test/runtime.mjs): 13/13 cases PASS in WSL2 Linux (surface,home-mode,projects,init,state,events,leftovers,sandboxes,volumes,transport-order,worker-frames,docker-default,worker-docker).prepare-publish.mjs): Verified offline tarball packing and import smoke test for@layerfs/agent,@layerfs/backend,@layerfs/runtime, and@layerfs/cli.3. Python SDK & Linux Native Store Tests
engine/crates/layerfs-python/tests/python/test_*.py): 19/19 PASS on Linux py314 under WSL2.test_scope.py): 9/9 scenarios PASS — scope validation, path confinement before dispatch, Commit/Layer ref preservation, history/events refusal, Branch mutation confinement, namespace-filtered paging, Workspace mount/exec binding and unchanged unscoped shapes.test_real_store.py): 2/2 tests PASS on/var/tmpext4 (verified Store project summary, branch fork, and storage statistics).4. Repository Boundary, Tooling & Integrity Checks
tools/check_product_boundary.py): PASS (scanned 982 Rust/SQL/JS production files with 0 violations).tools/check_fuser_integrity.py): PASS (34 manifests, locks, and authorized fuser patches verified).python3 -B -m unittest discover -s tools -p 'test_*.py'): 155/155 tests PASS.5. Explicit NOT RUN Declarations & Rationale
/dev/fuseand fresh Docker containers withSYS_ADMINcapability (e.g.stalled_unmount,complete_installed_roots,kernel_mount_routes). Governed by CI nightly runner pertools/ci_tests.pyandAGENTS.md§4; sealed E2E launcher intentionally not exercised in local workspace.synchronous=OFFis the sole authorized profile.PR Submission Readiness & Next Steps
Target branch:
upstream/main(Ephemeral-AI-Lab/layerfs).Integration branch:
area-p/complete-p1-p15(based onarea-p/final-review).Submission status:
Ephemeral-AI-Lab/layerfsand tracked as draft PR#521.
(
area-p/batch-1: P2, P14, P1, P7, P8 and the commit cost-accounting fix).This integration branch carries those increments in their original slice
commits, so the PR stays a draft until Area P batch 1: P2, P14, P1, P7, P8 and commit cost accounting fix #505 merges; it will then be rebased
to show only P4, P5, P9–P13, P15 and the integration work, and marked ready
for review.
🤖 Generated with Claude Code