Author a new package from conda-forge or Spack#

This workflow turns a foreign package recipe into a canonical manifest, planned SBOM, Resolvo profile locks, conventional EasyBuild recipes, and a persisted build-evaluation campaign. It is the workflow used for conda-forge eOn and Spack QMCPACK fixtures.

Inputs#

Collect these inputs:

  1. a conda-forge meta.yaml~/~recipe.yaml or Spack package.py;

  2. one or more public product-profile TOML files;

  3. an EasyBuild robot tree, with site overlays passed after upstream;

  4. a stack-policy TOML file for preferred pins, locked pins, and exclusions.

  5. positional SHA-256 values for source artifacts whose foreign metadata has only a VCS commit or tag.

Profile TOML is where installable product choices belong. Each profile emits a separate .eb file. MPI/OpenMP toolchain options alone do not create a suffix; an independently loadable ABI or feature variant does.

Inspect the foreign recipe#

eb-stack package inspect \
  --source fixtures/foreign_ingest/conda_eon/recipe.yaml \
  --format conda-forge \
  --toolchain-name foss \
  --toolchain-version 2026.1 \
  --profile-config examples/profiles/eon.toml \
  --out-dir /tmp/eon-inspect

Inspect package.plan.json and package.sbom.cdx.json. The parser preserves source spans, dependency roles, conda selectors, Spack when= conditions, variants, conflicts, requirements, and static build flags. Dynamic foreign logic becomes a residual rather than an invented fact.

Inspection may report source:missing-sha256 for a commit-pinned Spack release. A VCS commit does not determine the byte hash of the generated source archive. Pass the reviewed archive hash to package plan with one --source-checksum per source artifact; the planner does not invent or omit packaging checksums.

For QMCPACK:

eb-stack package inspect \
  --source fixtures/foreign_ingest/spack_qmcpack/package.py \
  --format spack \
  --toolchain-name foss \
  --toolchain-version 2026.1 \
  --profile-config examples/profiles/qmcpack.toml \
  --out-dir /tmp/qmcpack-inspect

Define product profiles#

A profile layer can replace or extend the parser-derived defaults. The public examples show the complete schema. A QMCPACK profile set commonly contains a default MPI+OpenMP build and a separate -complex recipe:

schema_version = 1

[[profiles]]
name = "default"
default = true
versionsuffix = []
config_options = ["-DQMC_MPI=ON", "-DQMC_OMP=ON", "-DQMC_COMPLEX=OFF"]

[profiles.toolchain_options]
usempi = true
openmp = true

[[profiles.verification_commands]]
program = "bash"
args = ["-lc", "module load {module} && qmcpack --version"]

[[profiles]]
name = "complex"
default = false
versionsuffix = ["-complex"]
config_options = ["-DQMC_MPI=ON", "-DQMC_OMP=ON", "-DQMC_COMPLEX=ON"]

Verification placeholders are {module}, {package}, {version}, {profile}, and {versionsuffix}.

Put stack knowledge inside Resolvo#

Site or distribution pins are solver input, not a text rewrite after solving:

schema_version = 1
name = "foss-2026.1"
toolchain = { name = "foss", version = "2026.1" }

[[pins]]
name = "HDF5"
version_requirement = "==2.1.1"
mode = "preferred"
source = "site stack"

[[pins]]
name = "PyTorch"
version_requirement = "==2.9.1"
toolchain = { name = "foss", version = "2024a" }
versionsuffix = ""
mode = "preferred"
source = "distribution stack"

A preferred pin is favored when jointly feasible. Resolvo may choose another compatible candidate and records the requested version, toolchain, versionsuffix, selected identity, and fallback. A locked pin cannot fall back. Omitting toolchain or versionsuffix leaves that part of the artifact identity unconstrained. Candidate exclusions record known target/build evidence and stay visible in the profile lock.

Plan the complete bundle#

eb-stack package plan \
  --source fixtures/foreign_ingest/conda_eon/recipe.yaml \
  --format conda-forge \
  --toolchain-name foss \
  --toolchain-version 2026.1 \
  --profile-config examples/profiles/eon.toml \
  --easyconfigs /path/to/easybuild-easyconfigs/easybuild/easyconfigs \
  --easyconfigs fixtures/eon_foss_2026_1/easyconfigs \
  --stack-policy examples/stacks/eon-foss-2026.1.toml \
  --out-dir /tmp/eon

The command must produce a manifest, SBOM, one lock per requested profile, and one recipe per profile. Run recipe format, recipe lint, and recipe check against the upstream tree plus the bundle overlay.

eb-stack recipe format /tmp/eon/easyconfigs/e/eOn/*.eb
eb-stack recipe lint /tmp/eon/easyconfigs/e/eOn/*.eb
eb-stack recipe check \
  --recipe /tmp/eon/easyconfigs/e/eOn/eOn-2.16.0-foss-2026.1.eb \
  --easyconfigs /path/to/easybuild-easyconfigs/easybuild/easyconfigs \
  --easyconfigs /tmp/eon/easyconfigs

Missing robot dependencies are real package work. Add their foreign recipe or an authored EasyBuild recipe and run the canonical planner again; do not create placeholder recipes with dummy checksums.

Route the build#

Keep reusable topology in a public base file and site details in a later layer:

eb-stack target list \
  --config examples/targets/base.toml \
  --config ~/.config/eb-stack/site.toml

eb-stack target doctor \
  --config examples/targets/base.toml \
  --config ~/.config/eb-stack/site.toml \
  --target site-builder

The selected target decides transport, executor, runtime, and EasyBuild workload. A control host can therefore stage over SSH, submit through Slurm, run on the host or in Podman/Docker, and invoke the site’s EasyBuild command. Use a runtime-specific EASYBUILD_INSTALLPATH, work_root, and tmp_root so host-linked modules cannot leak into a container build. A shared EASYBUILD_SOURCEPATH is safe for source archives.

Run the Hermes/OMP repair loop#

eb-stack campaign run \
  --bundle /tmp/eon \
  --config examples/targets/base.toml \
  --config ~/.config/eb-stack/site.toml \
  --target site-builder \
  --state /tmp/eon.campaign.json

Hermes owns the campaign loop. OMP workers claim typed findings, repair the manifest/profile/recipe/target layer appropriate to the class, record evidence, and rerun the campaign. Build compatibility claims are not proof: configure, compile, link, test, install, and sanity failures stay empirical findings until the target build succeeds.

The endpoint is three separate claims:

  • resolves: all profile locks and recipes exist;

  • builds: every recipe succeeds through EasyBuild on the selected target;

  • binary-verified: every declared verification command succeeds.

Follow skills/new-package/SKILL.md for the exact campaign procedure and human-owned PR handoff.