Skip to content

Make Faker the documented standard for fixture pseudonymization - #35

Merged
gavinbee merged 2 commits into
mainfrom
faker-pseudonymization-standard
Aug 7, 2026
Merged

Make Faker the documented standard for fixture pseudonymization#35
gavinbee merged 2 commits into
mainfrom
faker-pseudonymization-standard

Conversation

@gavinbee

@gavinbee gavinbee commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

deck-eval-parser already does fixture pseudonymization well — seeded Faker, en_CA, constrained vocabularies for swimming terms, generator beside the fixture. Nothing documented it, so it read as one repo's local habit rather than the org standard.

This came up building the rules-oracle fixtures for officials-admin (S0.2). That issue says to "use the SwimBlocks PII guidance and tooling rather than inventing a scheme" — but the guidance was three bullets amounting to "scrub before commit", and there is no tooling. Without this, that repo would have grown a second, unaudited scheme.

Deliberately documentation only. No shared code: the generator belongs beside the fixture it produces, following deck-eval-parser, and a .github repo isn't the place for scripts unrelated to org settings.

Two things worth a look, since both are easy to get backwards:

  • Prefer fully synthetic data. Pseudonymizing real data is only justified when its real structure is the point — reproducing a legacy system's behaviour, where real dates and credential combinations are what's under test.
  • Read only the columns you need. Personal data never fetched cannot leak, and it turns review into checking a column list rather than inspecting a diff. The S0.2 generator never requests names or emails at all.

It also states plainly that pseudonymized ≠ anonymous — dates plus category combinations re-identify people inside a club of a few hundred — so fixture READMEs shouldn't imply otherwise.

🤖 Generated with Claude Code

deck-eval-parser has been doing this properly for a while — seeded Faker,
locale en_CA, constrained vocabularies for the domain terms Faker doesn't
know, generator script beside the fixture it writes. Nothing said so, so the
next repo to need fake people had no reason to land in the same place, and
officials-admin nearly grew a second scheme for its rules-oracle fixtures.

Records the convention and the reasoning behind each rule, because the rules
look arbitrary until you know what breaks without them: unseeded fixtures make
diffs meaningless and failures unreproducible; mapping a real value to more
than one fake value silently destroys the referential integrity that is often
the thing under test; and the real-to-fake mapping is a re-identification
table, so it belongs with local secrets rather than in git.

Two points that are easy to get backwards. Prefer generating synthetic data
outright — pseudonymizing real data is only warranted when its real structure
is the point, such as reproducing a legacy system's behaviour. And when that
does apply, read only the columns you need: personal data never fetched cannot
leak, and it reduces review to checking a column list rather than inspecting a
diff. Also states plainly that pseudonymized is not anonymous, since dates and
category combinations re-identify people inside a club of a few hundred.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@gavinbee

gavinbee commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

ruff CI check failure unrelated to this PR. Filed and fixing in #36.

gavinbee added a commit that referenced this pull request Aug 7, 2026
…37)

Closes #36. Unblocks #35, which is red through no fault of its own.

## The problem

`CONTRIBUTING.md` line 79 has documented `select = ["E","F","I","W"]`
since the lint gate existed, but **no config file ever enforced it** —
there's no `pyproject.toml`, `ruff.toml`, or `.ruff.toml` in this repo.
So `ruff check .` ran with whatever the installed version defaults to,
and `requirements-dev.txt` carries `ruff>=0.6`.

Net effect: the gate's rule set can change with no commit to this repo.
`main` is green because it hasn't re-run since a newer ruff landed, not
because it's clean.

## Not a no-op

Adding the documented config was more than paperwork — the documented
selection is stricter in a direction this code had **never** been
checked against:

| Configuration | Findings |
|---|---|
| No config (before) | 4 — `SIM102` ×2, `PLW1510`, in
`scripts/apply-settings.py` |
| `lint.select = ["E","F","I","W"]` | 8 — all `E501` line-too-long |

So both sets are real and disjoint. This PR wraps the 8 long lines (7 in
`apply-settings.py`, 1 in `test_apply_settings.py`) — all mechanical, no
behaviour change.

`SIM102` and `PLW1510` sit outside the documented selection and no
longer fire. **Widening the selection to include them is a deliberate
policy change** and belongs in its own PR, since every repo cites this
standard as org-wide. Worth noting `PLW1510` isn't a latent bug here
anyway: `apply-settings.py` inspects `result.returncode` immediately
after the call, so "fixing" it with `check=True` would change the
control flow.

## Why pin the selection, not ruff

Pinning ruff exactly would contradict the `>=` minimums policy in
`CONTRIBUTING.md` § Dependencies, and would only defer the problem to
the next bump. Pinning the rule *selection* makes the gate deterministic
while leaving that policy intact.

## Verification

```
ruff check .   ->  All checks passed!
pytest -q      ->  11 passed
```

This fixes `.github` only. The same documented-but-unenforced selection
affects other
repos, and two of them don't lint at all — tracked in #38.

`CONTRIBUTING.md` line 79 needed no edit — it already described this
selection. It's now true.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@gavinbee
gavinbee merged commit a41f13b into main Aug 7, 2026
1 check passed
@gavinbee
gavinbee deleted the faker-pseudonymization-standard branch August 7, 2026 04:21
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