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 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.
{
"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"}
}
}