Calibration bundle

A versioned JSON document that names the build, the domain, the parameters, the evidence, and the reviewer for a calibration. It is data, not code: the package loads it, checks it, renders it into FVS keywords, and records its id in provenance. It does not fit the parameters.

The contract lives in fresh_fvsbc.bundle.CalibrationBundle. A contract pilot is at notes/reference/calibration/pilot-bundle.json; it is deliberately unsigned and is not a calibration. The worked regional example is notes/reference/calibration/kc-pilot-bundle.json; its protocol and claim boundary are in notes/reference/calibration/kc-protocol.md, and its results are in notes/reference/calibration/kc-replication.md.

Fields

Field

What it carries

bundle_id

A stable lower-case identifier, such as bc-pilot-2026-11-02

version

A semantic version, such as 0.1.0

pins

pyfvs_commit, fvs_source_commit, and variant, plus optional wrapper_version. The loader refuses a bundle whose pins do not match the package.

domain

regions, tsas, becs, species_groups, dbh_classes, management, and optional qmd_min and ba_min. An empty list means no restriction; dbh_classes is a list of inclusive [min, max] ranges in centimetres; qmd_min is in centimetres and ba_min in square metres per hectare.

parameters

The keyword records: keyword, fields, units, value_index, and optional bounds.

training

data_checksum (sha256: and 64 hex), split, objective, and convergence.

review

reviewer, date (YYYY-MM-DD), and signed_off.

claim

The claim boundary in one sentence.

A parameter

{
  "keyword": "MORTMULT",
  "fields": ["1", "FD", "1.8000", "0", "999"],
  "units": "multiplier",
  "value_index": 2,
  "bounds": {"min": 0.25, "max": 4.0}
}

value_index is zero-based. The bounded field must be numeric, and it must sit strictly inside the bounds; a value equal to either bound loads with a warning, because a parameter at a bound is not identified.

The accepted keywords are BAMAX, SDIMAX, SDICALC, BAIMULT, MORTMULT, HTGMULT, DGSTDEV, CRNMULT, FIXMORT, and MORTMSB. A bundle cannot invent a keyword.

Using a bundle

from fresh_fvsbc import load_bundle, run_case

bundle = load_bundle("notes/reference/calibration/pilot-bundle.json")
keywords = (*bundle.render(), *keywords)
result = run_case(records, keywords, directory=run_dir, calibration=bundle)
assert result.provenance.calibration_id == bundle.bundle_id

The run checks the bundle’s domain against the run’s BEC, species groups, DBHs, and management, and it refuses a set that does not carry the rendered keywords. A bundle whose data checksum is missing or whose pins differ from the package is refused before the run starts.

What it does not do

  • It does not fit a parameter; W4 does.

  • It does not certify a bundle; signed_off is a review record, not a proof.

  • It does not claim a bundle is valid outside its domain. The domain check is the boundary.