Package bundle reference¶
package inspect, package plan, and package bump write a version-one
package bundle. The bundle is the hand-off between parsing, Resolvo, EasyBuild
authoring, and empirical build evaluation.
Bundle layout¶
BUNDLE/
package.plan.json
package.sbom.cdx.json
locks/<profile>.lock.json
easyconfigs/<letter>/<name>/<recipe>.eb
easyconfigs/<letter>/<name>/<patch files>
package inspect writes only the plan and planned SBOM. package plan and
package bump also write one lock and one EasyBuild recipe for every requested
profile. A campaign requires at least one lock and recipe.
When package plan is given --package-catalog layers, the writer emits a
closed package bundle: root artifacts at the bundle root, companions under
packages/<name>-<version>-<toolchain>/, one shared easyconfigs/ overlay,
build-order.json, closure.plan.json, and closure.sbom.cdx.json. Companion
providers may be foreign or easybuild-bump; bump companions keep
origin = easy-build and use the annual-bump emitter.
Canonical package plan¶
package.plan.json is the build manifest. Its schema_version is 1 and
unknown fields are rejected by the Rust model.
Field |
Meaning |
|---|---|
|
|
|
Normalized name, version, homepage, description, and license |
|
URLs, filenames, SHA-256 values, VCS identities, staging directories, and provenance |
|
Names, constraints, optional explicit EasyBuild toolchains, roles, structured conditions, virtual capabilities, and provenance |
|
Preserved conflicts and requirements |
|
Toolchain, easyblock, build systems, configuration options, checksummed patches, typed easyconfig parameters, and module class |
|
Independently installable product configurations |
|
Requested profile and stack-policy pairs |
|
Explicit parser, normalization, resolution, emission, build, or verification work |
Source and dependency provenance records the input span, extractor, original
text, and one of exact, derived, or ambiguous. The static foreign
adapters preserve facts they can prove and put dynamic or ambiguous logic in
residuals instead of inventing EasyBuild metadata.
Written provenance paths are source identities rather than checkout locations:
conda-forge/recipe.yaml, spack/package.py, or the corresponding EasyBuild
recipe name. Line and column spans still address the named input. Absolute
checkout paths are used while reading local inputs but are not serialized into
the bundle, so identical inputs produce identical manifests on different hosts.
An EasyBuild-origin dependency carries toolchain when its tuple names an
explicit dependency toolchain. Annual bump planning maps that toolchain family
onto the target generation and requires Resolvo to select a matching artifact.
An implicit tuple omits toolchain and may select the minimum non-SYSTEM
member of the target hierarchy. SYSTEM is eligible only when the source tuple
or parent recipe selects it explicitly.
Parser notes are diagnostics, not a work queue. Canonical planning never
searches note text for words such as dynamic or residual. A successfully
materialized url_for_version, for example, may remain visible as a note but
does not create residual work. Anything that needs mechanical repair or
authoring judgment is represented by a typed residual with category,
severity, summary, optional evidence, and optional source provenance.
Conda build.number and compiler, standard-library, or cross-python template
requirements are also notes because the EasyBuild toolchain represents those
concerns. An unresolved template in an ordinary package identity remains a
template-evaluation residual.
Residual severity has three values:
mechanical: deterministic formatting or metadata work;judgment: package-authoring or target evidence is required;blocking: the next pipeline stage cannot make a valid claim.
{
"id": "foreign:imperative-patch:0",
"stage": "parse",
"category": "imperative-patch",
"severity": "judgment",
"summary": "imperative patch method requires EasyBuild translation",
"provenance": {
"span": {"path": "package.py", "start_line": 42}
}
}
The build manifest stores patches as artifacts:
{
"filename": "Orbit-2.0-portability.patch",
"sha256": "4f43b42fdcf84d0cf634d993dd944f252c8243dc612a919fe2825d56f937c8eb",
"source": "patches/Orbit-2.0-portability.patch"
}
Relative patch sources resolve against the package-policy TOML. Planning verifies the declared SHA-256 before copying each patch beside every emitted recipe. The manifest retains the portable source path; machine-local resolved paths do not enter the bundle.
Spack patch() directives retain their checksum, exact remote URL, source
provenance, and structured when condition. Package-version predicates are
specialized against the selected version; profile predicates remain attached
until variant materialization. A remote artifact has a conventional
filename, a url, a sha256, and a condition:
{
"filename": "26aac9a2adb9a688ab8fe2a9664450de97557cf9.patch",
"sha256": "54484b722df264dae3fd0c1094883b17431617e278eeba2cffbd720b36c9e21a",
"url": "https://github.com/example/project/commit/26aac9a2adb9a688ab8fe2a9664450de97557cf9.patch?full_index=1",
"condition": {"op": "always"}
}
EasyBuild accepts HTTP(S) URLs in patches and checks the downloaded bytes
against the corresponding positional checksum. Local artifacts are verified
and copied into the recipe directory; remote artifacts remain URLs in the
emitted recipe. Imperative Spack def patch(self) methods cannot be represented
as patch files and become residual build-authoring work when their version
condition applies.
easyconfig_parameters accepts strings, integers, booleans, lists, and
tables. It cannot contain executable expressions. Build-level parameters are
defaults for every emitted recipe; profile-level parameters replace matching
keys for one variant.
Planned CycloneDX SBOM¶
package.sbom.cdx.json is CycloneDX 1.5 derived from the package plan. It is a
pre-build inventory of requested components and dependency intent. It is not a
scan of installed files and does not establish the builds or
binary-verified claim.
The root component carries the primary source SHA-256 when available. Its external references retain VCS tag/commit identity and one distribution URL plus SHA-256 per source artifact, including derived GitHub tag archives. These values come from the canonical plan after any explicit positional checksum overrides.
Consumers should preserve the lifecycle and eb_stack:* properties when
copying the document into a wider stack report. Build evidence belongs in the
campaign state, not in the planned SBOM.
The planned SBOM omits a generation timestamp because its identity is the canonical input and dependency intent, not the wall-clock time of a CLI run. Campaign evidence records build and verification event times. This keeps the SBOM reproducible across checkout roots and repeated planning runs.
Product profiles and recipe names¶
A profile represents one loadable product. Each requested profile is solved and emitted independently:
the default profile has an empty versionsuffix and an unsuffixed recipe;
a non-default profile declares its complete versionsuffix, such as
-complex;MPI and OpenMP toolchain options alone do not create a separate recipe name;
a feature or ABI variant that users load independently gets its own profile, lock, and
.ebfile.
For example, the QMCPACK profile set emits:
locks/default.lock.json
locks/complex.lock.json
easyconfigs/q/QMCPACK/QMCPACK-4.3.0-foss-2026.1.eb
easyconfigs/q/QMCPACK/QMCPACK-4.3.0-foss-2026.1-complex.eb
Resolvo profile lock¶
locks/<profile>.lock.json records the actual SAT selection used to render
that recipe:
{
"schema_version": 1,
"package": "QMCPACK",
"version": "4.3.0",
"profile": "complex",
"toolchain": {"name": "foss", "version": "2026.1"},
"versionsuffix": "-complex",
"dependencies": [
{
"name": "HDF5",
"version": "2.1.1",
"toolchain": {"name": "foss", "version": "2026.1"},
"easyconfig_path": "easybuild/easyconfigs/h/HDF5/HDF5-2.1.1-foss-2026.1.eb",
"build": false
}
],
"pin_outcomes": [],
"exclusions": [],
"solver": "resolvo"
}
Every locked dependency carries its selected EasyBuild identity and source
recipe path. Standard robot trees retain the path beginning at
easybuild/easyconfigs/; other robot layouts retain the recipe filename.
Machine-local robot prefixes are not serialized. build distinguishes direct
build dependencies from runtime or host dependencies.
pin_outcomes keeps stack preferences reviewable. A preferred pin may record
fallback: true when Resolvo selects another jointly compatible candidate. A
locked pin cannot fall back. exclusions records candidates removed from
consideration with the evidence-backed reason and optional scope.
SAT compatibility is not build evidence. A selected candidate may still fail configure, compile, link, tests, install, or sanity; the campaign records that outcome as a typed finding.
EasyBuild recipe set¶
Generated recipes follow the conventional EasyBuild hierarchy and syntax. Sources and checksums remain positional: all source checksums precede patch checksums. Verified local patch files are copied into the recipe directory; checksum-addressed remote patches remain exact URLs. Robot overlays are searched in command-line order, with later paths overriding earlier identities.
A foreign VCS commit is immutable source identity but is not the SHA-256 of
the downloaded archive. Supply one --source-checksum per source artifact when
the foreign recipe lacks archive hashes. package inspect keeps the missing
checksum as a blocking residual; package plan refuses to emit recipes until
all source and patch checksums are complete and valid.
Run the three recipe gates before a campaign:
eb-stack recipe format BUNDLE/easyconfigs/*/*/*.eb
eb-stack recipe lint BUNDLE/easyconfigs/*/*/*.eb
eb-stack recipe check \
--recipe BUNDLE/easyconfigs/q/QMCPACK/QMCPACK-4.3.0-foss-2026.1.eb \
--easyconfigs /path/to/upstream/easyconfigs \
--easyconfigs BUNDLE/easyconfigs
The bundle establishes resolves only when every requested profile has a lock
and recipe. See campaign state and findings for the build and binary claims.