Build the Vfs foundation and pivot the executor to VfsRef - #34
Closed
vinniefalco wants to merge 3 commits into
Closed
Build the Vfs foundation and pivot the executor to VfsRef#34vinniefalco wants to merge 3 commits into
vinniefalco wants to merge 3 commits into
Conversation
Plan: vibe/2026-09-07-2-gateway-tts-phase-1.md
Introduces a new zero-dependency crate that sits permanently at the bottom of the dependency stack, holding the generic virtual filesystem machinery: one shared error type, the value types backends exchange, and canonical interned virtual paths. Paths are normalized and interned the moment the API receives them, so claim lookups stay pointer-cheap and aliases cannot slip past the claims tables. The only way to form a path is through a crate-private canonicalizer, so canonicalization at receipt is enforced by visibility rather than convention. A self-policing test reads the crate manifest and fails if any dependency table gains an entry. - `crates/shared-vfs/Cargo.toml` declares an empty `[dependencies]` table carrying the zero-dependency rule in comments; `the_manifest_declares_no_dependencies` reads the manifest through `CARGO_MANIFEST_DIR` and fails if any dependency table carries an entry. - `VfsPath` wraps an interned id so equality is an integer compare; `canonicalize` is crate-private, making it the only way to form one. - `Interner` is hand-rolled on std behind a poison-safe `OnceLock` mutex; each string is leaked once, so resolution is a vector index and a panicking writer cannot leave the tables half-updated. - `FileType` names all seven POSIX kinds with no catch-all, so an exotic backend node cannot be hidden. - `Stat` uses optional fields for mode, modified, and created, so a backend that does not track a field says `None` rather than fabricating a value. - `canonicalize` treats backslashes as separators, collapses duplicates, removes dot segments, pops exactly one segment per dotdot, and rejects traversal past the root plus relative and empty paths; case is preserved and significant. Ten tests cover the matrix and interning identity. - `canonicalize` and `Interner::intern` carry `#[allow(dead_code)]`: only tests call them today, with the production caller still to arrive. Design: new facade @ crates/shared-vfs/src/lib.rs boundary: pub Design: new global-state @ crates/shared-vfs/src/path.rs::interner Design: new newtype @ crates/shared-vfs/src/path.rs::VfsPath boundary: pub Design: new value-object @ crates/shared-vfs/src/path.rs::VfsPath boundary: pub Design: new newtype @ crates/shared-vfs/src/path.rs::VfsPathBuf boundary: pub Design: new value-object @ crates/shared-vfs/src/path.rs::VfsPathBuf boundary: pub Design: new value-object @ crates/shared-vfs/src/types.rs::FileType boundary: pub Design: new bag-of-state @ crates/shared-vfs/src/types.rs::GrepQuery boundary: pub Deferred: canonicalize and Interner::intern carry #[allow(dead_code)] with no production caller until Access arrives Plan: vibe/2026-09-11-3-vfs-foundation.md
vinniefalco
marked this pull request as draft
September 12, 2026 01:22
This change defines the backend trait pair behind the virtual namespace: one trait for a backend that vends and releases identity-bound sessions, and one carrying all sixteen filesystem operations with defaults for ranged reads, string replacement, and content search. It adds the policy hook consulted before every operation, the operation and verdict types it matches on, and an allow-everything policy as the initial implementation. Execution identities are opaque and vended from a process-wide monotonic counter, so every operation can be attributed to one serial thread of execution. The default search matches literal text only and refuses regular expressions, since the crate is std-only and a regex engine must come from an overriding backend. - `Vfs` - acquire and release are the only entry points; every operation lives on the acquired access object, so storage is unreachable without an identity. Send is required and Sync is not: the handle serializes access. - `VfsAccess` - sixteen methods, with defaults for read_range, str_replace, grep, and the POSIX extras keeping the required set at eleven. - `Verdict` - Deny and Ask carry reason strings: Deny's flows back to the model as its recovery path, Ask's is the approval dialog text. - `ExecId` - an opaque single-field wrapper with no public constructor; vend draws from a process-global counter and has no caller until the handle arrives. - `read_range` - the default reads the whole file and slices, clipping at the end and returning empty when the offset is past it. - `str_replace` - the default rewrites the unique occurrence; zero or multiple matches are errors and leave the file unchanged. - `grep` - the default globs, reads, and line-scans with literal substring matching; regex queries return Unsupported, non-UTF-8 files and directories are skipped, and results cap at max_results with a truncation flag. - `AllowAll` - permits every operation. - `write_owned` - noted in the write documentation but not implemented in v1; the zero-copy move for the memory overlay waits for profiling. - `crates/shared-vfs/src/path.rs` - the dead-code allows on intern and canonicalize are removed now that the grep default canonicalizes. Design: new newtype @ crates/shared-vfs/src/traits.rs::ExecId boundary: pub Design: new global-state @ crates/shared-vfs/src/traits.rs::ExecId::vend Design: new speculative-abstraction @ crates/shared-vfs/src/traits.rs::VfsAccess boundary: pub Design: new speculative-abstraction @ crates/shared-vfs/src/traits.rs::Policy boundary: pub Design: new value-object @ crates/shared-vfs/src/traits.rs::Op boundary: pub Deferred: ExecId::vend carries #[allow(dead_code)] with no caller until the handle arrives in a later step Deferred: the defaulted write_owned method is noted but not implemented in v1, to be added when profiling calls for it Plan: vibe/2026-09-11-3-vfs-foundation.md
vinniefalco
marked this pull request as ready for review
September 12, 2026 01:26
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.
Summary
One filesystem abstraction replaces the run-scoped Store trait and serves every consumer: Lua, the model's tools, and the Bashkit engine. The Store survives as a public concrete facade with an unchanged Lua surface, mounted at
/_promptforge/storeinside the namespace it used to stand apart from, andexecutor::run()takesVfsRefin place ofStoreRef.What changes
Vfs/VfsAccesstrait pair behind a poison-safe cloneableVfsRef; a mount router with longest-prefix dispatch, builder-fixed mount tables, and nestable overlays; universal POSIX-shaped path canonicalization with interning; a claims model attributing every operation to anExecIdthrough an RAIIAccesscapability, where concurrent write conflicts are fatal; a policy layer with reason-carryingDeny/Askverdicts (AllowAllin v1); a generic memory backend; and a stage-1 host backend (HostBackend::identity()/HostBackend::rooted(dir)) with containment enforcement and failure-atomic writes./_promptforgemount layout, theempty()stock constructor with the store mount preinstalled, andModePolicy(Ask / Plan / Agent) behind a UI-flippable shared mode.Storefacade wraps a prefix-scopedAccessand is exposed asvfs.store(&access). TheStoreErrorvocabulary, anchor-edit rules, numbered reads, idempotent delete, and glob grammar are preserved exactly. The WriteScope registry is deleted in favor of the claims model.execute::run()takes&VfsRef;RunContextbuilds the Store facade internally; Lua store operations become leaf yields in the coroutine protocol, answered viaspawn_blockinguniformly for all backends; a claims violation maps to a fatal determinismRunErrorKindthat terminates the run and is not catchable from Lua.Verification
VfsRefandAccess.VfsRef, including a fanout whose cross-arm append terminates the run with a determinism violation naming both arms.FsBackendoverVfsRefcompile-checked and smoke-tested with an ls/cat/grep script across mounted backends.Out of scope
The do_shell dispatcher, git builtins, approval policy, the SQLite run-record backend, terminal mirrors, Bashkit integration proper, and stage-2 host-backend hardening.
Plan: vibe/2026-09-11-3-vfs-foundation.md