Architecture#
eb-stack separates package intent, dependency selection, recipe emission,
and empirical build evidence. Every user-facing workflow passes through the
same canonical package model.
Data flow#
Syntax adapters#
The conda adapter expands a restricted template subset, parses classic and v1 recipe shapes, preserves multi-source entries, and represents selectors as structured conditions. The Spack adapter statically reads package DSL calls; it does not execute Python. It preserves versions, dependencies and roles, variants, conflicts, requirements, source metadata, and source spans.
Adapters do not choose EasyBuild dependency versions or emit placeholder recipes. Their output is package intent plus explicit residuals.
Canonical package model#
PackagePlan is schema-versioned and shared by foreign imports and bumps. It
contains package metadata, provenance-bearing sources and dependencies,
structured conditions/rules, EasyBuild build intent, product profiles, output
requests, and residuals.
The planned SBOM is derived from this model. It is marked pre-build and remains separate from installed/binary evidence.
Product profiles#
Profiles represent independently installable products. Materialization applies feature flags, dependency-feature values, compiler/toolchain/platform context, variables, versionsuffix components, toolchain options, configuration options, and verification commands.
The emission invariant is one profile lock and one conventional .eb file per
requested profile. The default CPU/MPI/OpenMP product is unsuffixed unless the
site explicitly defines a distinct loadable variant.
Resolvo boundary#
The solver receives:
the materialized direct dependency requirements;
candidates filtered through the EasyBuild toolchain hierarchy;
preferred or locked stack pins;
candidate exclusions backed by target/build evidence.
Preferred pins use Resolvo’s favored candidate and retain an in-solver fallback. Locked pins constrain selection to one candidate. Pin outcomes and exclusions are serialized in the profile lock.
Existing robot recipes are already-built artifacts. Their build-only tools have independent build contexts and are not forced into the generated package’s co-loaded runtime closure. Direct build requirements of the generated profile remain solver requirements and are emitted as build dependencies.
Deterministic EasyBuild emission#
New-package emission renders package metadata, all sources and checksums, EasyBuild build settings, locked dependency tuples, profile toolchain options, configuration, sanity paths, and module class. Bumps preserve the source recipe’s syntax and unrelated bytes while applying the canonical lock to the toolchain, package version, dependency versions, and source checksum entry.
Layered targets#
A target is four orthogonal layers:
transport: local or SSH;
executor: direct or Slurm;
runtime: host, Podman, or Docker;
EasyBuild workload: command, robot paths, durable roots, and environment.
TOML layers merge by target name in argument order. This keeps the public repository and skills portable while a private site layer supplies hostnames, paths, modules, scheduler sizing, and container images.
Compiled EasyBuild modules, work trees, and temporary roots belong to one
runtime ABI. A container target therefore uses its own EASYBUILD_INSTALLPATH,
work_root, and tmp_root. Source archives may use a shared cache because they
do not contain host-linked executables.
Persisted campaigns#
A campaign stages one bundle, builds every emitted recipe, and runs profile verification commands. Its state stores attempts, exact routed commands, compact evidence, typed findings, ownership, resolutions, and the three claims.
Spawn and exit failures are data, not lost control flow. They are classified as transport, executor, runtime, checksum, patch, dependency, configure, compile, link, test, install, sanity, resource, timeout, or unknown findings. Hermes drives retries; OMP workers coordinate through claim/resolve operations.
API surface#
The CLI and MCP server expose the same version-one workflows. Internal parser and syntax-preserving emit modules are not compatibility APIs. There is one canonical route for new packages and one for bumps, both ending in the same bundle and campaign model.