Home / Projects / Orchestrata Ultimate / Decisions / ADR index / ADR-0089
ADR-0089: Bare toolchain names fail the gate in Prolog, and the C toolchain is one declared file
| field | value |
|---|---|
| Status | Accepted |
| Date | 2026-10-11 |
| Register | factory (docs/adr, 4-digit ids) |
| Source | docs/adr/0089-bare-toolchain-names-fail-the-gate-in-prolog-and-the-c-toolchain-is-one-declared-file.md — permalink at d6bf58280 (unchanged on master since that commit) |
| Wiki register entry | ADR-0089 (wiki status: Accepted) |
The record
Status: Accepted · Date: 2026-10-11
Context
Two failures, measured on 2026-10-11, have one cause: a program named by a bare word is resolved from
PATH, and on this host PATH does not point where the code assumes.
- Interpreters. Session shells inherit the direnv environment of the primary checkout
orchestrata-ultimate-architecture(brancharchitecture/domain-model, ~530 commits behind master), so its.venv/binleadsPATH. Run from a worktree,python -c 'import orchestrata_ultimate'imported the primary checkout's stalesrc/. Anysubprocess.run(["python", ...])therefore executed another checkout's code. - Compilers.
~/.guix-profile/binalso precedes/usr/bin, so a baregccis Guix GCC 16.2.0. It fails the vendored janus-swi build against the system swipl headers, and Guix'sC_INCLUDE_PATHleaksmath.hahead of the 3.14t headers. Two worktree provisions failed this way. Four places each chose a compiler of their own:.envrcexported the bareCC=gcc CXX=g++;deploy/install-orchestrata.shdefaulted to baregccand re-derived/usr/bin/gccwith a PATH-shadow probe;deploy/install-tools.shhardcoded/usr/bin/gcc;scripts/worktree_toolchain.pypinned a hardcoded path only when it detected a shadow.
No mechanism could find these sites. The AST snapshot records each call's callee without its arguments, so the program a spawn runs was unrecoverable, and the Prolog gate (REQ-GATE-001) reads facts only from the snapshot.
Decision (Mohamed, verbatim)
"fix bare python make it prolog and atoms"
"make the gcc path a configuration and set it"
- The snapshot records the facts; it classifies nothing. Every module in
plans/ast-ultimate.jsoncarriesargv_literals: each literal program token at a spawn-shaped call, recorded verbatim with its file, line, callee text and form (argv/exec/shell). The snapshot declares the per-module captures it performed (captures). That identity is part of the warm-start cache, so a snapshot lacking a capture is fully re-parsed and never read as "captured, none found". - Prolog decides.
code_facts.pldeclaresargv_literal/5andsnapshot_capture/1.gate_rules.plnames the toolchain programs as enumerated atoms (toolchain_program/2). The ruleviolation(bare_toolchain, File, Line, Detail)joins them withwaived/2andin_project/1. Severity is data ingate_severity.pl. A rule whose required capture the snapshot did not perform makes the gate INVALID, never PASS. - One compiler declaration.
env/toolchain.env(tracked) holdsCCandCXXas absolute paths (/usr/bin/gcc,/usr/bin/g++on this host). Every consumer reads it:.envrc(dotenv, last),deploy/toolchain.sh(installers), andscripts/worktree_toolchain.py(worktree provisioning, janus and tree-sitter builds). Every consumer refuses a missing, bare-named or non-executable declaration rather than fall back toPATH. A host that needs another compiler edits that file.
Alternatives rejected
- Classify program names in the Python extractor. That hides policy in the fact layer; the codebase's
rule is facts in
code_facts.pl, policy ingate_rules.pl, severity ingate_severity.pl. - A regex lint over source text. It re-parses files the snapshot already describes (REQ-GATE-009) and
cannot see through a
cmd = [...]; run(cmd)assignment. - Keep the PATH-shadow probe. It decides per host and per shell what compiler to use, and it let an
inherited bare
CC=gccthrough. - Per-consumer defaults. That is the four-way divergence that produced the failures above.
Consequences
- An old snapshot (no
captures) forces one full re-parse (93 s measured) on its next regeneration. The gate reads INVALID for it rather than PASS. - Every existing bare toolchain spawn becomes a gate finding, and those findings are fixed or waived with a
reason (
# gate-ok: <reason>). If the count is large, the rule may land atwarningand move toerroronce the tree is clean. That is a one-line change ingate_severity.pl, decided by Mohamed. - Worktree provisioning, both installers and both native build scripts fail loudly on a bad declaration instead of building with the wrong compiler.
Provenance
- Directives: Mohamed, 2026-10-11 (quoted above).
- Evidence: worktree import of the primary checkout's
src/(2026-10-11); two failed provisions with Guix GCC 16.2.0; AST snapshot schema (ModuleInfobefore this change carried no call arguments). - Implementation:
feat/bare-toolchain-gate(snapshot capture, Prolog facts and rule) andfix/bare-interpreter-gate(toolchain declaration), specified by the orchestrator and built by the Qwen executor underplans/briefs/qwen/.
Related ADRs
No other ADR is named in this record.
Rulings cited
None.
Specs cited
None (or no spec page exists for the ones named).