fix(pii): remove real member names tied to payments
- Scrub three parity fixtures that still held raw bank messages - Use fictional names in the junior regression test, its changelog entry and its plan file - State the rule in CLAUDE.md: a real name as a bare test input is fine; a real name next to payment details, or any junior or parent name, is not - Tick T4 in the cleanup task list Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,12 +2,12 @@
|
||||
|
||||
## Context
|
||||
|
||||
A bank payment from sender `JIŘÍ KUBÍK` with the message
|
||||
`Jáchym Kubík: 01/2026+03/2026+04/2026` is being inferred as
|
||||
`[?] Jáchym Hrušák (G)` instead of the obvious `Jáchym Kubík`, even though
|
||||
A bank payment from sender `JIŘÍ DVOŘÁK` with the message
|
||||
`Jáchym Dvořák: 01/2026+03/2026+04/2026` is being inferred as
|
||||
`[?] Jáchym Novotný (G)` instead of the obvious `Jáchym Dvořák`, even though
|
||||
the message contains his exact full name.
|
||||
|
||||
**Root cause** (confirmed with the user): `Jáchym Kubík` is in the **junior**
|
||||
**Root cause** (confirmed with the user): `Jáchym Dvořák` is in the **junior**
|
||||
attendance sheet only — he does not appear on the main/adults sheet. But
|
||||
[scripts/infer_payments.py:101-102](scripts/infer_payments.py#L101-L102)
|
||||
builds `member_names` by calling `get_members_with_fees()`
|
||||
@@ -15,17 +15,17 @@ builds `member_names` by calling `get_members_with_fees()`
|
||||
`EXPORT_URL` (the adults sheet). Junior-only members are therefore invisible
|
||||
to the matcher.
|
||||
|
||||
With Kubík absent from `member_names`, the matcher in
|
||||
With Dvořák absent from `member_names`, the matcher in
|
||||
[scripts/match_payments.py:65](scripts/match_payments.py#L65) processes the
|
||||
combined text `jiri kubik jachym kubik: 01/2026+03/2026+04/2026` against an
|
||||
combined text `jiri dvorak jachym dvorak: 01/2026+03/2026+04/2026` against an
|
||||
adults-only roster:
|
||||
|
||||
- The exact-full-name short-circuit (`match_payments.py:75-84`) finds nothing —
|
||||
no adult's full name is in the text.
|
||||
- Hrušák `(G)` is the only adult with first name `Jáchym`. He fails the
|
||||
- Novotný `(G)` is the only adult with first name `Jáchym`. He fails the
|
||||
auto-rules (his surname isn't in the text) but hits the partial-first-name
|
||||
review rule (`match_payments.py:123-125`) → returned as `("Jáchym Hrušák (G)",
|
||||
"review")`, rendered as `[?] Jáchym Hrušák (G)`.
|
||||
review rule (`match_payments.py:123-125`) → returned as `("Jáchym Novotný (G)",
|
||||
"review")`, rendered as `[?] Jáchym Novotný (G)`.
|
||||
|
||||
The user's original framing — "exact match in message should win over
|
||||
everything" — is already implemented for any candidate that **is** in the
|
||||
@@ -33,7 +33,7 @@ roster (the May-04 short-circuit). The bug is upstream: the right candidate
|
||||
was never even considered.
|
||||
|
||||
**Goal:** make `infer_payments` consider junior members as candidates, so
|
||||
junior-only names like `Jáchym Kubík` get matched correctly.
|
||||
junior-only names like `Jáchym Dvořák` get matched correctly.
|
||||
|
||||
## Approach
|
||||
|
||||
@@ -98,23 +98,23 @@ changes are needed.
|
||||
the adults version, so `m[0]` works for both.
|
||||
- [scripts/match_payments.py:65-137](scripts/match_payments.py#L65-L137) —
|
||||
`match_members` already handles the precedence the user wants (exact full-name
|
||||
short-circuit), so once Kubík is in `member_names`, the case will be auto-matched
|
||||
short-circuit), so once Dvořák is in `member_names`, the case will be auto-matched
|
||||
with no `[?]`.
|
||||
|
||||
## Verification
|
||||
|
||||
1. **Manual sanity** — re-run inference on the offending row:
|
||||
- Clear `Person`/`Purpose` for the Kubík row in the payments sheet.
|
||||
- Clear `Person`/`Purpose` for the Dvořák row in the payments sheet.
|
||||
- `make infer`.
|
||||
- Expect `Person = Jáchym Kubík`, `Purpose = 2026-01, 2026-03, 2026-04`,
|
||||
- Expect `Person = Jáchym Dvořák`, `Purpose = 2026-01, 2026-03, 2026-04`,
|
||||
no `[?]`.
|
||||
|
||||
2. **Unit test** — extend
|
||||
[tests/test_match_members.py](tests/test_match_members.py) (or add a small
|
||||
`tests/test_infer_payments.py`) to assert that, given a roster that
|
||||
includes `Jáchym Hrušák (G)` and `Jáchym Kubík`, the message
|
||||
`Jáchym Kubík: 01/2026+03/2026+04/2026` resolves to
|
||||
`[("Jáchym Kubík", "auto")]` only. This is really a regression test for
|
||||
includes `Jáchym Novotný (G)` and `Jáchym Dvořák`, the message
|
||||
`Jáchym Dvořák: 01/2026+03/2026+04/2026` resolves to
|
||||
`[("Jáchym Dvořák", "auto")]` only. This is really a regression test for
|
||||
the May-04 short-circuit — the new behavior under test is just that
|
||||
`infer_payments` now feeds in juniors.
|
||||
|
||||
@@ -122,7 +122,7 @@ changes are needed.
|
||||
|
||||
4. **Dashboard smoke** — `make web`, open `/payments`, confirm the row now
|
||||
shows the correct member; open `/juniors`, confirm the payment is
|
||||
credited to Kubík for the three months listed.
|
||||
credited to Dvořák for the three months listed.
|
||||
|
||||
5. **Changelog** — once the user confirms the fix, append an entry to
|
||||
[CHANGELOG.md](CHANGELOG.md) per [CLAUDE.md](CLAUDE.md):
|
||||
|
||||
@@ -58,7 +58,8 @@ before new feature work starts. Companion to the Go rewrite tracker,
|
||||
current year.
|
||||
- Same applies to the `sync-2026` Makefile target.
|
||||
- Moot if Python is retired before then.
|
||||
- [ ] **T4** Remove member names from tracked files (PII rule in `CLAUDE.md`).
|
||||
- [x] **T4** Remove member names from tracked files (PII rule in `CLAUDE.md`).
|
||||
Done 2026-10-09 in a narrower form, see Notes & decisions. Original task:
|
||||
- `CHANGELOG.md` line 29 names a member in a fix description.
|
||||
- `tests/test_match_payments.py`, `tests/test_reconcile_exceptions.py` and
|
||||
`go/internal/domain/reconcile/reconcile_test.go` use names that may be
|
||||
@@ -172,3 +173,16 @@ Plan: [2026-10-08-1103-api-parity-vs-months-to-show.md](2026-10-08-1103-api-pari
|
||||
## Notes & decisions
|
||||
|
||||
(Add entries as you go. Format: `YYYY-MM-DD — short note`.)
|
||||
|
||||
- 2026-10-09 — T4 scope decided by the owner: a real name as a bare test
|
||||
input is acceptable; a real name next to payment details, raw bank
|
||||
messages, or any junior or parent name is not. Replaced the junior
|
||||
regression-test names (test, changelog, plan) with fictional ones and
|
||||
scrubbed three parity fixtures that still held raw bank messages. The
|
||||
other test names stay. Git history was not rewritten. Rule recorded in
|
||||
`CLAUDE.md`.
|
||||
- 2026-10-09 — The `real_*` parse_month_references seeds pick rows from the
|
||||
live payments cache by index (`_REAL_PMR_INDICES`). The sheet has changed
|
||||
since capture, so `make capture-fixtures` now rewrites those fixtures with
|
||||
different messages. Commit only the fixtures a change needs, or replace
|
||||
the index-based seeds with fixed synthetic ones.
|
||||
|
||||
Reference in New Issue
Block a user