Repository navigation
feat(computer): add the computer artifact and the ubuntu-24.04 base - #354
Merged
earakely-scale merged 22 commits intoOct 28, 2026
Conversation
earakely-scale
force-pushed
the
edgararakelyan/fd-4347-m01-computer-artifact-type-and-an-ubuntu-2404-base-container
branch
from
October 27, 2026 19:26
d81c9cb to
ac3a200
Compare
…swap What is learned about an artifact version after it's written, such as the images a computer artifact inspects at put and, later, what booting them found, has nowhere to go. Artifacts gain a metadata dict outside their versioned fields, which merge_metadata changes by compare-and-swap on metadata.updated_at, as envs and agents do. The golden wire files gain the empty metadata each artifact now writes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A computer artifact is the base a computer env composes a driver and apps onto: its architectures, its OS (linux), its session user, its settings, and its container form, whose image is a docker_image artifact pinned by id and version. Other OS types and forms are refused as not yet supported. put reads each form's image from its registry before writing: the index has a linux manifest for every architecture, and the os-release of the lowest layer holding it names the declared distribution and version. A mismatch refuses the put, naming its form and architecture; an image no registry serves yet is recorded uninspected. What was read goes in metadata.validated_images. In a bundle, forms.container.image takes what an image key takes, a Dockerfile in the folder, a registry ref or an image artifact, so the resolver now writes images for image keys inside a table. The registry API gains manifest and blob reads, with the token handshake pin_digest uses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
aliases.toml gains an [artifacts] table, read and checked like [agents] and [envs]: each alias names an artifact the bundle has by its folder. A task's reference to an installed bundle's artifact alias is pointed at that artifact and pulled in, unless the store has one of the alias's id, and an entry's reference to one is refused, as env aliases are. The run says what each artifact alias names, and plugin list shows them. An alias may now hold '.', so a versioned name like ubuntu-24.04 is one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The computers bundle ships one base, ubuntu-24.04: Ubuntu 24.04 for amd64 and arm64 with XFCE on Xvfb, D-Bus and AT-SPI, and Python 3 with venv and Tk for drivers and the probe's Fixture Desk. tini starts supervisord, which runs every program as the session user, user (uid 1000); nothing runs as root. The resolution and time zone settings arrive as AGENTENV_SETTING_<NAME> and reach every program, and a value it can't use stops it at start. A driver or an app adds its services as programs in /etc/supervisor/conf.d. Its aliases.toml names it ubuntu-24.04. Its image joins the published images, so a release builds it on both platforms, checks its desktop comes up, and publishes ghcr.io/scaleapi/agentenv-ubuntu-24.04:src-<hex>; Dependabot keeps its pinned Ubuntu base current. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ocal registry Built from its folder, the base's desktop runs as user (uid 1000) with every program under supervisord, at the resolution and in the time zone the deploy passes, with a window manager and AT-SPI up; a setting it can't use stops it with exit 2. Pushed to the local registry, it registers as a computer whose image put reads back as Ubuntu 24.04. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
agent-env artifact computer put --id <id> <folder> writes the computer a folder's artifact.toml declares, outside a bundle: its form's image key written as a bundle writes one, from a Dockerfile in the folder as its build context alone, a registry ref pinned now, or the image artifact it names. Its images are read from their registries first, and --no-inspect skips that, leaving each form uninspected. get prints a computer as JSON, with what put read. The fake OCI registry the computer tests share moves to tst/util. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The XFCE session exits whenever X does, within supervisord's three start seconds, so ten quick Xvfb deaths left the desktop program FATAL. It now counts as started at once; start-desktop already waits for X. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ndow manager The desk sizes itself to the screen at its origin and writes its widgets' screen boxes once it starts. A window manager that adopts it after that moves it under its decorations and panel, so the boxes it wrote were off by the move. Asking for fullscreen keeps it where it put itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…4 base The scaffold's desktop was a hand-written image with a start script. It's now the base with the reference driver and the Fixture Desk added as supervisord programs, built from a base the tests build once. The base's start-desktop gains with-x, which runs any program once X answers, as it already ran the XFCE session. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ifact, inspected or not put read the form's image artifact only to inspect it, so put(inspect=False) wrote a computer naming a missing artifact, a missing version or a file. It now loads the image on every put and refuses one the store doesn't hold. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t the base's start Any digits passed the check, so a width Xvfb or X can't use left a container whose desktop never came up, not the exit 2 a setting it can't use gives. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…its registry serves DockerImageArtifact.put records the image store's tag with no digest, so a single-platform image's manifest digest came out as the whole tag and its layers couldn't be read. Inspection now pins such a ref first, as put_ref does, and records what it read by that digest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ublished image The planner already registers a shipped image published for a bundle image's sources, the base's among them; this shows it does for ubuntu-24.04, with nothing built and both architectures read back from the registry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… of artifact metadata A stored universe's metadata key holds its legacy metadata file ids, the key artifact metadata now uses, so reading one filled its artifact metadata with them, and merging into it would have left a document no version can load. A universe reads the key as its legacy ids only and keeps no artifact metadata. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… they name The planner kept a table, uses and stored versions apiece for agent, env and artifact aliases, and the plan three fields; one mapping by kind holds them now, so a kind of alias adds one entry rather than a set of fields. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bundles write computers, and the CLI's folder put kept a second reader of the image key the bundle resolver owns. It can come back as a one-entry bundle run if a caller needs it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d type what put records The platform manifests and os-release reading move out of the computer artifact into image_store/image_contents, which a disk form's inspection can reuse; the artifact keeps the check of what it declares against what was read, each result an ImageCheck. put_folder goes with the CLI. Tests serve their fake registry with Registry().serve(monkeypatch) in place of patching three functions apiece. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pin_digest's check and the manifest and blob reads each chose the scheme, traded Basic credentials for a Bearer token and mapped a registry's answers to errors. The check now runs on the repository reader they share, with HEAD added, so a refusal from any read gives the same advice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s base arg, read an unreadable layer Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…a failed check Moving the os-release reading out of the computer artifact left the layer's download and removal outside the error the caller turns into a failed form, so a full disk printed a traceback. Both reads now raise ValueError for a file error, naming the blob. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… ships Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
earakely-scale
force-pushed
the
edgararakelyan/fd-4347-m01-computer-artifact-type-and-an-ubuntu-2404-base-container
branch
from
October 27, 2026 22:31
77a90dc to
3e2870d
Compare
… uninspected, not refused tarfile reads zstd from Python 3.14; before it, a layer of that media type is recorded as uninspected with the reason, as a context-only image is. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
earakely-scale
deleted the
edgararakelyan/fd-4347-m01-computer-artifact-type-and-an-ubuntu-2404-base-container
branch
October 28, 2026 00:44
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.
M0.1 of the CUA env epic (FD-4345): the
computerartifact and theubuntu-24.04base, FD-4347. It also folds in M0.10,[artifacts]aliases (FD-4381), and closes M4.6, publishing the base from the release workflow (FD-4385); M4.6's disk form moves to M4.1 (FD-4363). It follows the reviewed design, CUA Env M0.1: Computer Artifact and ubuntu-24.04 Base, with all 16 choices confirmed.What's in it
Artifact metadata. Every artifact gains a
metadatadict outside its versioned fields.merge_metadatachanges it by compare-and-swap onmetadata.updated_at, as envs and agents do. The golden wire files gain the emptymetadataeach artifact now writes. An environment universe is the exception: its storedmetadatakey already holds its legacy metadata file ids, so it reads that key as those ids only, keeps no artifact metadata, and refusesmerge_metadata.The
computerartifact type (ComputerArtifact,computer). It declares:arch;os, onlylinuxfor now: distribution, version (orrolling), display;session.user;config.<name>settings, each with a default, a phase and a scope; build-phase settings are task-scoped;forms.container, whoseimageis adocker_imageartifact pinned by id and version, withinit = "supervisord"andinput_channel = "env".Other OS types and forms are refused as not yet supported.
Inspection at
put. Each form's image is read from its registry, with no Docker and no full pull:linux/<arch>manifest for every declared architecture;os-releaseof the lowest layer holding it names the declared distribution and version.A mismatch refuses the
put, naming the form and architecture. An image no registry serves yet, such as a build context alone, is recordeduninspected. What was read goes inmetadata.validated_images. The registry API gainsread_manifestandread_blob, on one request path withpin_digest.store/image_store/image_contents.pyreads an image's platform manifests and itsos-releasefrom them, and each result is anImageCheck.In a bundle,
forms.container.imagetakes what an image key takes:{ dockerfile = "<path>" }, built from the folder;{ ref = "<image>" }, pinned from a registry;{ artifact = "<id>", version = <n> }.The resolver now writes images for image keys inside a table.
[artifacts]aliases (M0.10).aliases.tomlgains an[artifacts]table, read and checked like[agents]and[envs].plugin listshows them.., soubuntu-24.04is one.The
computersbundle and theubuntu-24.04base:venvand Tk, for drivers and the probe's Fixture Desk.user(uid 1000), so nothing runs as root.resolutionandtimezonearrive asAGENTENV_SETTING_<NAME>and reach every program. A value it can't use stops it at start with exit 2./etc/supervisor/conf.d; one that needs X runs asstart-desktop with-x <command>, as the XFCE session does, which waits for X.aliases.tomlnames itubuntu-24.04.published_images, so a release builds it on both platforms, checks its desktop comes up, and publishesghcr.io/scaleapi/agentenv-ubuntu-24.04:src-<hex>. A released install then writes the base over that published image, pulled by its digest, instead of building it. Dependabot keeps its pinned base current.The scaffold moves onto the base (row 16).
tst/integration/computer/scaffold's desktop was a hand-written image with a start script. It is nowFROMthe base, with the reference driver and the Fixture Desk added as supervisord programs. The tests build the base once (a session fixture intst/integration/computer/conftest.py). The probe, recording, recording-chaos and agent tests all run on it, so M0.7 only deletes the scaffold bundle.Where this differs from the design
[artifacts]table ships with the base it names. FD-4381 closes with this PR.os-releaseis read from the bottom up: the lowest layer holding it, normally the OS's own, so only that layer is fetched (about 30 MB for Ubuntu). A later layer that rewrites it isn't seen.Artifact.getreads no alias. The planner rewrites every alias a task names to its target id, and nothing else looks one up yet.container/, so the published image's build context is the same folder the bundle builds from, and a deploy finds it by its sources.firmware.Testing
The unit, integration and chaos runs and the probe ran on an amd64 EC2 box (c6i.4xlarge, Ubuntu 24.04, Docker 29) and on the laptop (arm64, Docker Desktop), on the branch before its review fixes. Every later change has its own unit test and touches neither the base image nor the probe path:
metadatakey;next-env-eventexample makes seven shipped bundles.The unit tier and
tst/integration/computerwere rerun on the laptop after the rebase.Unit: 11,094 passed locally on Python 3.14 after the rebase. The one failure was
parse_test.py::test_deep_nesting[json]: it fails under xdist on 3.14 on main too, and passes alone and in CI on 3.12. Coverage, branches included:computer.py98%, the CLI 100%,registry_api.py94%.Integration (
tst/integration/computer, 15 tests):user(uid 1000) with the resolution and time zone a deploy passes;big,40000x800,1280x0andMars/Olympuseach stop it with exit 2;putreads back.The release path, built as
publish-pypi.ymlbuilds it (docker build --platform … --provenance=false, no cache):infra_build.py smokedu)On EC2 both builds were pushed as
-amd64and-arm64and joined withdocker buildx imagetools createinto one index holding linux/amd64 and linux/arm64.puton EC2, run through anagent-env artifact computerCLI this PR has since dropped (bundles write computers):image = { ref = "<the joined index>" }passed, os-releaseubuntu/24.04, in 2.4 spublic.ecr.aws/docker/library/ubuntu:24.04passed, 3.2 sforms.container (arm64): it has no linux/arm64 image, only linux/amd64os-release has VERSION_ID='24.04', not '22.04'{ dockerfile = "Dockerfile" }uninspected (only its build context is stored, so its first boot checks it)Chaos: 22/22 on both. The image was the base plus the reference driver and the Fixture Desk. After each of these, every program was RUNNING again and the probe passed at basic (25 checks):
docker restart, back in 3.8 s on EC2 and 3.9 s on the laptop;xdpyinfo.The probe at full, three runs in a row, on each: 51 passed, 0 failed, 1 not applicable, recording included. Afterwards all 22 processes ran as
user, and each of the five supervisord programs was RUNNING.Found by chaos and the e2e, and fixed here:
startsecs=0), andwith-xwaits for X; the new integration test fails on the old config.Maintainability pass (reviewed in the PR):
EntityKind, andPlan.alias_usesreplacesaliases,env_aliasesandartifact_aliases.pin_digestand the new reads share one request path (_Repository, now with HEAD).image_contents, and inspection results areImageChecks.Registry().serve(monkeypatch), and the integration tests sharedocker().put_folderare gone, and with them a second reader of the image-key syntax the bundle resolver owns.Greptile: three findings, each fixed and answered:
put(inspect=False)didn't check the image artifact;passed.🤖 Generated with Claude Code


Confidence Score: 5/5
The reviewed changes appear safe to merge.Summary
Adds the
computerartifact, the Ubuntu 24.04 desktop base, artifact aliases, and release-image publishing.ubuntu-24.04.Diagram
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Computer put] --> B[Load referenced image artifact] B --> C[Check Linux architectures] C --> D[Read OS release] D --> E{Result} E -->|Matches| F[Record passed] E -->|Unsupported compression| G[Record uninspected] E -->|Mismatch or read failure| H[Refuse put] F --> I[Store computer] G --> IReviews (9) · Last reviewed commit: "fix(computer): write an image whose zstd..." · Reviewed by Greptile