Skip to content

test(determinism): sweep PYTHONHASHSEED and repeat cleaning 100 times - #477

Merged
kevincostner17 merged 1 commit into
mainfrom
test/determinism
Sep 16, 2026
Merged

kevincostner17 merged 1 commit into
mainfrom
test/determinism

Conversation

@kevincostner17

Copy link
Copy Markdown
Contributor

Why

Determinism was thin at the public-API level. tests/test_properties.py:11 is two calls on one fixture, and PYTHONHASHSEED was set at exactly one site (test_plan_hash_and_mostly.py:134) for repair-plan hashing only.

Nothing swept the hash seed across fd.clean itself — even though set and dict iteration order feeds category normalisation, dominant-variant selection and action ordering, which is precisely where a decision could silently track interpreter state.

What is asserted

On a frame that reaches dtype repair, sentinel handling, currency parsing and the semantic layer at once:

Property Result
100 repeated runs 1 distinct digest
PYTHONHASHSEED ∈ {0, 1, 42, 31337, 65535} identical digest
report.to_dict() across runs stable

The hash-seed cases run in a subprocess, because the seed is fixed at interpreter start and cannot be changed in-process.

What the digest covers

Cell values, dtypes, every action's step/column/count/risk/status, and decisions_hash.

Timing and memory fields are excluded on purpose, and the exclusion list is named in the module docstring rather than hidden in a helper: they are documented as volatile, and report.peak_memory is process-lifetime RSS. Asserting they repeat would be asserting that a wall clock repeats.

Verification

  • 7 tests, all passing. All three properties already hold on main, so these are regression guards.
  • Full suite py3.12: 6664 passed, 22 skipped, 0 failed, coverage 93.90%.
  • ruff check . clean repo-wide.
  • No library code changed.

Determinism was thin at the public-API level. tests/test_properties.py:11 is
two calls on one fixture, and PYTHONHASHSEED was set at exactly one site
(test_plan_hash_and_mostly.py:134) for repair-plan hashing only. Nothing swept
the hash seed across fd.clean itself, even though set and dict iteration order
feeds category normalisation, dominant-variant selection and action ordering --
the places where a decision could silently track interpreter state.

Three properties, on a frame that reaches dtype repair, sentinel handling,
currency parsing and the semantic layer at once:

- 100 repeated runs produce a single digest.
- The digest is unchanged across PYTHONHASHSEED 0, 1, 42, 31337 and 65535, each
  measured in a subprocess because the seed is fixed at interpreter start.
- report.to_dict() is stable between runs.

The digest covers cell values, dtypes, every action's step, column, count, risk
and status, and decisions_hash. Timing and memory fields are excluded on
purpose and the exclusion list is named in the module docstring: they are
documented as volatile, and report.peak_memory is process-lifetime RSS, which
cannot be reproducible. Asserting otherwise would be asserting that a wall
clock repeats.

All three already hold, so these are regression guards. No library code
changed.

Full suite 6664 passed / 0 failed, coverage 93.90%; ruff clean repo-wide.
@coderabbitai

coderabbitai Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 39f6a7a0-9d5e-49fa-affe-4926e200eb60


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@kevincostner17
kevincostner17 merged commit bd3d6fe into main Sep 16, 2026
19 checks passed
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.

1 participant