Build and test a change#
Commands and outputs here follow the conventions of the operations guide.
Before you start#
- The git side,
GIT_SIDE, and thefieldhelper of the daily loop (once, step 2). - The project's build and test command (here its
check.sh).
There are no workspaces yet (M5), so an agent that must build or test its change before it lands
does so in a git worktree of the git side, at the head it read, and commits the files it made there.
The script that starts the agent exports first, so that the review branch holds main's head, and
takes the pair that the export prints: the commit to check out, and the changeset it is.
1. Check out main's head in a worktree#
EXPORT=$(swarmops git export "$GIT_SIDE" --ref main --branch review)
SHA=$(printf '%s\n' "$EXPORT" | field result.head_sha)
PARENT=$(printf '%s\n' "$EXPORT" | field result.head_changeset)
git -C "$GIT_SIDE" worktree add --detach ~/work/worker-1 "$SHA"Preparing worktree (detached HEAD 0eb07c3)
HEAD is now at 0eb07c3 a shorter greetingThe worktree is detached because git keeps git fetch from moving a branch that a worktree has
checked out.
2. Build and test#
The agent edits, builds and tests in it with whatever the project uses, here its check.sh:
(cd ~/work/worker-1 && ./check.sh)check: ok3. Commit, land and remove the worktree#
Then it commits the files as they are on disk: one put per changed file, each named by its
absolute path, which the server reads itself
(The tools agents get), with parent the changeset the
worktree is. The commit tool takes these arguments, and land the changeset it returns as its head:
{"message": "greet the world", "parent": "1092352e3a895f0ae9301a5e0637b74ae36763b57d3e907897e0e0408a07830b", "changes": [{"path": "src/lib.rs", "op": "put", "content": {"file": "/home/me/work/worker-1/src/lib.rs"}}]}The same requests at the command line, and the worktree's removal after the land:
cat > change.json <<EOF
[{"path": "src/lib.rs", "op": "put", "content": {"file": "$HOME/work/worker-1/src/lib.rs"}}]
EOF
swarmops --as agent/worker-1 commit -m "greet the world" --changes change.json --parent "$PARENT"
swarmops --as agent/worker-1 land eac1712c5c1223fc75a5a022f695f8d7838641426072452150bb26d684b8a3e9 --key "$(uuidgen)"
git -C "$GIT_SIDE" worktree remove --force ~/work/worker-1{"ok":true,"op":"commit","api":1,"result":{"changeset":"eac1712c5c1223fc75a5a022f695f8d7838641426072452150bb26d684b8a3e9","parent":"1092352e…","root":"24c30d20…","files_changed":1,"blobs_written":1,"replayed":false},"meta":{"repo":"…","elapsed_ms":30,…}}
{"ok":true,"op":"land","api":1,"result":{"status":"landed","landed":"380650cb0351378d43c98d50298949cbd63a4ad4f1dfdfdbc2dbfde1d960a379","rebased_onto":"1092352e…","target":"main","base":"1092352e…","attempts":1,"relocated":false,"files_changed":1,"squashed_from":["eac1712c…"]},"meta":{"repo":"…","elapsed_ms":20,…}}What to keep in mind#
parentis the changeset the worktree stands for, which ismain's head only if nothing landed since the export.landmerges the change onto the head it finds, three-way, as it does for any commit. What ran in the worktree is the base, not that merge: CI on the review branch tests the merge.- A
putwrites a regular file unless it says"mode": "exec"(or"symlink"): name the mode of every script, or the commit drops its executable bit. worktree removeneeds--forcehere, because git sees the agent's edits as uncommitted. A killed agent leaves its worktree registered in the git side:git -C "$GIT_SIDE" worktree listshows it, asprunableonce its directory is gone, andworktree pruneforgets it.
Backup and restore
Copy a store that agents are writing, check the copy, and put it back after damage. Commands and outputs here follow the conventions of the operations guide.
The daily loop
The loop keeps two copies of the history in step: the store, where agents commit and land, and the git repository that people, CI and deployment use. The…