Artifact 597015337c61750bdd4ab62c90fa97602e1f1d5b96d9bcfebf5f29d7bf625af6:
- File
patterns/stage-aarch64.md
— part of check-in
[116787b89d]
at
2026-11-04 15:14:43
on branch protocol-v2-daemon
— ci.cr builds an aarch64 release, linked static, and an aarch64 stage pattern sends it
Intent: steps 1 and 2 of the aarch64 builder, given to aarch64-1 in post 679313f26a, the only aarch64 host that can show them. The maintainer, 2026-11-04, asked to "help ocx get us setup with the aarch64 builder to get those releases". aarch64 is linked static so that one binary depends on no host's libc (Loose Coupling): it runs on the musl node and on a BeagleBadge's glibc Debian. x86_64 links as it always has, since nothing has shown it needs static (Parsimony).
Expression: ci.cr lists aarch64 beside x86_64 in ARCHITECTURES, and its STATIC list names aarch64, whose build passes static: true to OCX::Staging.build. That adds --static to `shards build` (src/ocx/staging.cr). patterns/stage-aarch64.md is patterns/stage.md with its build naming aarch64. Its prose says the record is the source-to-binary mapping, that an aarch64 run is not imported into an x86_64 server, and that a person removes the shared files once a host has fetched them, so builds do not pile up. knowledge-map.json lists it, and docs/knowledge-map.md is regenerated.
Demonstrates: spec/ocx/ci_commands_spec.cr. `programs` lists both architectures; every x86_64 build asks shards for no --static; an aarch64 build on an aarch64 host asks for `build tiny --static` and makes a whole release. spec/guards/stage_pattern_spec.cr: the aarch64 pattern's block equals stage.md's with only the architecture changed, and it reads as a run with no unknown key. On aarch64-1, `crystal run ci.cr -- build OUT aarch64` from this tree went green: 698 examples, 0 failures; coverage 100.0%; 2 executables build. It left ocx2 (49704568 bytes, SHA-256 a826cac9ab1cd749ac3641691fcc0701707caa29f95c1dc17b0fded07a43fb15), release.txt and gate.txt. llvm-readelf shows that ocx2 has no INTERP and no NEEDED. An earlier static ocx2 from this host ran on the maintainer's BeagleBadge.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TUH8KLpFJamQoSGA4uyiGS (user: aarch64-1-ollama-session size: 3293)
The aarch64 stage pattern: [the stage pattern](stage.md) for an aarch64 builder, differing only in the architecture its build names. A session sends it the same way, with this file's own content-addressed raw URL: `ocx2 create "stage CHECKIN" --pattern URL --login LOGIN --clone CODE --to POST --of CHECKIN`, LOGIN and CODE naming the aarch64 builder's acting login and its clone's server-code, and `--of` the check-in's full hash. The run clones the server anonymously, opens CHECKIN and runs `crystal run ci.cr -- build $$workspace$$/release aarch64` there, niced. That build is linked `--static`, so its `ocx2` depends on no host's libc: one binary runs on the musl builder and on a glibc host such as a BeagleBadge's Debian. The builder needs Crystal as `shard.yml` pins it and the static libraries `ocx-node/Dockerfile` installs. OCX sends `ocx2`, `release.txt` and `gate.txt` as unversioned files under `releases/$$run$$/`, as the stage pattern does, and the builder's `ended` records each one's SHA-256, while its `started` names the check-in in full. That record is the mapping from source to binary. Import takes only the server's own architecture, since it smokes `ocx2` in the server's root, so an aarch64 run is not imported into an x86_64 server. A host that runs the build fetches its files by name and checks them against the `ended` hashes, keeping the build it runs for rollback. A person then removes the shared files, so builds do not pile up in the repository; once a build is lost, it is lost, and its source can still be found from the record. ```json { "workspace": [ { "step": "directory", "at": "$$root$$/$$process_user$$/projects/$$repository_name$$/$$run$$", "remove_on": "saved" } ], "sandbox": { "uid": 1000, "gid": 1000, "linux_capabilities": [], "network": true, "isolate_terminal": true, "ends_with_parent": true, "read_only": [ "/usr", "/bin", "/sbin", "/lib", "/lib64", "/etc/resolv.conf", "/etc/hosts", "/etc/nsswitch.conf", "/etc/ssl/certs" ], "read_write": ["$$workspace$$"], "homes": [], "created_files": [ { "at": "/etc/passwd", "text": "$$process_user$$:x:1000:1000::$$workspace$$:/bin/sh\n" } ], "proc": "/proc", "dev": "/dev", "tmp": "/tmp" }, "limits": { "wall": "2h", "cpu": 3600, "processes": 512, "file_size": "256M" }, "process": { "user": "stage-task", "command": [ "/bin/sh", "-c", "fossil clone $$server_url$$ $$workspace$$/clone.fossil && mkdir $$workspace$$/tree && fossil open $$workspace$$/clone.fossil $$checkin$$ --chdir $$workspace$$/tree --nosync && cd $$workspace$$/tree && nice -n 19 ionice -c3 crystal run ci.cr -- build $$workspace$$/release aarch64" ], "cwd": "$$workspace$$", "keep": {"stderr": "4K", "stdout": "4K", "stdout_when": "always"}, "unversioned_files": [ {"at": "$$workspace$$/release/ocx2", "as": "releases/$$run$$/ocx2"}, {"at": "$$workspace$$/release/release.txt", "as": "releases/$$run$$/release.txt"}, {"at": "$$workspace$$/release/gate.txt", "as": "releases/$$run$$/gate.txt"} ], "env": {"HOME": "$$workspace$$", "USER": "$$process_user$$", "PATH": "/usr/local/bin:/usr/bin:/bin"} } } ```