maintenance: CI flake guard, stale paths, two-tier acceptance tolerance (3.14.20, 3.14.21) - #251
Merged
Merged
Conversation
PR #246 went red on `probe (3.13)` with ##[error]Failed to CreateArtifact: Unable to make request: ENOTFOUND AFTER 13m48s of the probe's own work had passed. A DNS blip reaching GitHub's artifact endpoint turned a green run red, and the remedy was a human noticing that a red check was not a real failure. That last part is the actual cost, and it is why this is worth fixing rather than re-running: it teaches people to re-run reds instead of reading them, in a repo whose CI discipline is "red means stop". A flake amplifier is worse than a flake. Both uploads are DIAGNOSTICS, and each file says so itself: intel-mac's comment is "when the install fails, the requirement file and whatever pip managed to resolve are the diagnosis", and tests.yml's transcript feeds gen_durations.py. So losing one costs a diagnostic, while failing the job on one costs a signal -- and `if: always()` means they run precisely when something else has already gone wrong, i.e. exactly when the real failure most needs to be the thing you see. `continue-on-error: true` on the two upload steps, so a job's verdict comes from the work it did. The alternative considered and not taken -- keep the failure but move the aggregate gate's decision ahead of the upload -- keeps a lost artifact visible but leaves the shard itself red, which is the half that misleads. Verified: both workflows still parse, and both upload steps are the only `upload-artifact` uses in either file. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
All of these PASSED -- the strings are arbitrary Parameter labels, docstring
examples, or synthetic configs the generic config layer does not validate.
They are worth fixing for one reason: a reader who greps `lens.t_E` to
understand what a test means finds nothing in the source, and then has to
work out whether the test is stale or the code is. That is the same cost the
multiseed and MMEXOFAST-converter ports paid down; this is the tail of it.
test_bad_user_input.py label="lens.t_E" x4 -> mulensevent.t_E
test_microlensing_physics.py same, x1
test_mode_evidence.py a docstring example, lens.t_0_raw
test_element_parameterization.py a selector error-message label
test_mkparam.py the writer's synthetic per-source config
test_param_specificity.py gets a DOCSTRING fix rather than a rewrite, and
the reason is worth recording: its `TWO_SOURCE_SYSTEM` is a PRE-SPLIT config
(`lens: [{lenses, sources}]`) and it passes only because ConfigManager does
not validate a lens block -- the same property that had made four
test_config_provenance tests inert until the 8.6.17 sweep. Here it is
harmless, because the check under test is component-agnostic and a
fictional lens exercises it as well as a real component would. So the
scenario is now labelled historical, with the note that this direction lost
its shipped instance in the split and the LIVE case is the shorter one
(`detrend_coeffs`).
LEFT ALONE, deliberately: anything already framed as history ("Before the
split this was ...", "HISTORICAL:") is correct as written.
STILL OPEN: test_latex_macro_xref.py:228 asserts
`_latex_varname("lens.12.t_E") == "ezlensonetwotE"`. Pointing it at a real
path changes the expected mangled string, so it needs a run to confirm
rather than a guess.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…(3.14.20) ONE tolerance (rtol 1e-6 / atol 1e-3) was sized to the worst cross-platform difference, so it applied everywhere -- including on the machine that RECORDED the fixtures, where no regression smaller than that can be detected at all. A genuine 1e-4 nat change on the reference machine passed. Now each fixture records the platform it was measured on, and the test picks its tier: the recording machine is held tight, anything else to the physics tolerance. No per-platform fixtures, no calibration, and a new platform gets the loose tier automatically rather than no coverage. MEASURED, not assumed. PR #246's macOS job dumped every term: 786 of 925 bit-identical, largest ABSOLUTE difference 2.2e-06 nats, signs mixed 74/65 (scatter, not a directional offset), macOS on accelerate against Linux's scipy-openblas. For scale, 0.1 sigma on a 1-D Gaussian is ~5e-3 nats. THE STRICT TIER IS 1e-14, NOT ZERO, and that came out of running the generator's --check BEFORE touching a fixture. 12 of 13 reproduce exactly; OGLE_0383LD's RV:mulensinstrument.model comes back 26832.036090685935 -> 26832.03609068592 (5.4e-16 relative, the same delta on every run), so that recorded value is about an ulp stale against current code and rtol=0 would have failed the reference platform against its OWN fixtures. 1e-14 is a few ulp here: still eight orders tighter than the cross-platform tier. THE FIXTURES WERE NOT REGENERATED. Regeneration rewrites every recorded value from current code, and these are a PRE-SPLIT recording that cannot be reproduced -- it would have silently adopted that OGLE_0383LD ulp instead of leaving it visible. One key added per file, every other key verified byte-for-byte: 65 insertions, 0 deletions. The fingerprint is COARSE -- system, machine, BLAS NAME, not version -- so an OpenBLAS point release that changes no result cannot silently demote the reference machine to the loose tier, and one that DOES change a result is caught by the loose tier anyway. test_both_tolerance_tiers_are_reachable exists because every fixture in the tree carries this machine's fingerprint, so the strict tier is the only one the suite exercises here: a bug making is_reference_platform answer True unconditionally would be invisible while holding macOS eight orders too tight. It asserts all three directions (own / foreign / unstamped) with a fabricated fingerprint. Same shape as the vacuity failures in docs/testing.md -- the guarded path passes and the unguarded one is never entered. Verified: 29 passed on the strict tier, delta dump unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PR #251 went red on ubuntu 3.12/3.13/3.14 and macOS 3.12 with OGLE_0383LD's RV:mulensinstrument.model moving 26832.036090685935 -> 26832.036090686284: relative 1.3e-14, against a REFERENCE_RTOL of 1e-14, and in the OPPOSITE direction from this box's 5.4e-16. The fingerprint {system, machine, blas} names a platform CLASS, not a machine. CI's Linux runner matched it, therefore claimed to be the machine that recorded the fixtures, and was held to a bit-identity tolerance it had no reason to meet -- OpenBLAS dispatches kernels by CPU capability at runtime, so one class rounds two ways. Making the fingerprint finer (CPU model, OpenBLAS core) is a guess about which attributes matter and would demote the reference machine on any hardware change. Requiring the claim to be STATED is honest instead: EXOZIPPY_ACCEPTANCE_STRICT plus a fingerprint match. The default -- CI included -- is the physics tolerance, which is always safe. Also REFERENCE_ATOL = 1e-30 for the denormal term CI flagged alongside it (POT:low_bound.mulensevent.mu_rel_mag at -2.0e-220), where a relative test is meaningless. Same trap as the delta dump's 266% outlier, whose absolute delta was 3.5e-22. test_both_tolerance_tiers_are_reachable now asserts the new direction as well: WITHOUT the opt-in nothing claims the strict tier. That is the assertion #251 lacked. Verified: acceptance 24 passed on the default tier (what CI runs) and 24 passed with EXOZIPPY_ACCEPTANCE_STRICT=1. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
test_both_tolerance_tiers_are_reachable asserted is_reference_platform() on a SHIPPED fixture. Those carry the recording box's fingerprint (Linux), so the assertion holds only there -- macos-latest 3.12 shard 2 went red with "this machine does not match the fingerprint in its own fixtures". The subject of the test is the SELECTOR, not where the suite runs. So stamp the own-machine case with platform_fingerprint() and it matches on any runner, and make the foreign case an obviously fabricated platform rather than macOS -- naming a real platform made the test read as though it cared which one. Verified: passes locally; the assertions no longer reference the recorded fingerprint at all, so they are platform-independent by construction. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three independent maintenance changes from the 8.6.17 follow-up list.
1. A failed diagnostic upload must not fail the job (3.14.21)
PR #246 went red on
probe (3.13)withFailed to CreateArtifact: Unable to make request: ENOTFOUNDafter 13m48s of the probe's own work had passed. A DNS blip reaching GitHub's artifact endpoint turned a green run red.That last part is the cost, and why it is worth fixing rather than re-running: it teaches people to re-run reds instead of reading them, in a repo whose CI discipline is "red means stop". A flake amplifier is worse than a flake — and
if: always()means these steps run precisely when something else has already gone wrong, i.e. exactly when the real failure most needs to be the thing you see.Both uploads are diagnostics, and each file says so itself.
continue-on-error: trueon the two of them, so a job's verdict comes from the work it did. The alternative considered and rejected — keep the failure but move the aggregate gate ahead of the upload — keeps a lost artifact visible but leaves the shard red, which is the misleading half.2. Retire the last references to paths that no longer exist
All of these passed: they are arbitrary
Parameterlabels, docstring examples, or synthetic configs the generic config layer does not validate. Worth fixing for one reason — a reader who grepslens.t_Efinds nothing in the source and then has to work out whether the test is stale or the code is.test_param_specificity.pygets a docstring fix rather than a rewrite, and the reason is worth recording: itsTWO_SOURCE_SYSTEMis a pre-split config that passes only becauseConfigManagerdoes not validate a lens block — the same property that had made fourtest_config_provenancetests inert until the 8.6.17 sweep. Here it is harmless, because the check under test is component-agnostic. So the scenario is labelled historical, with a note that the LIVE case for that check is nowdetrend_coeffs.Left alone deliberately: anything already framed as history. Still open:
test_latex_macro_xref.py:228asserts_latex_varname("lens.12.t_E") == "ezlensonetwotE"; pointing it at a real path changes the expected mangled string, so it needs a run rather than a guess.3. Two-tier acceptance tolerance keyed on a platform fingerprint (3.14.20)
ONE tolerance (rtol 1e-6 / atol 1e-3), sized to the worst cross-platform difference, applied everywhere — including on the machine that recorded the fixtures, where no regression smaller than that can be detected at all. A genuine 1e-4 nat change on the reference machine passed.
Each fixture now records the platform it was measured on. The recording machine is held tight; anything else gets the physics tolerance. No per-platform fixtures, no calibration through CI, and a new platform gets the loose tier automatically instead of no coverage.
Measured, not assumed. PR #246's macOS job dumped every term: 786 of 925 bit-identical, largest absolute difference 2.2e-06 nats, signs mixed 74/65 (scatter, not a directional offset), macOS on
accelerateagainst Linux'sscipy-openblas. For scale, 0.1 sigma on a 1-D Gaussian is ~5e-3 nats.The strict tier is 1e-14, not zero, and that came out of running the generator's
--checkbefore touching a fixture. 12 of 13 reproduce exactly;OGLE_0383LD'sRV:mulensinstrument.modelcomes back26832.036090685935 -> 26832.03609068592(5.4e-16 relative, the same delta every run). So that recorded value is about an ulp stale against current code, andrtol=0would have failed the reference platform against its own fixtures. 1e-14 is a few ulp here — still eight orders tighter than the cross-platform tier.The fixtures were not regenerated. Regeneration rewrites every recorded value from current code, and these are a pre-split recording that cannot be reproduced — it would have silently adopted that
OGLE_0383LDulp instead of leaving it visible. One key added per file, every other key verified byte-for-byte: 65 insertions, 0 deletions.The fingerprint is coarse — system, machine, BLAS name, not version — so a point release that changes no result cannot silently demote the reference machine to the loose tier, and one that does change a result is caught by the loose tier anyway.
test_both_tolerance_tiers_are_reachableexists because every fixture here carries this machine's fingerprint, so the strict tier is the only one the suite exercises locally: a bug makingis_reference_platformanswer True unconditionally would be invisible while holding macOS eight orders too tight. It asserts all three directions (own / foreign / unstamped) with a fabricated fingerprint. Same shape as the vacuity failures indocs/testing.md— the guarded path passes and the unguarded one is never entered.Verification
Pre-push hook green (ruff, ruff format, full suite). Acceptance 29 passed on the strict tier with the delta dump unchanged; 200 passed across the files touched in (2).
🤖 Generated with Claude Code