Repository navigation
feat(artifacts): an environment artifact holds several files by path - #424
Merged
earakely-scale merged 7 commits intoNov 1, 2026
Conversation
An environment artifact is now its environment's files by path. One file whose path is its filename is stored as the one wrapped file_artifact_ref every environment artifact had, document for document; several files, or one inside a folder, are stored as file_artifact_refs, the shape a file universe uses. - get_file_artifacts() reads either shape; get_file_artifact() refuses a collection. - A load hands each file to the env's data/add with its path in the data-objects marker, through MCP server, website and signed-URL loads; a one-file artifact sends the part it always has. Pre-v1 loads refuse a collection. - Staging into an agent or container writes each file at its path. - An environment artifact folder may hold several files; a lone file, even in a subfolder, is still a one-file environment. A universe folder is unchanged and refuses to export a multi-file environment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d round-trip a universe that holds them - A load of several files stages them in a new folder under the server's data folder and removes it after, however the load ends, so a file an earlier load left can't be in the way of this load's folders. One file is still staged at its filename. - One file's name is still one plain name, on the v1 and pre-v1 loads. - verify_universe_load_export_roundtrip compares an environment of several files export to export only, as a non-JSON one, and bundles each of its files at original/<name>/<path>. - Exporting an environment whose one file is kept at a path other than its filename names that, for a top-level file too. - Tests: a bundle environment of several files is reused, and rewritten when one changes; a website loads several files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
derive_path_id(base, path) is the id a universe's file, an environment's file written from a folder and a collected file are written under: <base>__<16 hex of the path's sha256>. FileArtifactUniverse.put_bundled and put_existing, EnvironmentArtifact.from_toml and collect_artifacts each built it themselves; the ids are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test_a_second_hold_restarts_the_release_timer slept 100 ms twice against a 150 ms max_hold_ms, leaving about 50 ms for each tool call; under a loaded parallel run the first timer could fire before the second hold landed. It now holds for 1.5 s with the second hold 0.3 s after the first, so a slow loop has 1.2 s to land it, and asserts the keys go up no sooner than 1.5 s after the second hold was sent. That holds however slow the machine, and fails without the restart: the keys then go up about 1.2 s after it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…S has too test_the_event_pipe_is_not_left_open_in_the_server listed /proc/self/fd, which macOS doesn't have, so it failed there every time. /dev/fd lists the process's open descriptors on macOS and Linux alike (on Linux it links to /proc/self/fd). On macOS it now passes, and fails when the bridge leaves the pipe's write end open (13 descriptors against 12). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…les keeps its originals The judge's bundle puts such a service's originals at original/<service>/<path>, but both prompts named only original/<service>.json. The system prompt now describes that folder and how to judge the service: read what it was seeded with, check export_1 still holds it in meaning, corroborate with export_2; the user prompt points to it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…not on the sandbox's host A server whose sandbox is its own container ran the load folder's mkdir and rm through the sandbox's exec. On a VM sandbox in container mode (the local provider) that runs on the host, so the folder was never removed from the server and a root host could get /data made on it. They now go through docker exec into the sandbox's container, as staging's own writes do, and a container sandbox's exec failing raises. Found by a chaos run of #424. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
earakely-scale
deleted the
edgararakelyan/fd-4818-environment-artifacts-with-more-than-one-file
branch
November 1, 2026 00:58
earakely-scale
added a commit
that referenced
this pull request
Nov 1, 2026
… and load a computer's snapshot back into it Built on environment artifacts of several files (#424): - snapshot_env keeps an environment whose card declares a file tree, a computer's, file by file by its paths in its environment artifact, under a grant sized for a file tree, instead of refusing it. Each file is written as derive_path_id(<snapshot>-<environment>, path). snapshot_env_state takes data_get_params for every environment's data/get. - snapshot_computer_env is snapshot_env with the computer's file_paths as data/get's paths, and writes the same environment universe snapshot_env does, in place of a FileArtifactUniverse. - A computer env loads an environment artifact, its own, by writing its files at their paths under the session user's home through the driver's data/add, and a universe by loading its own environment and leaving the others out with a warning. load_artifact of a computer's snapshot so restores it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
An environment artifact can now hold several files, each kept by its path. Every environment artifact today is the one-file case of that, and is stored, read and loaded exactly as before.
file_artifact_refevery environment artifact had, document for document, whicheverputwrote it. Golden documents are unchanged.file_artifact_refs: {path: ref}, the shape aFileArtifactUniverseuses.data/addwith its path, asdata/getreturned them.Issue: FD-4818. Design (approved): Environment Artifacts With Several Files. This is the prerequisite for #423 (FD-4382), which is rebuilt on it next.
How
EnvironmentArtifactputtakesfile_artifactorfile_artifacts(by path), exactly one. A single file whose path is its filename is written asfile_artifact./(utils.paths.validate_file_path).get_file_artifacts()reads either shape:{filename: file}for one file, legacy unpinned documents included.get_file_artifact()is unchanged for one file and refuses a collection, namingget_file_artifacts.data/add. MCP server envs, website envs and signed-URL loads for a plugin's env send one file part per file, each with its path in the data-objects marker (_deployment.file_parts)./data/<filename>as before; its filename is still checked as one plain name./data/.agentenv-load-<8 hex>/,/tmp/data/...for a website), removed after the load however it ends (_deployment.staged_paths,_remove_from_container). So a file an earlier load left can't be in the way of this load's folders, and repeated loads leave nothing behind.legacy_load_file).load_artifactwithagent_name,container_nameorsandbox_name):stage_files_into_container, the helper that stages a file universe into an agent.verify_universe_load_export_roundtripcompares an environment of several files export to export only, as it does a non-JSON one, since it isn't one JSON state. The agent-judge bundle puts each of its files atoriginal/<name>/<path>, and both judge prompts describe that folder: the judge reads what the service was seeded with and checksexport_1still holds it in meaning, corroborated byexport_2.EnvironmentUniverseArtifact.get_file_artifacts()lists<environment_name>/<path>for each file of a collection; a one-file environment is listed at its filename, as before. Exporting a universe that holds a multi-file environment is refused (Unwritable), since a universe's folder holds one file per environment.<id>__<16 hex of its path's sha256>, as a file universe's are. That rule is now one function,store.ids.derive_path_id, whichFileArtifactUniverse.put_bundledandput_existing,EnvironmentArtifact.from_tomlandcollect_artifactsall use; no id changes.descriptionbeside several files is refused.Readers outside this repo
The hub reads
file_artifact_reffrom environment documents and callsget_file_artifact(). One-file artifacts are unchanged, so nothing there breaks. A collection in the hub's store would be listed as missing files or fail a universe download, and an agent-env older than this release reads one as malformed. Supporting collections in the hub is a follow-up after CUA OSS v1, since computer envs aren't used through the hub yet.Not in this PR
putseveral files; a bundle folder writes one.Testing
Unit: 12,295 passed, none failed, on macOS.
Also folded in, two test fixes:
test_the_event_pipe_is_not_left_open_in_the_server(Hermes) counted open file descriptors through/proc/self/fd, which macOS doesn't have, so it failed there every time. It now lists/dev/fd, which macOS and Linux both have (on Linux it links to/proc/self/fd). On macOS it passes, and still fails when the bridge leaves the pipe's write end open (13 descriptors against 12).test_a_second_hold_restarts_the_release_timer(protocol computer queue) flaked once under a loaded parallel run, with about 50 ms of slack per tool call. It now holds for 1.5 s with the second hold 0.3 s after the first, and asserts the keys go up no sooner than 1.5 s after the second hold was sent. Being slow can't fail it, and it still fails without the restart (keys up 1.20 s after the second hold); it passed 40 of 40 times in parallel on a loaded CPU.New and extended tests:
putthrough either argument writes the same document;get_file_artifactrefuses;putwith neither or both arguments, are refused;/, and nested paths are staged under their folders.stage_files_into_containerfor a container sandbox, a VM's container and the VM host.descriptionbeside several files is refused;Chaos run (two Linux EC2 boxes plus the laptop, real items MCP server on local Docker; the box harness is not committed):
a/b.json,.., empty), a store failure while staging, and a load cancelled while staging.data/addwith a clear error;.DS_Storeand__pycache__beside one file still give a one-file environment; links inside the bundle are kept, links outside refused.mkdirandrmran on the sandbox's host, not in the server, so the folder was never removed. They now go throughdocker execinto the server's container, as staging's writes do. Regression test:test_artifact_staging.py::test_a_server_whose_vm_sandbox_is_its_container_stages_and_removes_a_loads_folder_through_docker.Integration, laptop (local Docker):
inbox, then a two-file environment (inbox/a.json,top.json) in oneadd_data: the second load isn't blocked by the first one's file, and the state holds both files' items, timed by their total size.verify_sandboxand collected back bycollect_artifacts.🤖 Generated with Claude Code