Build deterministic per-record person-year identifiers from source keys
person_year_id.RdHashes the native filing/person key (OBJECTID, TABLE_ID) into a stable
PYID- identifier. This is the durable back-link that ties a Part VII record
to its source row across the raw, classified, and linked panels – distinct
from the cross-year cluster id create_emp_ids() stamps as EMP_ID.
Details
OBJECTID is parser-independent (it comes from the IRS filing index, not the
XML body parse); TABLE_ID is the stored per-person key within a filing.
(OBJECTID, TABLE_ID) is unique and non-missing across the raw Part VII data,
so unlike a name/title key it needs no disambiguation and is unaffected by
downstream name or title cleaning. Both tokens are drawn from [A-Z0-9-], so
the | field delimiter below can never occur inside them.
The recipe is frozen under keyspec; bump it if the inputs or delimiter ever
change, so old and new ids never silently collide.
Future work – CONTENT_ID
PERSON_YEAR_ID depends on TABLE_ID, which is parser-derived and has broken
on pathological XML in the past. A parser revision that renumbers TABLE_ID
would shift every id. A parser-independent CONTENT_ID – a hash of
normalized native content fields (name variants + raw title + compensation) –
could be stored alongside as a recovery bridge to re-map old<->new ids for the
records whose content is stable. Not implemented yet; note that a parser
change severe enough to break TABLE_ID for a record would usually corrupt
that record's content extraction too, so CONTENT_ID mainly insures against a
global renumbering of otherwise-clean records. Persisting the raw OBJECTID
and TABLE_ID columns (see dev/01_preprocess_year.R) keeps that bridge
buildable after the fact.