?- toolbox.
Each card from the landing page, in plain language: what it means, why it matters, and what it looks like in practice. Prefer the raw list? See the complete feature list.
Repo://Category/Name-Version:Action?{Context} — every term answers four questions: where, what, how, and why.
Everything that flows through portage-ng — from the rules to the prover to the orderer to the printer — is this one kind of term. Read it left to right: where does the fact come from (the portage tree, pkg for installed packages, an overlay), what exact version is meant, how should the pipeline treat it (download, install, run, …), and why is it here at all — the context.
The context is where feature logic earns its keep. It is a list of feature terms — structured, mergeable metadata: build_with_use:use_state(…) carries the USE flags the package must be built with, after(…) carries ordering constraints. Metadata that traditional resolvers scatter across side tables travels inside the literal itself — any plan step can be read on its own, justification included.
And when two dependency paths reach the same package, their contexts are merged by feature unification, in the tradition of Zeller's feature logic: USE demands union — one path's [ssl] and another's [xml] become [ssl,xml] — while contradictions like [debug] against [-debug] make the merge fail and send the prover back. Conflict detection isn't a separate pass; it falls out of the representation. Growth stays bounded, too: a parent's USE demands apply to that dependency alone and never leak to grandchildren.
Deep dive: Chapter 5 — Proof Literals · Chapter 22 — Context Terms
portage://'dev-lang/python-3.13.2':install?{[ build_with_use:use_state([ssl,threads],[]), after(portage://'sys-apps/portage-3.0.77-r3':install) ]} reads as install python 3.13.2 from the portage tree, built with ssl and threads enabled, scheduled after sys-apps/portage's own install. two paths meet — feature unification merges contexts python[ssl] + python[xml] → use_state([ssl,xml],[]) merged python[debug] + python[-debug] → merge fails — prover backtracks
The term shape is verbatim — this is what the prover stores and the printer reads. Each context tag answers a question a later stage will ask.
Every dead end teaches the resolver something — like pencil marks in a sudoku.
Resolving a large dependency tree means making thousands of version choices, and some combinations only reveal their conflict deep into the search. A classic backtracking resolver undoes its work, tries the next candidate, and can rediscover the very same conflict along a different route.
portage-ng records the reason for each failure as a no-good — a learned fact saying "any plan containing this combination fails" — and restarts with that knowledge. Each package keeps a version domain, the set of versions still worth considering, and learned no-goods shrink these domains between retries. Every retry is strictly better informed than the last; the same dead end can't be visited twice. If you know SAT solvers: this is closer to CDCL than to traditional backtracking.
And when the same library is reached through many dependency paths — half the tree depends on it — the accumulated requirements are merged by feature-term unification rather than proved once per path. Only the difference is re-examined.
Deep dive: Chapter 10 — Version Domains · Chapter 8 — The Prover
· retry 1 — conflict at dev-libs/openssl net-misc/curl-8.18 needs openssl >= 3.2 media-video/ffmpeg-4.4 needs openssl < 3.0 learned no-good: ffmpeg-4.4 with openssl ≥ 3.2 narrowed ffmpeg: {4.4.6, 6.1.4, 8.0} → {6.1.4, 8.0} · retry 2 — re-proves only the ffmpeg subtree ✓ plan proved — ffmpeg-8.0 against openssl-3.5
A sketch of the mechanism: the conflict becomes knowledge, the domain shrinks, and the retry stays local to the affected subtree.
Strict first. If no strict plan exists, relax — one explicit tier at a time.
The first attempt is always a strict proof: stable keywords only, nothing masked, your configuration as it stands. If a plan exists under those rules, that's the plan you get.
Some requests genuinely have no strict plan — a package still in testing (~amd64), or two packages that block each other. Instead of stopping at the error, portage-ng retries under progressively weaker rules: accepting testing keywords first, then resolving blockers, then unmasking, then combinations. Each tier is still a formal proof — it just states its extra premises out loud.
Those premises come back as assumptions embedded in the plan: the exact package.accept_keywords, package.unmask or package.use lines that make it valid. Review, apply, done — nothing to reverse-engineer from error output.
Deep dive: Chapter 9 — Assumptions
~ ❯ portage-ng --pretend app-editors/zed strict proof ............ no plan (keyword-masked) tier 1 · keywords ....... ✓ plan proved assumption accept_keywords(app-editors/zed ~amd64) suggested /etc/portage/package.accept_keywords: app-editors/zed ~amd64
The plan is valid the moment you paste the suggested line — the price of relaxation is stated as configuration, not prose.
There is rarely one right plan. See the roads not taken before you commit.
USE flags, slots and version choices mean a target usually has several valid plans, not one. Most resolvers pick one path silently. portage-ng reasons about the configuration space, and --variants lets you enumerate the neighbourhood: how does the plan change if a flag is toggled, a slot switched, a version pinned?
Use --variants auto to let it pick interesting toggles, --variants all for everything, or name the flags yourself. Because every variant is itself a proven plan, the comparison is exact — package lists, step counts, download sizes — not an estimate.
Deep dive: Chapter 15 — Command Line Interface
~ ❯ portage-ng --pretend --variants wayland,X dev-libs/gtk+ variant 1 USE="wayland -X" 41 actions · 7 steps variant 2 USE="-wayland X" 45 actions · 8 steps + x11-libs/libX11 + x11-libs/libXi + …
Real command from the manpage; the plan numbers are illustrative. Each variant is a full formal proof you can inspect.
When a build reveals a missing dependency, the plan is re-proved — not patched in flight.
Some ebuilds forget to declare a build dependency, and the omission only surfaces at build time: semodule_package: command not found, deep inside a package. Injecting the missing package into the running plan would make the plan a lie — it would no longer be the thing that was proved.
So the failure is treated as information. A detector reads the failed phase's log, identifies the missing command, header or library, resolves it to the package that provides it (the VDB reverse-owner index, then a curated seed table), and records the discovery as durable learned knowledge. Then the plan is derived again from scratch — now provably including the provider, ordered before the target.
That sounds expensive; it isn't. Everything already built satisfies the new proof from the installed-package database, so the retry pass only builds the newly discovered provider and its target. And each discovery doubles as a draft upstream bug report: "add BDEPEND on the provider".
Deep dive: Chapter 16 — Building · Chapter 19 — Upstream Bugs
build failed · sec-policy/selinux-base (compile, exit 127) log: semodule_package: command not found diagnose missing provider → sys-apps/semodule-utils learn discovered dependency recorded durable re-derive fresh proof — provider ordered before target retry builds 2 packages, rest satisfied from VDB >>> Missing build dependencies discovered (bug report drafts) --- Summary: sec-policy/selinux-base: missing BDEPEND= sys-apps/semodule-utils (command semodule_package not found) Affected package: portage://sec-policy/selinux-base Missing dependency: sys-apps/semodule-utils (build-time / BDEPEND) Observed: command semodule_package not found during the compile phase (exit 127): semodule_package: command not found Potential fix (suggestion): Add BDEPEND="sys-apps/semodule-utils" to the ebuild or the responsible inherited eclass. (discovered by portage-ng missing-provider feedback, portage-ng#102)
A real case from the project notes — the draft prints at the end of the build, ready to file. The invariant: the executed plan is always prove_plan(Goals, KB) — never a hand-edited one.
Failures are classified before they're retried — and every recovery is reported.
Long builds fail for boring reasons: a parallel-make race, a transient environment glitch, a package with a known one-line quirk. portage-ng keys each failure by signature and routes it accordingly — transient signatures pass through an automatic retry chain, and known packaging exceptions are repaired in-transaction by a pluggable fixup registry.
Deeper breakage is planned for, too. Sub-slot (:=) ABI changes schedule same-version rebuilds of installed reverse dependencies as part of the plan. GHC and OCaml breakage — invisible to sub-slots — is recognised from compiler failure signatures, and the broken packages are rebuilt mid-run before the failed phase is retried: the job a manual haskell-updater pass usually does afterwards.
Nothing is silent: every retry, fixup and rebuild appears in the build summary.
Deep dive: Chapter 16 — Building
phase failed · dev-haskell/aeson (compile) signature: GHC ABI hash mismatch — base upgraded heal rebuild 3 broken consumers, then retry dev-haskell/text · scientific · th-abstraction retry compile ✓ summary: 1 ABI repair · 1 transient retry · 0 silent
Breakage is detected from the compiler's failure signature, repaired, and the build continues — with the repair on the record.
Describe what you want in your own words — and ask the plan why it did what it did.
--search accepts natural language, matched against the tree by vector embeddings computed locally via Ollama (GPU- and Neural-Engine-accelerated on Apple Silicon). --similar walks the same index to find related packages.
--explain answers questions about a computed plan — "why is openssl in here?" — through a configured language model, and --llm opens an interactive chat. Ollama, Claude, ChatGPT, Gemini and Grok are supported. The model never gets shell access: it queries the knowledge base through a sandboxed Pengine interface — introspection, not arbitrary code execution.
And when a failed build stumps the deterministic detectors, --diagnose runs meta-circular self-repair: a model briefed on how portage-ng itself produces proofs and plans reads the failure log and proposes structured feedback:* learning or a draft fixup. Every proposal is validated against the tree and confirmed by hand — then the plan is re-derived, never patched.
Deep dive: Chapter 17 — LLM Integration
~ ❯ portage-ng --search text editor with syntax highlighting ~ ❯ portage-ng --similar app-editors/vim ~ ❯ portage-ng --pretend --explain \ "Why is dev-libs/openssl in the plan?" vim ~ ❯ portage-ng --llm grok ~ ❯ portage-ng --diagnose cat/pkg meta-circular diagnose of a failed build — confirm before applying feedback
All five commands verbatim from the manpage examples section.
One machine holds the knowledge base — every machine on the network benefits.
In server mode, portage-ng exposes the resolver as a Pengine-based HTTPS API with job and result queues. Workers poll the server for targets, run the full pipeline locally, and post results back. Thin clients submit targets and render finished plans without ever loading the knowledge base themselves.
Cluster formation is automatic on a local network — servers and workers advertise themselves over mDNS/Bonjour, so there are no endpoints to configure. Every connection is mutually TLS-authenticated with CA, server and client certificates. And for a single machine, daemon mode keeps the knowledge base warm behind a Unix socket, so repeated queries skip the cold start entirely.
Deep dive: Chapter 18 — Distributed Proving
build box ❯ portage-ng --mode server --port 4000 helpers ❯ portage-ng --mode worker # finds the server via Bonjour laptop ❯ portage-ng --mode client --pretend @world
Real mode flags. The laptop renders a proven plan without holding the ~32,000-ebuild knowledge base.
A new reasoner, the same build machinery — your tree works unchanged.
portage-ng replaces the reasoning, not the ecosystem. Actual package building delegates to Portage's own ebuild infrastructure through a documented invocation contract — phases, USE, exit codes, merge serialization — so every ebuild, eclass and phase function behaves exactly as it always has.
The command line mirrors emerge where it matters: --pretend, --ask, --deep, --newuse, --emptytree, --fetchonly and dozens more, so existing habits (and scripts) transfer. Before an upgrade, --snapshot captures binary packages of everything about to be replaced; if the upgrade disappoints, --rollback restores the known-good state.
Deep dive: Chapter 15 — Command Line Interface · Chapter 16 — Building
~ ❯ portage-ng --pretend --update --deep --newuse @world ~ ❯ portage-ng --snapshot before-kde-update --update @world ~ ❯ portage-ng --rollback 20260309-143022
Familiar flags, plus an undo button: snapshot before, roll back after — verbatim from the manpage examples.
Underneath, the engine doesn't know what a package is.
The prover and the planning laws operate on abstract literals: something is required, something provides it, a step can run once everything it needs is available. Every Gentoo concept — ebuilds, USE flags, slots, keywords — enters through a single pluggable rules layer behind a rule/2 hook.
Swap that layer and the same engine plans a different world: any domain whose constraints can be written down as rules — container image graphs, infrastructure rollouts, build systems. The object system that makes this modularity possible is contextual logic programming, a runtime class-and-instance system for Prolog developed for portage-ng and usable as a general-purpose paradigm in its own right.
Deep dive: Chapter 11 — The Rules Layer · Chapter 21 — Contextual Logic Programming
%% the core proves goals it knows nothing about … prove(Goal, Proof) :- rule(Goal, Body), prove_all(Body, Proof). %% … Gentoo is one binding of rule/2 … rule(required(Pkg), Deps) :- ebuild(Pkg, depend, Deps). %% … any rule-encoded domain fits the same hook rule(deployed(Svc), Needs) :- service(Svc, requires, Needs).
Schematic, not the literal source — the point is the shape: one generic prover, domains as interchangeable rule sets.