Provenance contract

What fresh-fvsbc records for every run, so that a number in a paper can be traced to what produced it.

This contract is fresh-fvsbc’s own design. It is not read from FVS. The three external facts it depends on are verified: FVS writes the variant and version into FVS_Cases (see Output contract); the keyword file name is recorded there too; and the build’s inputs are pinned (see ADR 0001).

Why it exists

The project’s users include reviewers and readers of published results, who need to know which variant, which source revision, and which keyword set produced a number. notes/scope.md names that need, and AGENTS.md states the rule: record the provenance of every run.

What a run records

A record has these fields.

Field

What it is

fresh_fvsbc_version

The fresh-fvsbc version that ran

pyfvs_commit

The commit of the pinned UBC-FRESH/PyFVS fork

fvs_source_commit

The commit of the FVS source submodule that the fork pins

variant

The variant built and run, which for this project is bc

variant_list

The full variant list the build produced, so a mistaken build is visible

bec

The validated BEC the run used, such as CAR IDFdk3/01

qmd

The expansion-weighted initial QMD, in centimetres

qmd_min

The bundle’s minimum QMD, when it declares one

ba

The expansion-weighted initial basal area, in square metres per hectare

ba_min

The bundle’s minimum BA, when it declares one

tsa

The TSA the run was checked against, when one was supplied

calibration_id

The calibration bundle the run used, when it used one

keyword_set_hash

A hash of the keyword set, canonically serialized

treelist_hash

A hash of the tree-list input, canonically serialized

case_id

The CaseID from FVS_Cases

run_datetime

The RunDateTime from FVS_Cases

random_seed

The seed, when the run used one. FVS exposes RANNSEED

The two hashes are of a canonical serialization, not of a file. A file hash changes with a trailing newline or a column width, and it would then say a run differed when it did not.

Where it lives

  • In memory, on the result object that a run returns.

  • On disk beside the outputs, as JSON, when the caller asks for it.

  • In the changelog or the close-out report, only as a citation. A raw record does not belong in a tracked file when it names a local path.

Contract rules

  1. A run that cannot record its provenance is a failure. Provenance is not optional, and it is not added afterwards.

  2. The record names commits, not versions, for the two pinned inputs. A version can be republished; a commit cannot.

  3. variant_list is recorded even though the project builds one variant, because a build that quietly produced a different variant is a real failure mode and this is what makes it visible.

  4. The record carries no credential, no personal path, and no raw data. AGENTS.md states the rule, and scripts/check_workflow.py rejects the mechanical cases.

Not verified

  • Answered: PyFVS exposes the built variant list at run time. pyfvs.list_variants() returns each variant with the module it would import and a status, and it returned {'BC': {'lib': 'pyfvs.fvsbc', 'status': 'OK'}} in this project’s build. The status is what makes a mistaken build visible, so the record’s list is built by refusing a variant whose status is not OK.

  • Answered within a bound: `RANNSEED` controls the variation these runs show. Two runs of one case with the seed fixed produced byte-identical summaries, and changing the seed alone changed one value in one period. So the seed is the control, and no other source showed itself. What that does not establish is that no other source exists: one that varies over a longer timescale than two runs a second apart, or one that needs more cycles or more plots to appear, would not have shown up here.