You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Python edits to filesystem.writeFile-created files are reverted by shadow reconciliation #2022
Python edits to a file seeded with vm.filesystem.writeFile() can succeed and be visible inside the Python execution, then silently revert to the API-seeded bytes when the host reads the file after execution.
This is the Python VFS bridge counterpart of #1961. The JavaScript fix in #1981 is present in the tested source, but Python uses a different write handler. Related: the shell/WASM report #1901 is still open. This report is specifically about Python writes, not the shell-to-Python child-process issue #2012.
Expected: both reads return EDITED\n. Actual: Python sees its edit and exits successfully, but the following filesystem API read returns original\n; subsequent executions also see the reverted data. A read-modify-write (p.write_text(p.read_text() + 'added\\n')) has the same problem. Seeding a fresh file through Python rather than through the filesystem API avoided the stale API-created shadow in our tests.
Verification
Reproduced with the published 0.2.22 packages on Linux arm64, including embedded AgentOs.create() without HTTP, Rivet actors, or PostgreSQL. That setup used a command-registration workaround to enable Python; this report starts after Python executes successfully.
Independently reproduced against unpatched upstream source at c3698e6a60be0a6fed61d411779930e0a7753fc8 using the native-sidecar Rust wire harness and ExecuteRequest { runtime: Python, ... }. This bypasses SDK interpreter-command registration entirely.
The new regression fails with left: "original\n", right: "EDITED\n" on its first post-execution host read. The bridge bundle was built from that checkout; no application storage service is needed.
Suspected cause
The wire WriteFile handler mirrors bytes into the VM's root staging/shadow tree. service_owned_python_filesystem_rpc_request handles Python Write by updating only the kernel VFS. Subsequent shadow-to-kernel reconciliation imports the unchanged staging copy and overwrites the newer Python bytes. The JavaScript mirroring change in #1981 does not run on this Python RPC path.
I am preparing a focused fix and regressions for Python writes, including read-modify-write and atomic replacement. Python append-position/truncate behavior and shell/WASM shadow writes are separate from this fix.
The focused fix preserves Python overwrite, read-modify-write, and temporary-file replacement of API-seeded files. Repeated filesystem API reads and a later Python execution all see the edited content. The regression's overwrite case fails on unpatched c3698e6 with original\n instead of EDITED\n.
Both new tests pass as root and as an unprivileged UID 1000:
cargo test --locked -p agentos-native-sidecar --test filesystem shadow_root::python_edits_to_host_written_files_survive_shadow_reconciliation -- --exact
cargo test --locked -p agentos-native-sidecar --test filesystem shadow_root::python_shadow_writes_preserve_symlinks_and_mount_boundaries -- --exact
The second regression checks symlink preservation, non-root mount exclusion, preservation of the hidden root file after unmount, and that guest-only directories do not acquire staging stubs.
Additional passing existing tests (each run in its own process):
Local build details: Linux arm64, Rust 1.98.1, Node 24.21; native Rust source plus a freshly built V8 bridge from the checkout. The existing JavaScript regression used published 0.2.22 WASM command assets. Due to local GNU linker memory limits, the actual builds used cargo rustc --locked -p agentos-native-sidecar --test filesystem -- -C link-arg=-Wl,--no-keep-memory (and --test python) followed by exact-filter invocations of the resulting test binaries. The existing python_split cases were activated with NEXTEST=1 and run separately. This was not a full-suite run.
Formatting checks, git diff --check, and node scripts/verify-fixed-versions.mjs pass.
Existing failures confirmed on the clean baseline
Two broader tests fail identically on this patch and an unmodified c3698e6 worktree:
python_split::python_workspace_mount_respects_read_only_root_permissions: Python initialization emits an EROFS: read-only filesystem: / warning while installing the filesystem bridge, causing the stderr assertion to fail.
shadow_root::shadow_sync_skips_every_live_normalized_mount_boundary: the fixture cannot overwrite memory mount shadow because that host shadow path does not exist (ENOENT).
These baseline failures are not claimed as fixed, and the broader test selection is not entirely green.
Scope
This is not a fix for #1901 or #2012. Exploratory empty-write/truncate/append tests reveal separate Python behavior; the PR does not claim those are fixed. It specifically fixes the stale shadow overwriting successful Python VFS writes, plus deletion-inventory handling when a kernel-only temporary file replaces a staged destination.
Activity
ankssjain commentedon Oct 6, 2026
Fix PR: #2023.
Fix validation
The focused fix preserves Python overwrite, read-modify-write, and temporary-file replacement of API-seeded files. Repeated filesystem API reads and a later Python execution all see the edited content. The regression's overwrite case fails on unpatched
c3698e6withoriginal\ninstead ofEDITED\n.Both new tests pass as root and as an unprivileged UID 1000:
The second regression checks symlink preservation, non-root mount exclusion, preservation of the hidden root file after unmount, and that guest-only directories do not acquire staging stubs.
Additional passing existing tests (each run in its own process):
shadow_root::guest_edits_to_host_written_files_survive_shadow_reconciliationshadow_root::shadow_direct_deletions_before_first_sync_propagate_into_kernel_vfsshadow_root::shadow_type_replacements_cover_file_symlink_directory_matrixshadow_root::guest_rename_moves_a_broken_symlink_without_resurrecting_its_sourceshadow_root::shadow_mode_change_updates_an_existing_kernel_directoryshadow_root::guest_chmod_zero_preserves_descendant_deletion_inventoryshadow_root::failed_shadow_directory_deletion_retries_after_unmounted_mountpoint_is_removedshadow_root::unreadable_shadow_subtree_does_not_delete_kernel_children(UID 1000)python_split::python_runtime_executes_workspace_py_file_by_pathpython_split::python_runtime_mounts_workspace_over_the_kernel_vfspython_split::python_runtime_supports_file_delete_and_renamepython_split::python_runtime_supports_symlink_readlink_and_metadatapython_split::workspace_files_are_shared_between_javascript_and_python_runtimesLocal build details: Linux arm64, Rust 1.98.1, Node 24.21; native Rust source plus a freshly built V8 bridge from the checkout. The existing JavaScript regression used published 0.2.22 WASM command assets. Due to local GNU linker memory limits, the actual builds used
cargo rustc --locked -p agentos-native-sidecar --test filesystem -- -C link-arg=-Wl,--no-keep-memory(and--test python) followed by exact-filter invocations of the resulting test binaries. The existingpython_splitcases were activated withNEXTEST=1and run separately. This was not a full-suite run.Formatting checks,
git diff --check, andnode scripts/verify-fixed-versions.mjspass.Existing failures confirmed on the clean baseline
Two broader tests fail identically on this patch and an unmodified
c3698e6worktree:python_split::python_workspace_mount_respects_read_only_root_permissions: Python initialization emits anEROFS: read-only filesystem: /warning while installing the filesystem bridge, causing the stderr assertion to fail.shadow_root::shadow_sync_skips_every_live_normalized_mount_boundary: the fixture cannot overwritememory mount shadowbecause that host shadow path does not exist (ENOENT).These baseline failures are not claimed as fixed, and the broader test selection is not entirely green.
Scope
This is not a fix for #1901 or #2012. Exploratory empty-write/truncate/append tests reveal separate Python behavior; the PR does not claim those are fixed. It specifically fixes the stale shadow overwriting successful Python VFS writes, plus deletion-inventory handling when a kernel-only temporary file replaces a staged destination.
fix(native-sidecar): keep guest writes to API-seeded files (#2070)