VFS Right-Thing Restructure: five-crate workspace, correctness fixes, semantic unification#3
Merged
Conversation
…atch — full context, vng repro harness, validation table, get_maintainer routing, and remaining send steps (DCO + send-email deliberately left to the author) Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Use the session-owned unmount path to abort the FUSE connection before joining readers, and keep and join uring queue threads with a NOP wakeup so /dev/fuse clones do not keep teardown alive. Adds the bounded SIGTERM regression from research/tests-ci-deep.md §1 and readiness-validation.md blocker. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…box-deep.md Q7) Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…8; research/tests-ci-deep.md §2)
…1; research/cli-cmd-sweep.md §3; D5)
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Resolve base-origin directory rename protection by checking whether the source path still resolves to a visible base directory, rather than trusting Layer::Base after partial promotion. Extends EXDEV and mv fallback regression coverage for validation/m1-foundations/scrutiny/synthesis.json and research/agentfs-core-deep.md §2.3 (D9). Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Rewrite the VAL-BUGS-014 regression to drive the shared Darwin/NFS supervision cleanup path with a real SIGINT, fake mount registry, bounded unmount, NFS shutdown, mountpoint removal, and child process liveness checks (evidence/m1-foundations/signals-b/VAL-BUGS-014-darwin-supervision-cargo-test.txt; research/cli-cmd-sandbox-deep.md Q7). Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Treat the u64::MAX io_uring NOP wake CQE as a shutdown sentinel before slot indexing, rather than letting it underflow into usize::MAX and panic during SIGINT teardown. Adds the VAL-FUSE-020 panic census over repeated SIGINT teardown cycles on both FUSE legs; evidence: research/tests-ci-deep.md §1 and evidence/m1-foundations/signals-b/VAL-BUGS-016-mount-foreground-sigint-sigterm.txt. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Turso OpenEphemeral creates TMPDIR/tursodb-ephemeral-* files when temp_store remains DEFAULT; AgentFS now sets temp_store=MEMORY on file-backed connections and gates VAL-CORE-022 with a pinned-TMPDIR CLI battery. Also make the panic census harness resolve the workspace target dir so it does not run stale pre-workspace binaries. Evidence: validation/m1-foundations/user-testing/synthesis-round1.json noted 611 leaked sidecars; root cause is turso_core vdbe/execute.rs OpenEphemeral temp_store default. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Collapse the vendored fuser ABI matrix to the single supported 7.36 layout advertised as 7.42 and delete abi-7-40 passthrough scaffolding (research/fuse-stack-deep.md Q1; architecture D10). Keep the M1 teardown path on FuseSessionHandle/SessionUnmounter per fresh BackgroundSession/spawn_mount2/Session::from_fd census. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Add a median-of-5 benchmark comparison helper so BENCH workers apply the >5% relative and >10ms absolute red rule consistently. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Dead per research/fuser-dead-surface-sweep.md dead-surface findings, research/fuse-stack-deep.md Q5, and library/m2-abi-collapse-census.md. Removes Linux-dead macOS/BSD branches, deprecated fuser session APIs, unused notifier/poll/mount-option/scheduler/KernelConfig surfaces, and duplicate uring ABI stubs while keeping SessionUnmounter teardown path. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…esearch/nfs-stack-deep.md Q7) Remove the empty config file, unused fs_util module, ROFS capability plumbing, mount-signal listener plumbing, stale helper constants, get_root_mount_handle, and WriteCounter::into_inner. Replace the unsafe generation static with OnceLock while keeping dispatch-reachable fake portmap GETPORT. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Replace the live SDK path helpers with thin delegates to the inode FileSystem trait and remove the superseded path mutation/read helpers cited by research/agentfs-core-deep.md §3.3/§4.1-4.2. This leaves one mutation implementation for CLI/MCP callers and preserves coverage with path delegate, file-handle, and trait rename characterization tests. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Delete commit_inode_ranges and the dead drain_inode Timer arms per research/agentfs-core-deep.md §3.2. Route per-inode Bytes and explicit drains through drain_pending_batched so every drain uses the same commit-then-remove discipline; add a Bytes-trigger characterization test. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Dead per research/cli-cmd-sandbox-deep.md Q4; census re-verified at HEAD. Removes never-compiled Linux cfg blocks from run_darwin.rs, fixes the stale unsupported-platform header, and deletes unused macOS sandbox helper API/allow_read_paths. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Add an ancestor walk in the canonical FileSystem::rename so direct SDK, MCP, and NFS callers reject directory-into-own-subtree moves with FsError::InvalidRename instead of writing fs_dentry cycles. Restores the dropped trait regression and adds MCP coverage for the M2 scrutiny blocker (validation/m2-dead-surface/scrutiny/reviews/m2-path-api-delete-delegate.json; research/agentfs-core-deep.md §5.1). Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Parse FUSE env knobs once into FuseConfig so dispatch, uring, and kernel-cache safety share one value path. Session now receives DispatchMode and UringConfig by value, and invalid readdirplus input keeps the default-on policy. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Declare runtime knobs in a generated ledger with docs drift tests and remove AGENTFS_OVERLAY_PARTIAL_ORIGIN env compatibility per architecture D8 and research/knob-sweep.md §5. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Route VAL-CONF-003 command env reads through cli::config accessors per architecture.md §2.7 and validation/m3-config-telemetry/user-testing evidence. Extend the EnvReader census to cli/src so future command reads fail structurally, keep AGENTFS_CLONE_TIMINGS declared at config::clone_timings_enabled, and remove ambient TURSO_DB_AUTH_TOKEN from the runtime knob ledger. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Archive the M1 medians artifact and record a post-M3 baseline after two serialized median-of-5 runs agreed within the perf contract band. The sentinel procedure remains the fallback for future ambient drift. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Every shell test now runs out of its own mktemp root with trap cleanup and honors AGENTFS_BIN with a cargo-run fallback (tests-ci-deep.md section 5: repo-CWD litter in test-init/test-run-syscalls, no trap, cargo-run lock contention; scripts-docs-sweep.md action plan item 5). gate.sh exports AGENTFS_BIN to all.sh so the strict suite exercises the prebuilt release binary, and both gate.sh and all.sh pin TMPDIR to a per-run scratch dir cleaned on exit because turso_core 0.5.3 never unlinks its /tmp/tursodb-ephemeral-* sort spills (vdbe/execute.rs:10096; library/environment.md). Suites that run git inside temp HOMEs now pin the distro git binary and write a hookless .gitconfig: the user's PATH routes git through a hook-manager shim that daemonized out of deleted test repos (715 leaked processes, load 98; library/environment.md). Also excise the ptrace-era experimental-sandbox skip branch and fsync comment from test-pread-sparse.c:34,62 (scrutiny M1 census gap) and drop the stale /cli/tests/syscall ignore rules (the legacy cli/ tree is gone; crates/agentfs-cli/tests/.gitignore covers the new path). Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…NS-016) clippy::await_holding_lock is now deny via [workspace.lints] with every crate opting in ([lints] workspace = true); the workspace is clean under it. The four multi-lock modules named by the canon (architecture.md section 7 item 7) — batcher, FUSE AdapterCaches, overlay, and the semantics handle table — get module-header lock-order documentation matching their verified acquisition patterns. Also cfg(target_os = "linux")-gate the knob-ledger tests that assert Linux truth (generated_knobs_doc_matches_declarations and the three FUSE-knob assertions plus their helpers): LINUX_FUSE_KNOBS is empty off Linux, so they can only be honest where docs/KNOBS.md is generated — matching the existing docs::tests::manual_help_parity gate. This keeps the macos-latest workspace CI job honest (m7-docs-generation follow-up). Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…e (M7) Executes the research/scripts-docs-sweep.md action plan (VAL-GATE-014): - phase8-validation.py stays the sole top-level python gate and now runs the noopen/flush/external-base-mutation coherence harnesses as named gates (sweep recommendation 1); gate.sh drops its separate coherence invocations and gains the consistency-canon census step. - Shared harness helpers move to scripts/validation/lib/common.py (resolver, process-group subprocess handling, JSON parsing); the writeback-durability common module and the coherence harnesses import it instead of drifting copies. The shared resolver also fixes the stale pre-workspace cli/target fallback paths (the class of bug library/environment.md documents from m2). - Historical validators (phase0.sh, phase6/65-validation.py, check-fork-governance.sh, backend-risk-spike.py) move to scripts/validation/archive/ with a README (sweep action plan item 4); nothing invokes them (fresh rg census at HEAD: only self-references). - New scripts/validation/consistency-canon.sh mechanically checks the architecture.md section 7 canon (crate DAG, sealed transport surfaces, 2500-line non-test cap, tracing-only logging, env-reads at the config edge, EnvFilter coverage, await_holding_lock, lock-order headers, docs layout, changelog) and is wired into gate.sh (VAL-CONS-014). - dist-workspace.toml pointed dist at the deleted cli/ directory; the workspace root is the dist member now. - docs/TESTING.md updated to the consolidated gate reality. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
test-cli-smoke.sh gives VAL-CLI-024 a named target: one fast end-to-end pass over init (incl. --force refusal), run, exec, clone, fs, timeline, backup --materialize, materialize, integrity, migrate (committed v0_4 fixture), MCP initialize via the deprecated mcp-server alias, ps, completions, and the deprecated nfs alias on an ephemeral port. Wired into all.sh as the cli-smoke leg and into services.yaml as gate-cli-smoke. Backgrounded legs across the suite now resolve the agentfs binary once and background it directly: backgrounding the run_agentfs wrapper recorded the subshell PID, so the cleanup kill orphaned the real process (one leaked nfs server observed while validating this feature; same mechanism as the environment.md signal-teardown note). Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
The milestone-gate comment still named the deleted cli/ path and the pre-consolidation gate composition; the VAL-GATE-008 census over .github/workflows and dist-workspace.toml is otherwise clean (no sdk/*, examples, sandbox, or windows targets), and the matrix builds exactly the five-crate workspace. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
The mount leg daemonized `agentfs mount` and exited right after fusermount3 -u, leaving the FUSE connection draining in the kernel for ~2s. The very next fuse-over-io_uring mount racing that drain wedges before mount(2) completes and never recovers (kernel 7.1.2-cachyos), so teardown-bounded's uring leg failed deterministically under all.sh pacing while passing standalone. Foreground the mount, background the binary directly, and wait for both unmount and process exit before the test returns. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Evidence: validation/misc-c/scrutiny/synthesis.json (items 1-2); m7-run-options
and m7-mcp-fixes handoffs (items 3-4).
- nfs: ServerHandle::join takes the task out of the slot on JoinError too, so
abort-teardowns stop warning spuriously and a second join cannot re-poll a
completed JoinHandle (synthesis nonBlocking lib.rs:97; red test captured).
- core batcher: pruned_generation_watermark advances only to the max generation
among actually-pruned entries (still ABA-safe), so never-batched attr-cache
fills survive concurrent prunes at retired-map saturation (synthesis
nonBlocking batcher.rs:381; red test captured).
- cli run: /dev/shm joins the world-writable read-scoped zones; the /dev
skip-prefix in the ro-remount pass keeps the fresh scoping tmpfs writable
while the host dir becomes invisible (invariants 1/2; analogous to the
m7-run-options /tmp closure; host-leak repro captured pre-fix).
- cli fs write: created files and intermediate dirs are owned by the invoking
uid/gid so a later chmod inside 'agentfs exec' no longer fails EPERM
(pre-fix repro: uid/gid 0 + chmod EPERM).
- core options: nonexistent path-shaped resolve arguments report
'database not found' instead of 'invalid agent ID'.
- core errors: #[from] wrapper variants chain their cause via source() only,
ending doubled 'cause: cause' output under anyhow {:#}; the fuse errno trace
switches to Debug capture to keep full detail.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…en skip_mount (misc-d) Runtime-litter hardening (misc-d-runtime-litter-hardening; evidence: m7-scripts-consolidation handoff / a77e142, library/user-testing.md): 1. FUSE-over-io_uring rapid-remount wedge (kernel 7.1.2-cachyos: a mount racing the ~2s kernel drain of a just-closed connection blocks in mount(2) forever): on enable_uring=Y kernels the fusermount reply wait is now bounded and retried (3x3s, then a clear error naming the constraint and the AGENTFS_FUSE_URING=0 escape), with a defensive lazy unmount so retries never stack mounts; mount readiness and readiness-timeout teardown in agentfs-mount run on helper threads with bounded waits, so a post-INIT stall also surfaces as an error instead of hanging. Documented in docs/MANUAL.md next to the knob section. 2. Recycled fusectl-id abort (found by the new rapid-remount regression leg, reproduced at the pre-change baseline on BOTH transports): SessionUnmounter's teardown abort wrote the connection id from a dead fd's fdinfo, but the kernel frees that id at unmount and reissues it to the next fuse mount, silently killing it (~1 in 5 rapid cycles, exit 0). The abort is now skipped when the device fd polls POLLERR. Regression: test-mount.sh rapid unmount-then-mount cycles (fresh mountpoint+DB per cycle, bounded windows, all owners reaped). 3. turso_core 0.5.3 leaks /tmp/tursodb-ephemeral-* sort spills (vdbe/execute.rs:10096 never unlinks; turso reads std::env::temp_dir() per spill): the CLI now pins TMPDIR to a per-process agentfs-spill-<pid>-<n> dir at startup, removed via atexit (covers the child-status process::exit passthrough sites), re-adopted after the mount daemon fork, and garbage-collects dead-owner spill dirs at start. Children of run/exec/init -c get the user's original TMPDIR restored in the spawn paths. Verified by new test-ephemeral-sidecar-cleanup.sh legs (child TMPDIR via run and exec, spill-dir residue census, stale-GC) and config.rs unit tests. 4. skip_mount() in cmd/run/linux.rs compared string prefixes, so a /devfoo-style mountpoint dodged the ro-remount pass; it now compares path components (Path::starts_with), pinned by unit tests (red captured via temporary revert: '/devfoo must not be skipped'). Gate: strict all.sh SUMMARY PASS=24 SKIP=0 FAIL=0; scripts/gate.sh green end-to-end (fmt, clippy -D warnings, 434 workspace tests, phase8 smoke, consistency canon). Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…ness probe (m7 polish) Evidence: validation/misc-d/scrutiny/synthesis.json nonBlockingIssues (adapter/mod.rs:2146 Display-formatted SdkError; session.rs:544 fail-open poll error in connection_is_aborted). - fuse/core: after source-only chaining (ae35384), SdkError Display carries only the category for Database/Io/Json/Time wrappers, so every log site that Display-formatted an SdkError switches to ?error Debug capture, same as the errno trace fix: forget/batch_forget drain warns, destroy and Drop flush/commit/finalize warns, the forget flush warn (:2146), and the core batcher scheduled-drain warn. Fresh census: no Display-formatted SdkError log sites remain (the surviving Display sites are io::Error/anyhow). - fuse transport: connection_is_aborted fails closed on a non-EINTR poll error - it now reports the connection as aborted so abort_fuse_connection skips the fusectl write, keeping the recycled-connection-id abort hazard unreachable when liveness cannot be positively confirmed. The poll loop is extracted as connection_is_aborted_by with an injectable poll; unit tests pin the poll-error (red under the pre-fix fail-open behavior), EINTR-retry, POLLERR, and quiet-live paths. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…tation)
VAL-CROSS-006 requires a sandboxed run to leave a timeline record naming
the session and command result, but no surface wrote tool_calls rows for
run sessions (timeline on a session delta.db returned []). The owner run
now records one 'run' audit row after supervision teardown, when the
delta DB is finalized and free of the live FUSE session: parameters carry
{session, command argv}, result carries {exit_code}, and a nonzero child
exit records status=error. The reopen finalizes afterwards so the audit
insert does not leave a WAL sidecar and undo the single-file teardown
(caught by phase8-concurrent-git-stress strict_no_sidecar_files). Audit
failures warn without disturbing the child-status passthrough
(VAL-CLI-020). TDD: the cli-smoke timeline-audit leg (isolated HOME,
sidecar census before the timeline reopen) was red against ff91302.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
m7-attestation (handoff 39ca6a98) found that read-style CLI reopens violate the single-file invariant I1: agentfs timeline (and every other command that reopens a WAL-mode DB without writing) left a 32-byte header-only -wal behind after exit, which the phase8 stress gate misses because it only unlinks size==0 WALs and never runs timeline. Census of all read reopen paths: timeline, fs ls/cat, diff, sync stats, integrity (read path), migrate (already-current and --dry-run); ps opens no DB. SDK-open paths now finalize before exit like the 3669c7b run-audit fix (warn-only, so a concurrent holder cannot fail a read command); the raw turso Builder paths (integrity, migrate) instead remove only the frameless sidecars the open itself created, preserving the existing read-paths-must-not-mutate-a-preexisting-family tests. Red-first: the new cli-smoke no-sidecar-after-read census leg failed against 3669c7b ('timeline left delta.db-wal behind'), as did the timeline unit leg under a temporary in-place revert. Also pins the tool_calls timestamp unit contract flagged by the same handoff: storage is millisecond-granular while both timeline display forms (table date, JSON integer) render the second-granular value that ToolCalls normalizes on read. The attestation diagnosis predates normalize_timestamp_for_output, so no display defect existed; the contract is now covered by core and CLI unit tests and the formatter documents its unit. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…e (M7) m7-fix-readonly-reopen-sidecar (handoff 8f3d174c) left two single-file invariant (I1) gaps. (1) serve mcp / mcp-server: the per-request-mode startup validation probe (open+drop in McpServer::new) and the per-request opens materialize a -wal that survived while and after serving; the probe now finalizes via cmd::init::finalize_readonly before releasing the file lock, and server exit (stdin EOF or a stdio-loop error) reopens and finalizes, warn-only, mirroring the NFS finalize-on-shutdown behavior (agentfs-nfs server/tcp.rs). (2) fs write drained the batcher but kept the truncated 0-byte -wal; write_filesystem now calls agentfs.fs.finalize() (drain plus checkpointed-sidecar removal) so the one-shot write exits durable and single-file. Red-first against the dbf8ddc release binary: test-mcp-server.sh's new single-file census failed at both legs (probe and graceful shutdown left red.db-wal behind), and the cli-smoke census extension failed with 'fs write left readonly.db-wal behind'. Both suites, agentfs-cli tests, and workspace fmt/clippy/tests are green at the fix. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…(M7) m7-fix-serve-finalize (library/codebase-quirks.md, handoff f2f003e0) left one known I1 offender: agentfs init kept a ~119KB -wal next to the freshly created DB. Two defects under one symptom: (1) plain/--base init never checkpointed the schema writes; init_database now calls cmd::init::finalize_readonly (dbf8ddc pattern) before returning in the no-command path. (2) init -c (and every mount-owning flow, e.g. exec) truncated the -wal at teardown but left the 0-byte file behind: core AgentFS captured db_path relative (.agentfs/<id>.db via agentfs_dir), and MountHandle::unmount_inner_async chdirs to / before the FUSE-drop finalize runs, so remove_checkpointed_sidecars statted /.agentfs/... and silently skipped. from_pool_with_path_config_and_reap_hooks now pins db_path with std::path::absolute at construction; that field is consumed only by finalize's sidecar removal. Also: test-mcp-server.sh fail() used the obsolete 'tail -20 file1 file2' form, which errors ('option used in invalid context') under this coreutils POSIX mode and swallowed the stdio-session diagnostics; now 'tail -n 20 --'. Fresh census: that was the only multi-file obsolete-form tail site. Red-first against the f4fac47 release binary: the new cli-smoke init census failed with 'init left smoke.db-wal behind' (plain 119512B; --base 135992B; -c 0B leftovers reproduced directly; exec shows the same 0B leftover, fixed by the same core change). Green at the fix: cli-smoke and mcp-server suites, workspace fmt/clippy/tests. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…exec single-file (M7) Two residuals from m7-fix-init-finalize (library/codebase-quirks.md): (1) The cli-smoke single-file census never ran exec, so exec's -wal hygiene was pinned only by a manual probe. New census leg: scratch copy under .agentfs/, exec by agent id (the cwd-relative resolution that triggered the 8e91eb9 bug), assert no -wal/-shm after exit. Red-first via a temporary in-place revert of the 8e91eb9 core absolutize: exec left a 0-byte exec-census.db-wal behind; green with the fix restored. (2) Bug-class hardening: agentfs_dir() is a bare relative .agentfs and turso retained the original relative path string internally, so any future by-path consumer running after mount teardown (cwd = /) would repeat the 8e91eb9 class. AgentFSOptions::db_path() now absolutizes in both the path and id branches (:memory: passes through), so turso and every db_path() consumer (open, mount precheck, migrate, integrity sidecar capture) receive an absolute path. Audit found no consumer needing db_path() relative: init's relative display string and the shell-completion enumeration build their paths independently, and resolve()'s stored path field is unchanged. The 8e91eb9 core-layer absolutize stays as the guard for direct-path constructors (AgentFS::new) that bypass AgentFSOptions. Green: workspace fmt/clippy/tests, cli-smoke suite (init/-c census legs intact plus the new exec leg), agentfs-cli tests. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…d census git-ai leaks in the harnesses (M7) Harness hardening from the m7-attestation round-2 handoff (f2bb6513): bench and phase8 legs shelled out to PATH git (the git-ai shim), leaking idle 'git-ai bg run' daemons on every run (~70 in round 2, ~400 in round 1; 2 reproduced from a single tiny bench iteration). Key finding: PATH shims cannot pin git inside 'agentfs run' - the sandbox hides temp dirs and home files, so a shim dir under /tmp silently falls out of PATH and the hook-manager git takes over. The gate's own pinned shell suites leaked 15 daemons per run this way. - lib/common.py: pin_distro_git (PATH shim + hookless gitconfig + GIT_CONFIG_GLOBAL + absolute GIT override) and git_ai_processes/ git_ai_leaks census helpers; leak attribution by environ HOME or AGENTFS_SESSION so the user's own daemon churn (HOME under the real home) does not false-positive. - git-workload-benchmark-multi.py, phase8-validation.py: pin os.environ and fail the run/gate on a post-run census leak (new required git_ai_process_census gate in phase8). - git-workload-benchmark.py, phase8-concurrent-git-stress.py: honor env GIT in every git invocation including the embedded workloads spawned via agentfs run; pin the workload env in prepare_environment. - git-workload-benchmark.py: parse workload JSON from full stdout and strip it from embedded records; the single-line JSON exceeded the 20000-char stdout_tail once phase8 nested its TMPDIR, deterministically failing phase7-in-phase8 at HEAD (missing_required_phases, pre-existing). - test-teardown-bounded.sh, test-corruption-torture.sh: sandboxed workloads call "$GIT_REAL" by absolute path (exported); the in-temp-HOME PATH shim only holds host-side. - posix/run-pjdfstest.sh: absolutize --agentfs-bin/AGENTFS_BIN at resolve time; the harness cd's into its work dirs, so a relative path died with exit 127 buried in init.log. Verification: scripts/gate.sh green end to end (all.sh strict PASS=24 SKIP=0 FAIL=0 incl. both torture legs; phase8 smoke PASS incl. the new census gate; canon PASS) with ZERO new git-ai processes across the whole gate window; bench multi and pjdfstest (relative bin, phase45-ci, 142 tests PASS) verified individually. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…belt profile (M7) The macOS agentfs run profile still granted a blanket (allow file-read*), leaving sacred invariant 2 (scopeable reads) Linux-only after ea416c8. Per research/darwin-seatbelt-read-scoping.md (Codex restricted platform defaults, Chromium/Mozilla Seatbelt prior art): reads are now default-deny, allowing only the session paths (~/.agentfs/run/<id>), the DEFAULT_ALLOWED_DIRS/--allow roots, curated PLATFORM_READ_ROOTS (dyld cache cryptex, /System minus the /System/Volumes firmlinks back into the data volume, /usr/lib, /usr/share terminfo/locale, /private/etc, /dev, temp dirs, executable dirs, /opt, /usr/local, /Applications), and metadata-only literals for path-resolution parents plus the /var->/private/var symlink and /System/Volumes/Data firmlink chains. Read roots also grant file-map-executable so mounted build artifacts stay executable. Write scoping is unchanged; sandbox-exec is pinned to /usr/bin/sandbox-exec per the research recommendation. macOS-gated unit tests (cmd::run::tests::darwin_read_scoping) pin the generated SBPL posture: deny-default with no bare (allow file-read*), every platform root present, session/allow roots expanding from the config, metadata-only parents, write rules unchanged. Verified red against the pre-fix generator and green now via a Linux scratch extraction of the cfg-macos code; CI macos-latest runs them natively. The manual macOS release gate (scripts/validation/macos-nfs-git-validation.sh) gains a read-scoping leg: a secret in an unallowed HOME dir must be unreadable (permission error, no content leak) and --allow must make it readable. MANUAL (Sandboxing section + regenerated clap reference), TESTING, and CHANGELOG document the platform status. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…station) Definition-of-done record for the restructure mission (VAL-GATE-012, VAL-GATE-015, VAL-CROSS-012): full-gate.json (scripts/gate.sh exit 0 at 230c30a with PASS=24 SKIP=0 FAIL=0 strict shell suite incl. both corruption-torture legs, phase8 --smoke failed_gates=[], consistency-canon all rules PASS, pjdfstest phase5-ci 311/311, cross-surface flows 30/30), medians.json (median-of-5 codex contract vs the post-M3 baseline: all 7 phases in band, checkout +0.4%, clone +2.9%; official dae8aa6 pair + repeat cited at this HEAD per the orchestrator ruling — the only later commit is cfg-macos/docs/help-text only), bench-milestones.json (per-milestone BENCH record incl. the M7 checkout-red closure via git pinning), and README.md (human summary + file-size cap census). restructure-baseline/attestation is a symlink to the artifact dir for the feature's expected path. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…cutable 126 (M7) M7 user-testing round 1 blocker VAL-CLI-019 (evidence/m7-cli-docs/cli-proc/ VAL-CLI-019-error-cases.txt and VAL-CLI-019-exec-127-reprobe.txt): agentfs exec routed the child spawn failure through the unified reporter and exited 1, violating the contract's command-not-found passthrough exception; run already exited 127 via its child execvp path (the cited pattern, cmd/run/linux.rs exec_command) but flattened found-but-not-executable to 127 as well. Add supervise::exit_code_for_spawn_error (NotFound -> 127, PermissionDenied -> 126), map exec's spawn error through it before falling back to the reporter (profile sink flushed at the new exit site), and reuse it in run's child exec-failure exit so both commands follow the POSIX 126/127 convention. Ordinary child exit codes and 128+signal passthrough (VAL-CLI-020) are unchanged. New cli-smoke leg asserts exec exits 127 for missing commands (absolute and PATH lookup), 126 for a non-executable, 43 for sh -c 'exit 43', with exactly one Error: line, plus run 127/126 parity; leg captured red against the cf02f65 release binary (exec exited 1) and green after the fix.
…ex metadata; run spawn exits (misc-e)
Harden the macOS run sandbox per research/darwin-seatbelt-read-scoping.md
(Codex/Chromium parameter model) and the m7 scrutiny findings
(validation/m7-cli-docs/scrutiny/synthesis.json, m7-darwin-read-scoping):
- generate_sandbox_profile now returns SandboxProfile { policy, params }:
every dynamic path (mountpoint, run dir, allow paths, resolution
ancestors, $HOME/Library) is a (param "NAME") reference passed to
/usr/bin/sandbox-exec as -D NAME=value, so a quote-bearing path can no
longer forge SBPL rules; the session id reaching the profile text is
charset-sanitized. Linux scratch-crate red/green: the new injection test
fails against the HEAD generator (raw 'pwn") (allow ...' interpolation)
and passes post-fix; param substitution reproduces the old profile
line-for-line modulo the new Preboot literal.
- PLATFORM_METADATA_PATHS gains /System/Volumes/Preboot so path resolution
to the dyld cryptex root can stat every component (m7 scrutiny
nonBlockingIssues[9]); noted for the manual hardware gate in
docs/TESTING.md.
- darwin run maps spawn failures through exit_code_for_spawn_error
(127 missing / 126 non-executable) on both the supervised and
join-session legs, matching exec.rs (ad663b6 residual).
- rust.yml runs clippy on macos-latest so macOS-only -D warnings breakage
is caught by CI instead of scrutiny.
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…misc-e) Evidence: validation/m7-cli-docs/scrutiny/synthesis.json nonBlockingIssues and the M7 user-testing VAL-CLI-006 workaround note. 1. Schema-mismatch open errors name `agentfs migrate` at the remaining CLI open sites (clone, sync pull/push/checkpoint/stats, serve mcp startup + per-request + shutdown reopen, run's delta-db opens on linux and darwin), matching the fs/exec/timeline/nfs/mount convention. Census: the only unwrapped non-test SDK open left is `agentfs init`, which only ever opens a freshly created (or force-recreated) file. 2. A failing `agentfs migrate` no longer leaves the 0-byte WAL its own open materialized: the in-place body moved into migrate_in_place so every error return drops the DB handle, and handle_migrate_command runs remove_created_frameless on error (dbf8ddc created-sidecar semantics). Red-first: test_failing_migrate_removes_frameless_sidecars_it_created fails pre-fix; the 67fa183 release binary leaves future.db-wal behind on a user_version-999 fixture. 3. run_child self-guards the pre-runtime fork's shared-mount-propagation prerequisite: an is_mountpoint check before the step-5 bind exits with a `mount --make-rshared /` remediation instead of silently binding an empty dir over cwd. Reproduced pre-fix under `unshare -Ur --mount --propagation private` (empty sandbox cwd, exit 0); post-fix the same repro fails loudly and normal runs are unaffected. 4. daemonize now bails when called from inside a tokio runtime context (unit regression test), making the fork-in-live-runtime class ea416c8 fixed for run self-guarding. Census at HEAD: `mount --backend nfs` non-foreground already forks pre-runtime since 129e02d; no daemonize call site runs inside a live runtime. 5. `agentfs backup <id> <bare-relative-target>` treats the empty parent of a bare relative target as the CWD instead of failing with an empty-path error (VAL-CLI-006), with unit tests for the bare-target and missing-parent cases. 6. Docs: MANUAL documents the MCP write_file new-file default mode 0644 and the ephemeral session-private view for sandbox writes to hidden non-allowed paths; fixed the unresolved AgentFS::open rustdoc link in agentfs-core options.rs. Verification: fmt, clippy --workspace --all-targets -D warnings, cargo test --workspace, and scripts/gate.sh all green with the rebuilt release binary. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…tes clean their session dirs (misc-e) misc-e-run-teardown-hygiene, residuals from the misc-e-cli-polish handoff (84390a39 discoveredIssues; abort path first flagged by the m7-run-options scrutiny review): 1. run()'s parent error paths after a successful mount (child handshake failure, uid/gid map write failure) exited via process::exit without dropping the live MountHandle, so FUSE was torn down only by process death and stayed behind as a dead mount table entry. Deterministic repro: nested userns with max_user_namespaces=0 makes the child's unshare fail post-mount (captured red pre-fix). abort_child_with_teardown now reaps the child, runs the bounded graceful unmount, then exits; write_namespace_mappings returns Result so callers own the abort. New abort-path leg in test-teardown-bounded.sh pins exit 1 + clean mount table (red pre-fix via ABORT-LEG-STALE-MOUNT=1, green post-fix). 2. Shell suites cleaned their mounts but not their ~/.agentfs/run/<session> dirs (715 dirs / 536M accumulated over the mission). Suites invoking agentfs run with the real HOME now pass explicit unique --session ids and remove exactly those dirs in their cleanup traps: test-run-bash/ -git/-syscalls, test-symlinks, test-cli-smoke (test-run-read-scoping already complied; temp-HOME suites are covered by their temp roots). Same targeted cleanup for the one littering python harness, noopen-coherence.py (124 of the accumulated dirs). Convention documented in all.sh; pre-existing accumulated dirs deliberately left in place (user decision). Never sweep ~/.agentfs/run wholesale. Verification: fmt, clippy --workspace --all-targets -D warnings, cargo test --workspace, scripts/gate.sh (SUMMARY PASS=24 SKIP=0 FAIL=0 with the new abort-path leg), phase8 --smoke rerun after the harness change; per-suite ~/.agentfs/run censuses unchanged before/after every suite.
…crutiny (misc-f)
misc-f-defense-in-depth-polish; all five items from the misc-e scrutiny
round-1 nonBlockingIssues catalogue (validation/misc-e/scrutiny/
synthesis.json), per-item red/green captured:
1. supervise.rs double-fault ordering: if spawn fails AND the teardown
unmount also fails, the unmount error shadowed the spawn io::Error so
darwin run exited via the reporter instead of 127/126. The spawn error
now stays the downcast root with the unmount failure attached as
context (spawn_failure_error); both-fail ordering unit-tested (red
captured via a temporary in-place revert of the helper body).
2. daemon.rs fork guard: Handle::try_current() is thread-local and a
runtime alive on another thread (or built but not entered) evades it;
tokio has no process-wide probe, so the daemonize caller contract now
documents the exact scope. Both call sites (agentfs mount fuse/nfs
non-foreground arms) remain pre-runtime.
3. sandbox_log_tag: a fully-hostile --session id sanitized to the empty
string yielded a bare 'agentfs-:' deny tag; falls back to a fixed
'session' placeholder. macos-gated test via generate_sandbox_profile;
red/green proven on Linux with the scratch-crate technique (pre-fix fn
from git show HEAD, post-fix from the working tree).
4. noopen-coherence.py finally-block cleanup: a leg hard-killed with its
FUSE mount live left the session dir behind (rmtree swallowed
ENOTCONN/EBUSY); cleanup_run_session_dir now detaches a stale/live
mount (fusermount3 -uz) before rmtree and can never raise through the
leg's real failure. Faithful repro: SIGKILLed run session, bare rmtree
left the dir (red), guarded cleanup removed it (green).
5. Suite cleanup traps guard against empty-but-set HOME with ${HOME:?} in
every rm -rf under ~/.agentfs/run: the five suites touched by 688651e
plus the identical pattern in test-run-read-scoping.sh and
macos-nfs-git-validation.sh (repo census now clean).
Verification: fmt, clippy --workspace --all-targets -D warnings, cargo
test --workspace, release rebuild, scripts/gate.sh green (SUMMARY PASS=24
SKIP=0 FAIL=0, phase8 + coherence harnesses passed). darwin.rs per-symbol
import census clean (trait imports evidenced by method calls); final
compile authority for the macos-cfg edits is CI's macos job.
…ff hint, capability index)
… darwin-only dead code, gate Linux-only fuse crate) Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…nic-census uring SKIP via AGENTFS_GATE_ALLOWED_SKIPS Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…ain shell; log the uring legacy fallback at INFO The teardown-bounded suite polled kill -0 from inside a command substitution, so under dash the blocked parent never reaps the exited owner and the poller pins on the zombie until the deadline; poll in the main shell instead. The uring transport now logs one INFO line when the kernel rejects ring registration, since the 'starting' line precedes acceptance; TESTING.md and the gate comment stop claiming enable_uring is absent on CI runners (it reads N). Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.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.
What this is
The complete "Right-Thing Restructure" of the vfs fork: 97 commits taking the tree from post-perf-campaign debt to the approved target architecture, executed as a validated 14-milestone mission (M1–M7 plus six hardening batches). This PR body doubles as the campaign's living history; the in-repo authority for design detail is
docs/SPEC.md,docs/MANUAL.md,docs/KNOBS.md,docs/TESTING.md, andCHANGELOG.md.North star (strictly ordered): correctness > consistency > completeness > effortless interface. Interface simplicity beats implementation simplicity. The perf campaign's measured wins were treated as correctness constraints, not negotiable tidiness.
Starting state (grounded in a 14-report, file:line-verified recon corpus): one 9,338-line god-file, two vendored crate forks leaking as public API, 96 undocumented-or-drifting knobs, three hand-synced DDL copies, five divergent mount-supervision paths, and a CI gate that masked failures.
Sacred invariants (held throughout, gate-enforced at HEAD)
metadata-mutation-no-real-write.py,partial-origin-no-real-write.pyin the standing gate)..agents/benchmarks/restructure-attestation/).What was done, milestone by milestone
M1 — Foundations + correctness bugs (fixed before anything moved)
examples/, Windows stubs + dist target, the experimental ptrace sandbox +--experimental-sandbox.finalize()(8337b61)..ok()ALTERs → idempotent-or-propagate.clone/init -corphaned-mount-on-Ctrl-C.|| truemasking inall.sh/gate.sh, SKIP counts as failure, torture both legs.M2 — Dead surface deletion
abi-7-*FUSE feature matrix (17 features) collapsed to the one compiled ABI level;--all-features/--no-default-featuresbuilds restored to green.M3 — Config + telemetry extraction
docs/KNOBS.mdgenerated-checked from the config types, including a sunset table with per-knob removal criteria.M4a/M4b — Core decomposition
store.rsextraction (chunk/inline engine), batcherPendingView/Draintrait split (type-enforced "no drain while holding a conn"), versioned cache-fill fix, lifecycle +ReapHook.ddl-census.pyenforces it in the gate),user_versionmigrations, integrity battery in core.store.rs, FORGET-driven pruning. Tests moved out of code files.M5 — Crate split
Five crates in a clean DAG, vendored code adopted first-party behind sealed entry APIs, blanket
#![allow]s dropped:M6 — Semantic unification (the consistency pillar)
Semanticsfacade under both adapters: handler code can only check permissions via oneaccess::*implementation; a FUSE/NFS parity conformance suite runs in the gate, making adapter drift structurally impossible.AckDurabilityon every write ack — the reply's durability claim is derived from what the core actually did, so the pre-M1 "FILE_SYNC lie" cannot be reintroduced.HandleTablewith LRU write-authority tokens + unlink invalidation; reply-caching DRC replacing the drop-filter tracker.run_supervisedadopted by all five mount-owning commands (one supervision path instead of five).M7 — CLI + docs consolidation
RunOptionsstruct (kills the 7-file knob fan-out); unifiedDEFAULT_ALLOWED_DIRS; onemigratelanding any supported old schema (v0.0/v0.2/v0.4) at CURRENT, in place or--copy, with--key/--cipher.tool_callsaudit logging.docs/move with MANUAL/KNOBS generation-parity checks (the command reference is generated from the CLI's own argument definitions and cannot drift from--help).scripts/gate.sh(phase8 as top-level gate, historical validators archived, sharedlib/).agentfs run(230c30a) — beyond the original plan.exec/runspawn failures now follow shell conventions: missing command → 127, non-executable → 126 (ad663b6).misc-a … misc-f — Hardening batches (validated like every other milestone)
Notable: uring mount drain race bounded; darwin SBPL parameterization (no raw string interpolation into Seatbelt profiles) + CI clippy on macos-latest;
/System/Volumes/Prebootmetadata path; schema-mismatch errors point atagentfs migratefrom every reachable open site;migrateerror-path sidecar cleanup;runmount-propagation guard (mount --make-rshared /remediation) and refusal to daemonize inside a live tokio runtime;runabort paths reap the child and unmount before exit; all test suites clean their session dirs (${HOME:?}-guarded); supervise double-fault ordering (spawn error stays the 127/126 root even when unmount also fails); hostile--sessionids can't degrade the sandbox log tag.How it was validated
fmt/clippy -D warnings/workspace tests, shell suites with SKIP=fail, phase8 smoke, noopen/flush coherence, consistency canon (typed errors in core / anyhow at edge, one reporter, env reads only in config modules, no non-test file over the line cap, documented lock order) — PASS=24 SKIP=0 FAIL=0; pjdfstest 311/311; cross-surface parity 30/30.Intentionally deferred work (for future droids)
None of these are regressions; each was a deliberate correctness-first deferral with the landing site prepared:
ack_durability_for_write_request()innfs_handlers.rsdeliberately maps everything toCommitted(a unit test pins the policy), andNFSPROC3_COMMITreturns PROC_UNAVAIL — protocol-legal since we never reply UNSTABLE. The opt-in path is prepared end-to-end: the reply'scommittedfield already derives from actual core durability,Semantics::commit_barrier(Some(ino))is the documented COMMIT hook, and the write verifier is already per-server-instance. Remaining work: honorstable_howbehind a config knob, dispatch COMMIT, harden the verifier (random u64 per boot rather than ms timestamp), and add crash-window/verifier-across-restart tests. Deferred because UNSTABLE with imperfect verifier semantics is exactly the data-loss class M1 exterminated, and the beneficiary (macOS) has no hardware in the validation loop.AGENTFS_OVERLAY_READS,AGENTFS_DRAIN_ON_SETATTR,AGENTFS_DRAIN_ON_RELEASE,AGENTFS_FUSE_SELF_INVAL,AGENTFS_CLONE_TIMINGS) remain with documented removal criteria indocs/KNOBS.md(N=2 consecutive green milestones per knob). Removing them is mechanical once the criteria are met post-merge.scripts/validation/macos-nfs-git-validation.shplus the three Seatbelt spot-checks indocs/TESTING.mdmust run on real hardware before trusting a macOS release. No macOS hardware was available to this campaign (explicit, accepted gap).fuse.enable_uring; the honest uring gate is the local one (annotated in the CI workflow).concurrency_integrity::active_workerstiming assert under full-workspace parallel load; one-off mcp-server "stdio session failed" under back-to-back gate load..agents/kernel/— DCO sign-off andgit send-emaildeliberately left to the author.DATA_SYNCis currently treated identically toFILE_SYNC(stricter than required); fine to leave.Merge readiness
agentfs migratelands any supported old DB at CURRENT.