Home / Projects / Orchestrata Ultimate / Decisions / ADR index / ADR-0095
ADR-0095: SWI-Prolog is built from pinned source into a fixed project-owned prefix, and the runtime path leaves guix
| field | value |
|---|---|
| Status | Accepted |
| Date | 2026-10-12 |
| Register | factory (docs/adr, 4-digit ids) |
| Source | docs/adr/0095-swi-prolog-is-built-from-pinned-source-into-a-fixed-project-owned-prefix-and-the-runtime-path-leaves-guix.md — recorded on branch docs/adr-register-0080-0085 at 3f68a11a5, not yet on master |
| Wiki register entry | ADR-0095 (wiki status: Accepted) |
The record
Status: Accepted · Date: 2026-10-12 · Ruled: 2026-10-12 (Mohamed: "agreed")
Context
-
The directive. Mohamed, 2026-10-12, verbatim: "why do we need guix, assignment, research installing it guix free".
-
Evidence. A read-only measurement on this host, committed as
plans/research/2026-10-12-guix-free-toolchain/. It holdsREPORT.md, the build scripts, the notes and the logs (2d7dbbcad). Repo facts are from origin/master22363abb2, which is also what/opt/orchestrataruns. -
What guix supplies to this project. Nothing else is invoked.
From guix Used by SWI-Prolog 9.3.8, plus its closure (gmp, ncurses, zlib, glibc libm, libgcc_s) env/toolchain.env:41(SWIPL=/home/mohamed/.guix-profile/bin/swipl) → the janus build and its RUNPATH; the prod kernel (the only prod unit mapping SWI); 13 bare-swiplcall sites; the test container through read-only/gnu/storeand~/.guix-profilemounts (provision_app_container.sh:54-56,93-96,288,infra/terraform/test_fleet.tf:88-103)age / age-keygen 1.3.2 scripts/secrets_cli.py, external-tools gatedirenv 2.37.1 .claude/hooks/ast-incremental-refresh.sh:97,deploy/install-claude-config.sh:189incusd 6.15 /etc/systemd/system/incus.service(untracked); runs every Incus fleet -
The cost of the current arrangement. ADR-0091 keeps the SWI path alive against
guix gc. Even so, a garbage-collected store path took prod governance down 13:00→22:51 on 2026-10-11 (env/toolchain.env:27-29). -
Measured guix-free (REPORT.md §2-§4).
- SWI 9.3.8 builds from tag
V9.3.8(dfe256b5434fd62af33aeff162f9127dd365125dplus 7 pinned submodule commits). It needs only distro gcc 14.2.1, cmake and ninja, and no sudo. Packagesclib;http;nlp;plunit;sgml;ssl;zlib;real 0m41.851s; 9.9 MB. - It loads the same 26 libraries as guix's 9.3.8, with identical output.
- janus rebuilt with
scripts/build_janus_ft.pyandsetup.pyunchanged shows 0 guix paths under/usr/bin/ldd. The real tests pass:tests/core/test_janus_bootstrap.py28,test_prolog_worker_parallel.py3, toolchain guards 14,test_build_janus_ft.py24. - age and direnv are published as static upstream binaries whose sha256 equals the GitHub digest.
- incusd has no OpenMandriva package and no upstream daemon binary.
- There is no published 9.3.8 tarball (404). The pin is the git tag plus submodule commits.
- SWI 9.3.8 builds from tag
-
A live hazard measured on the way. janus takes its SWI home from the first
swiplon PATH. With a 10.0.2swiplfirst, a 9.3.8 janus aborts:boot.prc: incompatible VM-signature, rc 134 (REPORT.md §3.10, case E). This is the crashprovision_app_container.sh:211-217recorded on 2026-08-29. SettingSWI_HOME_DIR, or putting the 9.3.8binfirst, prevents it (cases F and G).
Decision (proposed)
- Own the SWI build.
- Build SWI-Prolog 9.3.8 from the pinned tag and submodules into a fixed,
versioned, read-only prefix outside every deploy root:
/opt/orchestrata-toolchain/swipl-9.3.8. - Set
CMAKE_INSTALL_PREFIXto that final path, with the REPORT.md §3.3 flags. - Mirror the source archive and record its sha256 in a committed pin file.
- A committed toolchain script builds and verifies the prefix (version, pin, checksum). The installers refuse to proceed when it is absent or does not match.
- Build SWI-Prolog 9.3.8 from the pinned tag and submodules into a fixed,
versioned, read-only prefix outside every deploy root:
- Declare it.
env/toolchain.envSWIPLnames the prefix'sbin/swipl. janus is rebuilt everywhere;scripts/_janus_patched/setup.pykeeps its absolute RUNPATH. - Make the SWI home unambiguous.
- Every unit and container override puts the prefix
binfirst on PATH. - The kernel unit also sets
SWI_HOME_DIRto the prefix'slib/swipl. - A test asserts the home janus resolves equals the declared prefix.
- Every unit and container override puts the prefix
- age and direnv come from the pinned upstream static binaries (sha256
in REPORT.md §2). The install hints in
pyproject.toml:146,148andscripts/install_janus_ft.sh:207are updated. - Test container.
- The same prefix sits at the same path in the container, copied in the way D44 copies the code.
- The guixstore and guixprofile devices, their allow-list entries and
GUIX_PATH_PREFIXare removed. - The Arch
swi-prologpackage no longer decides anything, because the prefixbincomes first.
- incusd stays on guix as a scoped exception until a separate ADR decides its source. No guix-free option was measured for it.
- SWI 10.x is a separate, measured upgrade. REPORT.md §3.11 shows the janus tests also pass on 10.0.2, with two observed behaviour differences.
If accepted, this supersedes ADR-0091 for SWI-Prolog.
Alternatives rejected
- An OpenMandriva package. None exists for swi-prolog, age or direnv.
- Each environment's distro swipl. The host has none. The container has 10.0.2 while janus is built for 9.3.8, which aborts (case E). The copied venv also needs identical paths.
$ORIGINRUNPATH for a relocatable prefix. Measured rc 134 and rc 127 unless every process setsSWI_HOME_DIR, because SWI finds its home from PATH or its compiled-in prefix.- conda-forge. It has no 9.3.8 and would add a second package manager.
- PyPI. It has no SWI distribution.
- Keep guix with GC roots (ADR-0091). That is the arrangement that failed in prod on 2026-10-11, and it keeps the container tied to the host's store.
Consequences
- Prod, dev and the test fleet resolve SWI from one project-owned path that no package manager can collect.
- A soname change in OpenMandriva's gmp.so.10, ncursesw.so.6, z.so.1 or ssl.so.3 forces a toolchain rebuild. The floors are GLIBC_2.34 and OPENSSL_3.0.0.
- The guix entries in the systemd user manager's PATH become dead entries. Guix remains on the host for incusd until that ADR.
- Not yet measured:
- the test container end to end;
- an incusd source build;
- the janus wheel's byte-reproducibility (two builds differed in the wheel
archive, while the
.somatched).
- The file-level change list is in REPORT.md §5.
Provenance
- Directive: Mohamed, 2026-10-12 (quoted verbatim in Context).
- Evidence:
plans/research/2026-10-12-guix-free-toolchain/REPORT.mdand its logs (2d7dbbcad), produced by a read-only research agent. The agent made no repo, unit or container changes. - Recommendation: accept.
- Related: ADR-0089, ADR-0091, ADR-0092 (the plugin venv; REPORT.md §5 notes a janus-in-lock implication).
- Ruling: Mohamed, 2026-10-12, after a three-line summary (fixed-prefix build of the pinned 9.3.8; age and direnv from pinned upstream binaries; incusd stays on guix pending its own ADR): "agreed".
- ADR-0091 is superseded in part. Its guix-profile source and RUNPATH give
way to this prefix. Its
SWIPLdeclaration, its declaration-only build input and its version and RUNPATH checks stand, now pointed at this prefix.
Related ADRs
Rulings cited
None.
Specs cited
None (or no spec page exists for the ones named).