Orchestrata Ultimate › Plans › briefs › qwen › Q9-janus-swipl-declared

Q9 — the janus fork links the declared SWI-Prolog (ADR-0091)

Source: plans/briefs/qwen/Q9-janus-swipl-declared.md at 8a2cb42ad · last changed 2026-10-12 · first committed 2026-10-12 · 238 lines.

Prose pending.

Plan text

DESIGN_WAIVER: ADR-0091 (Accepted by Mohamed 2026-10-12: "agree with adr"). Read docs/adr/0091-the-janus-fork-links-the-declared-swi-prolog-through-a-path-guix-gc-keeps-alive.md on branch origin/docs/adr-register-0080-0085 first: git show origin/docs/adr-register-0080-0085:docs/adr/0091-the-janus-fork-links-the-declared-swi-prolog-through-a-path-guix-gc-keeps-alive.md. It is the design; this brief is the build order. ADR-0089 (Accepted, already on this branch) is the pattern you extend: env/toolchain.env and its readers.

Worktree: /home/mohamed/workspace/wt-JANUS-SWIPL, branch feat/janus-swipl-declared, head fb441c9a7 (the integration branch). Work ONLY there.

Two phases. Phase A ends in a STOP. Phase B starts only on a continuation prompt.


0. Rules (binding, both phases)

  1. A red test blocks completion, whoever owns it. Every failed, errored or skipped test needs a root cause with evidence (traceback, line, timing) and either a fix or a STOP naming the cause. "Outside my files", "pre-existing", "flaky", "infra" and "unrelated" are not statuses.
  2. No mocks, stubs, fakes, monkeypatch, skip, xfail, or -n/xdist. Builds are real builds of the fork; libraries are the real Guix SWI-Prolog. The existing resolver seams (dump_runtime_resolver=, ldd_resolver=, env=, path_override= in janus_bootstrap) are the sanctioned way to force a tier; use them, add no new kind of seam.
  3. Do NOT commit, merge, push, checkout, restore, reset, stash or clean. git add/show/diff/status/log are allowed. The orchestrator commits.
  4. Read Python through scripts/serialize_codebase_ast.py symbols|source|refs|who-imports. The AST toolkit does NOT index the vendored fork scripts/_janus_patched/ (excluded from the AST roots); read those files directly — that is the one sanctioned exception. Reading a file you are about to edit is fine.
  5. Absolute imports. os.environ.get(...). Typed signatures, docstrings. Keep the fork's diff against upstream minimal: change only the lines this brief names, in the fork's existing style (the fork is excluded from ruff/black on purpose — pyproject.toml extend-exclude).
  6. Never write the word "should". Every number comes from a command run in this session, quoted next to it.
  7. Production is read-only to you. You may readelf -d files under /opt/orchestrata. You never run promote_env.py, install-orchestrata.sh, ship, systemctl, incus, or anything that writes outside this worktree and its .venv. You never touch the test fleet.
  8. Any change outside the files listed in a phase → STOP and report why.

0.1 Running tests


1. Evidence (measured by the orchestrator, 2026-10-11/12)


A.1 The declaration

A.2 The fork's build (scripts/_janus_patched/setup.py)

A.3 The runtime resolver (src/orchestrata_ultimate/core/janus_bootstrap.py)

A.4 Docs

A.5 Tests (red first: show each failing before its implementation)

Find the existing test modules with refs/who-imports (tests/deploy/test_toolchain_sh.py, the worktree_toolchain tests, tests/core/test_janus_bootstrap.py, tests/scripts/test_build_janus_ft.py are the expected homes; confirm). Add, red first:

  1. declared_toolchain returns SWIPL; refuses a declaration missing SWIPL, with SWIPL=swipl (bare), and with a non-executable path. Real tmp declaration files; real executables (/usr/bin/gcc, /home/mohamed/.guix-profile/bin/swipl).
  2. load_toolchain (shell) exports SWIPL; the same three refusals.
  3. The fork's real build (the pattern test_build_janus_ft.py already uses): (a) SWIPL unset → the build fails and its output names env/toolchain.env; (b) SWIPL declared through a tmp PREFIX that is a symlink to /home/mohamed/.guix-profile → the built _swipl*.so's RUNPATH (readelf -d) equals <tmp prefix>/lib/swipl/lib/x86_64-linux exactly and contains no /gnu/store/.
  4. _resolve_libswipl_path: the ldd tier wins over a dump-runtime result that names a different library (seams); no tier resolves → the RuntimeError names env/toolchain.env and ADR-0091 and does not contain .so.10; a bare SWIPL=swipl in env= is never executed (tier 3 skips it).

A.6 Measure, then STOP

  1. Rebuild this worktree's janus with the declaration in the environment: env -u PYTHONPATH -u PYTEST_ADDOPTS SWIPL=/home/mohamed/.guix-profile/bin/swipl uv sync --reinstall-package janus-swi (quote the rc). Then readelf -d the built _swipl*.so (locate it with find .venv -name '_swipl*.so'): quote NEEDED and RUNPATH. RUNPATH must be /home/mohamed/.guix-profile/lib/swipl/lib/x86_64-linux.
  2. Under the systemd PATH, from /tmp: env -i HOME=$HOME PATH=/usr/local/bin:/usr/bin <worktree>/.venv/bin/python -c "from orchestrata_ultimate.core import janus_bootstrap as jb; print(jb.ensure_swipl_symbols_global()); import janus_swi as j; print(j.query_once('X is 6*7')); j.consult('t','ok :- use_module(library(http/json)).'); print(j.query_once('ok'))" — quote all three lines. No ORCHESTRATA_LIBSWIPL, no SWIPL set.
  3. The affected set over every changed file, then an authoritative forced run of it: quote test_totals, reused, rc. 0 failed, 0 errors, 0 skipped, reused 0.
  4. ruff check, black --check, pyright on the changed non-fork .py files; bash -n deploy/toolchain.sh; scripts/gate.py review-staged after git add of exactly the Phase A files. Quote each rc.
  5. Inventory, report only (do not change): every place, at build time and at run time, where the fork (scripts/_janus_patched/janus/*.py, e.g. _find_swipl.find_swipl) or our code invokes swipl, and what each does under the systemd PATH. Measure, do not guess.
  6. Write plans/briefs/qwen/Q9-report.md (sections: changes per file with line refs; red-first evidence quoted per test; A.6 outputs 1–5 quoted; anything you stopped on). Then STOP.

3. Phase B — fail loudly on a changed library (continuation prompt only)

Outline, refined in the continuation brief after Phase A's report:


4. Done-when (Phase A)