Skip to content

Report a fingerprint of the model's numbers - #100

Merged
dfalster merged 1 commit into
masterfrom
feature/consumer-change-signals
Aug 19, 2026
Merged

Report a fingerprint of the model's numbers#100
dfalster merged 1 commit into
masterfrom
feature/consumer-change-signals

Conversation

@dfalster

Copy link
Copy Markdown
Member

Stacked on #99 — base it there, not on master, because the fingerprint is a digest of the golden files #99 regenerates.

Results moved on #15, #36, #41 and four gradient PRs without the version moving, so a consumer caching computed results had no signal its cache was stale. The calibration study's workaround was to hash inst/include, R/ and src/ — which #47's rename would have moved without moving a single number.

leaf_behaviour_fingerprint() is a 12-character digest of the two recorded baselines together. They already are this package's definition of the numbers and are regenerated deliberately, so it inherits that discipline; test-fingerprint.R recomputes it from the files on disk, so regenerating a golden file and forgetting Rscript tools/fingerprint.R fails. File names are not in the digest and contents are hashed by line with the trailing \r stripped, so neither a rename nor a CRLF checkout can move it. Both asserted.

#58's first ask — bump the version when behaviour changes — is in #99, which is the PR that moves results.

Closes #58.

@dfalster

Copy link
Copy Markdown
Member Author

Why the exported function returns a constant rather than computing the digest

Because it cannot compute it. R CMD build ships tests/cpp/golden/ in the tarball (it is not in .Rbuildignoretests/cpp.R compiles against it), but R CMD INSTALL does not put tests/ in the library. Verified against a scratch install: no operating_points.tsv anywhere under the installed package. So an installed phylloptim has no access to the files the fingerprint is derived from.

Two ways out were considered:

  1. Move the golden files under inst/ so they are always installed. Rejected: it moves paths that tests/cpp/Makefile, test_golden.cpp, test-golden.R and tools/gradient_golden.R all name, for no benefit to the fingerprint's users — a consumer wants a value to compare, not the file.
  2. Commit the digest as a constant, and assert it. Taken. The generated-and-committed pattern is already how R/RcppR6.R and src/RcppR6.cpp work here, and the assertion is what makes it trustworthy: a constant nothing recomputes is a constant that goes stale, and a stale behaviour fingerprint is worse than none — a consumer's cache would look valid while the numbers underneath it had moved.

So test-fingerprint.R recomputes from disk and fails on a mismatch. That is the enforcement #58 says the fingerprint "would inherit" rather than need.

Two bugs the tests caught in the digest itself

Both were in the first version, and both would have made the fingerprint report a change that was not one:

readLines does not strip \r on Unix. The comment claimed it normalised the line ending. It does not — it keeps the carriage return at end of line, so a CRLF checkout on Windows would have produced a different fingerprint from an LF one for identical numbers. Now sub("\r$", "", ...) explicitly, and the test builds a CRLF file with writeChar and asserts the two agree.

The file names were in the digest. The first version hashed paste(basename(paths), parts), which reproduces the exact defect #58 raises against source hashing: "#47's rename would have silently changed the hash without changing behaviour". Names are now used only to sort for order-independence, never recorded. Asserted by copying a golden file to a differently-named tempfile and requiring the same answer.

The second one is the more interesting failure: I wrote the assertion the issue asked for (rename-insensitivity) into the prose and then violated it in the code three lines below. The test is what noticed.

What the fingerprint does not cover, stated because a cache key invites over-trust

?leaf_behaviour_fingerprint carries all three:

  • Not a source hash. Renames, comments, docs, new non-numerical API and performance work leave it alone. Intentional.
  • Not a promise of equal output on your machine. The golden files are bit-exact on macOS/arm64 only; elsewhere the same code agrees to ~1.4e-04 at the argmax. It identifies the code's behaviour, not a platform's realisation of it.
  • Not complete coverage. It sees what the golden grids reach, and they build a fresh Leaf per point — so a stale-state bug of hazard 8's kind can be fixed or introduced without moving it. All three of API cleanup: renames, a purely-intensive input set, the hidden hard-coded pressure, and the shutdown fix #15's were golden-bit-identical.

The third is the one worth knowing before depending on it, and it is not a flaw in the fingerprint so much as in the grids: the same limitation the guide already records for the golden file.

Verification

  • devtools::test()1107 pass, 0 fail, 0 skip (11 of them new)
  • R CMD check on the built tarball — 0 errors, 0 warnings, 0 notes. The one NOTE that was there ('::' or ':::' import not declared from: 'tibble', pre-existing: the vignettes have used tibble() since before this branch) is cleared by declaring it in Suggests.
  • Confirmed both golden files are present in the tarball, and that the test's path resolution works under R CMD check as well as devtools::test()R CMD check copies tests/ wholesale, so tests/testthat/../.. lands on the .Rcheck directory and tests/cpp/golden/ is where the test looks. Not assumed: the check run above exercises it.
  • tools/fingerprint.R was run against the tree; the constant it wrote is what the test recomputes.

Note for whoever merges

The fingerprint value is a function of #99's regenerated golden files. If #99 changes before merge — a review that moves a number, a rebase that regenerates — test-fingerprint.R will fail until Rscript tools/fingerprint.R is re-run. That is the mechanism working, not a merge problem, but it is worth knowing so it is not read as a broken branch.

@dfalster
dfalster force-pushed the feature/consumer-change-signals branch 2 times, most recently from 055e5dc to 0e95164 Compare August 19, 2026 21:35
Base automatically changed from fix/vulnerability-domain-and-trait-reads to master August 19, 2026 22:36
Results moved on #15, #36, #41 and four gradient PRs without the version moving,
so a consumer caching computed results had no signal its cache was stale. The
calibration study's workaround was to hash inst/include, R/ and src/ -- which
#47's rename would have moved without moving a single number.

leaf_behaviour_fingerprint() is a 12-character digest of the two recorded
baselines together. They already ARE this package's definition of the numbers and
are regenerated deliberately, so it inherits that discipline; test-fingerprint.R
recomputes it from the files on disk, so regenerating a golden file and forgetting
`Rscript tools/fingerprint.R` fails. File names are not in the digest and contents
are hashed by line with the trailing \r stripped, so neither a rename nor a CRLF
checkout can move it. Both asserted (#58).

Also declares `tibble`, which the vignettes use: R CMD check is now clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dfalster
dfalster force-pushed the feature/consumer-change-signals branch from 0e95164 to ad595cf Compare August 19, 2026 22:39
@dfalster
dfalster merged commit d31b465 into master Aug 19, 2026
7 checks passed
@dfalster
dfalster deleted the feature/consumer-change-signals branch August 19, 2026 22:43
dfalster added a commit that referenced this pull request Aug 20, 2026
…angco

Four conflicts, all in leaf_model.hpp, all where this branch and master changed the
same expression for different reasons:

  * three conductance conversions -- this branch replaced atm_vpd_ with vpd_leaf_,
    master made H2O_CO2_stom_diff_ratio a settable member. Both changes kept.
  * dprofit's early-return dE_dpsi -- this branch rewrote the derivation to state it
    directly, because the coefficient master's version cancels is no longer a single
    number once the deficit moves with Tleaf. This branch's version kept; it does not
    reference the ratio.

R/RcppR6.R was regenerated rather than hand-resolved, and man/ + NAMESPACE were
stale after the merge and regenerated too.

Verified: 566 C++ checks / 0 failures, golden bit-identical, gradient golden
unchanged, fingerprint unchanged at 3e36d9b94fd0, 1137 R tests / 0 failures,
R CMD check --as-cran Status: OK, ctest 2/2, bench targets build, and the CI
consumer program built against an installed prefix exits 0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A consumer cannot tell when the model's behaviour changed

1 participant