| name | hardening-research-code |
| description | Use when research or scientific code must be trustworthy — verifiably correct, regression-safe, and numerically stable. Triggers: harden this code, is this numerically correct, add regression tests vs known results, make the research code robust. |
Hardening Research Code
Make research and scientific code trustworthy: correct, regression-safe, and
numerically stable by establishing explicit criteria and backing them with
targeted tests.
Interaction mode
This skill leans Direct by default. For the full Collaborative-vs-Direct protocol and override rules, see the Interaction Modes reference in the ai-research-workflows:using-research-workflows skill.
Purpose
Research code often starts as exploratory scripts. Hardening turns it into
trustworthy software: outputs are verifiably correct, regressions are caught
automatically, and numerical behavior is documented and bounded.
Iron Law: a regression baseline is not a correctness check
Pinning the current outputs proves only that behavior hasn't changed — not
that it is right. Never label outputs as a verified or "golden" baseline until
they are checked against an independent reference (analytic limit, reference
implementation, published result, or invariant).
Before falling back to regression-pinning, you MUST confirm no analytic limit,
reference implementation, or invariant is reasonably derivable. If one is,
verify against it first — "the output looks plausible" is not verification. If
you pin outputs that have NOT been independently verified, label the baseline
UNVERIFIED so it is never mistaken for established-correct values, and say so
to whoever relies on it.
Establish correctness criteria
Before writing any test, decide what "correct" means for this code:
- Analytical / closed-form solutions — does the code reproduce a result
that can be derived by hand or from first principles? (e.g., the discrete
Fourier transform of a pure sine wave has a single nonzero bin.)
- Reference implementations — does it match a trusted, established library
or codebase on shared inputs?
- Published results — does it reproduce a table, figure, or metric from a
paper within stated tolerances?
- Conservation laws / invariants — does a physical or mathematical
invariant hold? (e.g., total energy is conserved, probability sums to 1,
a rotation matrix has determinant 1.)
Document the chosen criterion explicitly before writing tests. A test without
a stated criterion is uninterpretable when it fails.
Techniques
Golden / reference tests
Compute expected outputs from a trusted source — an analytic formula, a
reference implementation, or a published dataset — and assert that the code
under test matches those outputs within tolerance. Store expected values and
any input data in a versioned file alongside the tests.
Numerical-tolerance comparisons
Never use exact float equality. Always use absolute or relative tolerances
appropriate to the domain:
# strategy-level pseudocode — defer mechanics to python-testing skill
assert |result - expected| < atol + rtol * |expected|
Document the tolerance choice: what physical or numerical argument justifies
it? If tolerance is tightened or loosened later, the justification must be
updated.
Choosing the bound: start from the floating-point floor — roughly machine
epsilon times the number of accumulating operations (atol ≈ N · eps · scale)
— and widen only for a documented reason (iterative-solver tolerance, stochastic
variance). Use rtol for values spanning orders of magnitude, atol for values
near zero. Never tune the tolerance to make a failing test pass — a bound
chosen to fit the result is a silent correctness hole; derive it from the
numerics, not the output.
Regression tests pinned to accepted outputs
Use this only after confirming no analytic limit, reference implementation,
or invariant is reasonably derivable (see the Iron Law) — a regression baseline
is a fallback, not a substitute for verification. When it genuinely is the only
option: run the code on canonical inputs, check the outputs against whatever
partial criteria you can (ranges, signs, invariants, expected trends), and pin
them. Future runs must stay within tolerance of that baseline. If the pinned
values were not verified against an independent reference, label the baseline
UNVERIFIED so it is never mistaken for established-correct output. Update the
baseline deliberately, with a commit message explaining why the output changed.
Stability checks
Perturb inputs, random seeds, data types (e.g., float32 vs float64), or batch
sizes, and verify that outputs vary by a bounded amount. A result that changes
dramatically under small perturbations is not trustworthy, regardless of how
close it is to a reference on the nominal input.
Property / invariant checks
Assert structural properties that must hold regardless of the specific input
values: symmetry, monotonicity, conservation, idempotence, or range bounds.
These catch whole classes of bugs that value-comparison tests miss.
Deferral
Defer pytest fixtures, parametrization, numpy.testing / torch.testing
assertion mechanics, and CI configuration to the
scientific-python-development:python-testing skill. This skill is the
research-specific strategy layer: what to validate and why. Apply the
same principles to R, Julia, Fortran, or any other runtime.
Workflow
- Pick criteria — choose from the options in "Establish correctness
criteria" above; document the choice in a comment or README near the tests.
- Add the smallest tests that capture them — one test per criterion; avoid
testing implementation details.
- Run — and confirm each test can fail. Confirm the tests pass on the
current codebase, then confirm each correctness test actually fails when its
criterion is violated (perturb the expected value or the input, watch it go
red, then restore). A test that has never been seen to fail proves nothing.
- Record tolerances and reference-data location — note the tolerance
values and where reference/golden data lives. Cross-reference the
ai-research-workflows:ensuring-reproducibility skill for test-data provenance (content hashes,
retrieval dates, lockfile references).
Red flags — STOP, you're locking in a wrong result
The deadline temptation is to pin whatever the script prints and call it hardened:
| Thought | Reality |
|---|
| "The output looks plausible, I'll pin it as the baseline" | Plausible ≠ correct. Check a derivable reference/invariant first; otherwise label it UNVERIFIED. |
| "There's an analytic case but it'd take a while to derive" | That derivation is the correctness anchor. Do it before pinning, or mark the baseline UNVERIFIED. |
"float == is fine here, it's the same machine" | It won't be the same machine in CI or for the next person. Use tolerances. |
| "I'll widen the tolerance so the test passes" | Tuning the bound to the result hides the bug. Derive the bound from the numerics. |
| "It passed on the first run, so it's correct" | A test never seen to fail proves nothing. Make it go red, then green. |
Common Mistakes
- Exact float equality — using
== on floating-point results fails on trivially different hardware or library versions; always use absolute or relative tolerances appropriate to the domain.
- Tests with no stated correctness criterion — a test that asserts a specific number without explaining why that number is correct is uninterpretable when it fails; document the analytical, reference, or published-result source for each expected value.
- Un-versioned reference data — storing golden outputs in a file without a version tag, content hash, or commit reference means a silent data change can break tests with no explanation; version reference data alongside the tests.
- Testing implementation details — asserting internal intermediate values rather than observable outputs couples tests to the implementation; test at the boundary where the correctness criterion applies.
Quality checklist
Before marking hardening complete:
Cross-references
Integrates with the ai-research-workflows:validating-implementations skill (confirming the
hardened code still satisfies its plan's success criteria) and the
ai-research-workflows:ensuring-reproducibility skill (provenance for reference data and pinned
baselines).