The conflict
GratefulPatientFeaturizer and EncounterTransformer key on the donor's
latest discharge (grep -n "merge_key" philanthropy/preprocessing/_grateful_patient.py,
groupby on merge_key then take last_discharge). For a donor with more than
one encounter, a gift is measured against whichever discharge happens to be
most recent at fit/transform time, not the discharge that was actually closest
in time to that specific gift. Recurrent patients (oncology, transplant) are
the people this most misrepresents.
The fix needs the raw per-gift, per-encounter rows available at transform
time so each gift can be joined to its own nearest prior discharge. That is
exactly what __getstate__ is designed to strip
(grep -n "__getstate__" -A8 philanthropy/preprocessing/_grateful_patient.py):
raw encounter_df is PHI-bearing and is replaced with None on serialisation
so a saved model bundle never carries patient rows (#78).
Per-gift keying and the PHI-free bundle contract are in direct conflict. This
is not a bug with an obvious patch; it needs a design decision on which
guarantee gives way, or a third option (e.g. storing only a de-identified
per-donor encounter-to-gift offset at fit time, if that is precise enough).
Why it matters now
This is the one item from the 2026-08-13 audit that landed as "documented
limitation" rather than "fixed" going into the JOSS submission. It affects the
grateful-patient timing story the paper's Software design section describes as
solved via as_of; per-gift keying is a separate, still-open gap in the same
story.
What would close this
A written decision (this issue, a discussion, or a design doc) on one of:
- Keep latest-discharge keying, document the recurrent-patient
misattribution explicitly as an accepted limitation in
docs/explanation/compliance_considerations.md.
- Store a de-identified per-donor "days since nearest prior discharge, as of
each gift date" summary at fit time (no raw dates, no diagnosis), so
__getstate__ still has nothing PHI-bearing to strip.
- Some other resolution.
This needs a person to decide the tradeoff; it cannot be closed by a PR alone.
The conflict
GratefulPatientFeaturizerandEncounterTransformerkey on the donor'slatest discharge (
grep -n "merge_key" philanthropy/preprocessing/_grateful_patient.py,groupby on
merge_keythen takelast_discharge). For a donor with more thanone encounter, a gift is measured against whichever discharge happens to be
most recent at fit/transform time, not the discharge that was actually closest
in time to that specific gift. Recurrent patients (oncology, transplant) are
the people this most misrepresents.
The fix needs the raw per-gift, per-encounter rows available at
transformtime so each gift can be joined to its own nearest prior discharge. That is
exactly what
__getstate__is designed to strip(
grep -n "__getstate__" -A8 philanthropy/preprocessing/_grateful_patient.py):raw
encounter_dfis PHI-bearing and is replaced withNoneon serialisationso a saved model bundle never carries patient rows (#78).
Per-gift keying and the PHI-free bundle contract are in direct conflict. This
is not a bug with an obvious patch; it needs a design decision on which
guarantee gives way, or a third option (e.g. storing only a de-identified
per-donor encounter-to-gift offset at fit time, if that is precise enough).
Why it matters now
This is the one item from the 2026-08-13 audit that landed as "documented
limitation" rather than "fixed" going into the JOSS submission. It affects the
grateful-patient timing story the paper's Software design section describes as
solved via
as_of; per-gift keying is a separate, still-open gap in the samestory.
What would close this
A written decision (this issue, a discussion, or a design doc) on one of:
misattribution explicitly as an accepted limitation in
docs/explanation/compliance_considerations.md.each gift date" summary at
fittime (no raw dates, no diagnosis), so__getstate__still has nothing PHI-bearing to strip.This needs a person to decide the tradeoff; it cannot be closed by a PR alone.