Skip to content

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.

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.

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 from gold_affiliations
  • is_employee — boolean, derived from gold_contracts
  • is_external — boolean, derived from project memberships outside the institution
  • is_grant_holder — boolean, derived from gold_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.

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:

  1. Compute a join across the three sources using a probabilistic match on name + birth year + active dates.
  2. Surface unresolved IDs as a data-quality finding (a person in HR with no procedural counterpart, or vice versa).
  3. Materialise the resolved identity as gold_persons.canonical_person_id and 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.

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 confirm thesis_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.

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.yaml to 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_id so downstream signals can filter against identity-quality.
  • An audit trail of which match decisions were probabilistic vs. SME-overridden.

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.

  • 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)
jazzisnow jinflow is a jazzisnow product
v0.64.7 · built 2026-09-20 19:48 UTC