Genesis 02 — When a person is not atomic
The PhD-on-contract reconciliation, and why HR data needs different bones than billing data
In hospital data, a person is unambiguous. A row in persons is either a patient (a payer’s record), an employee (an HR record), or a clinician (a credential holder). The categories don’t overlap. The data sources don’t disagree.
In academic-institution data, the same human can simultaneously be:
- a doctoral student in one department
- a teaching assistant in another department
- a researcher on a third-party grant
- a contracted lecturer for an evening course
All four roles, same week, same human. The data sources frequently disagree — the SAP HR system has a userid, the procedural population system has a canonical_id, the orgunit registry has a third identifier, and these don’t always line up.
This is the week-1 surprise that defined the architecture of hrcentral’s Gold layer.
What hospital pack didn’t prepare for
Section titled “What hospital pack didn’t prepare for”The nuMetrix pack’s gold_persons treats personhood as a primary key. One row per person, joined to one role, joined to one orgunit. When a person changes role (a nurse becomes a senior nurse), the SCD model captures the transition as a versioned record.
The hospital pack works because role concurrency is rare. A nurse is not simultaneously a patient. A clinician is not simultaneously an employee in a different department. The data model can afford the simplification.
The academic-institution pack cannot. Role concurrency is the norm, not the exception. A PhD student who teaches an undergraduate seminar is not unusual — they are the default shape in a research university. The data model has to model the concurrency, not flatten it.
The Saturday rebuild
Section titled “The Saturday rebuild”On Saturday May 9 the pack landed two related commits:
ff15967— “Persons-as-atomic — SIS generator + bronze/silver/gold for student Persons”eca6f56— “PhD-on-contract reconciliation + role flags on gold_person_view”
The first introduces Persons-as-atomic as the design choice: every human gets exactly one row in gold_persons. The roles attach to the person via separate dimension tables (gold_affiliations, gold_contracts, gold_assignments, gold_qualifications), each of which can carry multiple rows for the same person.
The second introduces role flags on gold_person_view: derived columns on the materialised person view that surface the role concurrency without making the analyst join four tables to find it. A row in gold_person_view has:
is_student— boolean, derived fromgold_affiliationsis_employee— boolean, derived fromgold_contractsis_external— boolean, derived from project memberships outside the institutionis_grant_holder— boolean, derived fromgold_grants
Each flag is set independently from its own source. A PhD-on-contract reads as is_student=true AND is_employee=true. The analyst sees the truth without needing to know which source carried which piece.
What the reconciliation actually does
Section titled “What the reconciliation actually does”The harder problem isn’t modelling concurrency; it’s resolving identity across systems that don’t agree.
Three sources, three identifier keys:
- SAP HR emits employment data keyed by
userid(an institutional email-derived stub). - The procedural population system emits student affiliation data keyed by
canonical_id(a longer institutional ID). - The OrgUnit registry emits department membership data keyed by
person_ref(a third identifier).
The systems are operated by different teams and never deeply integrated. A given human appears in all three with three identifiers that are almost aligned but not quite. The reconciliation job:
- Compute a join across the three sources using a probabilistic match on name + birth year + active dates.
- Surface unresolved IDs as a data-quality finding (a person in HR with no procedural counterpart, or vice versa).
- Materialise the resolved identity as
gold_persons.canonical_person_idand use it as the join key for everything downstream.
By Saturday evening this was running. The first finding it produced: about 8% of persons in the worked-example tenant had one or more cross-system identity gaps. Each gap is a real data-management concern that exists in the institution today — not a defect, not a finding to action on right now, but a named artefact.
What this enables
Section titled “What this enables”Three signals that didn’t make sense before now do:
signal_affiliation_drift— a person whose affiliation appears in one source but not another, or whose affiliation status changes in one source without a corresponding change in the others. By Monday this signal would return 641 findings and confirmthesis_affiliation_drift(Genesis 01).signal_dual_affiliation_overload— a person whose concurrent affiliations exceed plausible workload (a doctoral student on three concurrent teaching contracts plus a grant). Surfaces a real attention-load risk.signal_succession_risk— a critical role held by a person whose contract is approaching expiry, with no obvious successor visible in the data. Reads role concurrency to identify who could succeed.
None of these signals would have been computable against a model that flattened persons to one role each.
What’s deliberately deferred
Section titled “What’s deliberately deferred”The reconciliation as it stands relies on probabilistic name+date matching. The Wisdom that would name this — “academic-institution identity reconciliation requires a per-tenant deterministic mapping for the long tail of unresolved cases” — does not yet exist in the pack as a SMEbit. It will, in a few weeks, once enough specific cases have been investigated to warrant codifying.
The next steps the reconciliation will likely take:
- A per-tenant
identity_reconciliation_50cents.yamlto override the probabilistic match for known-tricky cases (married-name changes, transliteration of non-Latin names, dual citizenship variants). - Confidence scoring on the materialised
canonical_person_idso downstream signals can filter against identity-quality. - An audit trail of which match decisions were probabilistic vs. SME-overridden.
Why this is in a Genesis journal
Section titled “Why this is in a Genesis journal”Because the first big domain-shaped surprise is the moment a pack stops being a clone of its predecessor and starts being its own thing. nuMetrix didn’t need persons-as-atomic; hrcentral does. nuMetrix didn’t need cross-source identity reconciliation; hrcentral cannot ship without it. The Saturday rebuild is the moment hrcentral’s data model crossed from “hospital pack with HR labels” to “academic-institution pack with its own shape”.
Six months from now this will probably be Wisdom #1 in the hrcentral SMEbits directory. For now it’s a Saturday’s work and a single architectural choice that paid for itself by the following Monday.
Sources
Section titled “Sources”- Architectural commits:
ff15967(Persons-as-atomic),eca6f56(PhD-on-contract reconciliation), 2026-05-09 - First signal that depends on the model:
signal_affiliation_drift(activated 2026-05-10, 641 findings 2026-05-11) - First thesis that depends on the model:
thesis_affiliation_drift(confirmed 2026-05-11) - The worked-example tenant: VAI (Valdoria Academic Institutions)