Home / Projects / Orchestrata Ultimate / Decisions / ADR index / ADR-0091

ADR-0091: The janus fork links the declared SWI-Prolog through a path guix gc keeps alive

field value
Status Accepted — Accepted (Mohamed, 2026-10-12), superseded in part by ADR-0095 (2026-10-12): the SWI source moves from the guix profile to the project-owned prefix; the declaration and its checks stand
Date 2026-10-11
Register factory (docs/adr, 4-digit ids)
Source docs/adr/0091-the-janus-fork-links-the-declared-swi-prolog-through-a-path-guix-gc-keeps-alive.md — recorded on branch docs/adr-register-0080-0085 at 3f68a11a5, not yet on master
Wiki register entry ADR-0091 (wiki status: Accepted)

← ADR-0090 · ADR-0092 →

The record

Status: Accepted (Mohamed, 2026-10-12), superseded in part by ADR-0095 (2026-10-12): the SWI source moves from the guix profile to the project-owned prefix; the declaration and its checks stand · Date: 2026-10-11

Context

The prod kernel (orchestrata-kernel.service, /opt/orchestrata) failed at the 13:00 boot on 2026-10-11 and stayed down until 22:51. Its journal at 13:03:38:

RuntimeError: Failed to RTLD_GLOBAL-preload libswipl at
'/usr/lib/swipl/lib/x86_64-linux/libswipl.so.10'

How janus finds libswipl

The kernel runs Prolog through our own fork of janus-swi, vendored at scripts/_janus_patched/ (base 1.5.3) and built in place on every uv sync (docs/free-threaded-prolog.md §d: the built .so "is a compiled artifact tied to this machine's SWI-Prolog"). Prod installed it from file:///opt/orchestrata/scripts/_janus_patched.

At build time, the fork's setup.py line 5 reads SWIPL=os.getenv("SWIPL") or "swipl": it takes swipl from PATH, runs swipl --dump-runtime-variables, and bakes the reported PLLIBDIR into the extension as RUNPATH (-Wl,-rpath=<PLLIBDIR>,--enable-new-dtags). With Guix that PLLIBDIR is a resolved store path, /gnu/store/<hash>-swi-prolog-…, which is not a GC root: once the profile moves on, guix gc may delete it.

At run time, core/janus_bootstrap._resolve_libswipl_path tries, in order: (1) ORCHESTRATA_LIBSWIPL, declared nowhere; (2) swipl on PATH; (3) ldd on the janus extension, i.e. the baked RUNPATH; (4) a hardcoded /usr/lib/swipl/lib/x86_64-linux/libswipl.so.10.

What happened

What the record already said

The restore (2026-10-11 22:51, Mohamed: "you run the restore")

systemctl --user set-environment ORCHESTRATA_LIBSWIPL=$HOME/.guix-profile/lib/swipl/lib/x86_64-linux/libswipl.so.9 plus starting the kernel and the five units earlyoom had killed (D21). The kernel preloaded libswipl from the profile, started the Prolog worker and listens on CXP 50053, 0 restarts. This lasts until the user manager restarts (logout or reboot).

Decision (proposed)

  1. Declare SWI-Prolog once. env/toolchain.env gains SWIPL, the absolute path of the Guix profile's bin/swipl (~/.guix-profile/bin/swipl, the profile link, not a store path). It is read exactly like CC/CXX under ADR-0089: missing, bare-named or non-executable is refused, with no PATH fallback. build_janus_ft.py, worktree provisioning and the deploy's uv sync pass it to the fork's build.
  2. The fork links through the profile, not the store. The fork's setup.py takes SWIPL from the declaration only (drops or "swipl"), and bakes the declared profile's library directory, unresolved, as RUNPATH. The profile is a GC root, so that path survives every guix gc; tier 3 then resolves on every start under any PATH, and no runtime environment variable is needed.
  3. A changed library fails loudly. The fork records the SWI-Prolog version it was compiled against. janus_bootstrap refuses to start when the loaded libswipl reports a different version (a profile upgrade within the soname). verify_deployment_identity.py refuses a deployment whose janus RUNPATH does not resolve, and the next promote rebuilds.
  4. Correct the stale record. docs/free-threaded-prolog.md's header names the declared SWI-Prolog (Guix 9.3.8, libswipl.so.9); tier 4's hardcoded .so.10 path is removed and the error names the declaration.
  5. Rebuild what is already broken. Prod's janus is rebuilt through promote; any worktree venv whose RUNPATH does not resolve is rebuilt by provisioning.

Alternatives rejected

Consequences

Provenance

Relations of ADR-0091

Rulings cited

None.

Specs cited

None (or no spec page exists for the ones named).