Skip to content

Consider row-level PII classification via the polymorphic Classification table #56

Description

@nate-layman

The problem

PII in v2 is currently a column-level boolean (pii: true on 5 columns of Personnel). That model has two limits the spec can't paper over:

  1. Conditional sensitivity. ContactDetails.Contact_Value is PII when Personnel_ID is set, public when Organization_ID is set (sponsor mailbox). A static column flag forces both rows to be treated the same.
  2. Composite sensitivity. OtherSupport rows are individually mundane (a sponsor name, a dollar amount, a date range). The disclosure property emerges from the chain: a Personnel_ID is attached + an amount is recorded + the disclosure is tied to a specific investigator. Same goes for COI financial disclosures, Effort charged amounts, etc. The PII signal lives in the row, not the column.

The idea

We already have a polymorphic Classification table. Today it carries science codes, sponsor categories, and similar scheme-bound labels. The pattern fits a Scheme = 'DataClassification' use case cleanly:

Classification_ID: 'cls-001'
Related_Entity_Type: 'OtherSupport'
Related_Entity_ID: 'os-12345'
Scheme: 'DataClassification'
Value_Code: 'PII'
Value_Label: 'Personally Identifiable Information'

Or richer, if institutions want finer classes:

Scheme: 'DataClassification'
Value_Code: 'HR_Confidential'   // or 'FERPA', 'HIPAA', 'Sponsor_Confidential', etc.

That lets the model express:

  • Row-level scope (only the rows that actually carry sensitive data are marked, instead of every Contact_Value row inheriting the flag from the column).
  • Multiple overlapping regimes (a row can carry FERPA AND HR_Confidential without us inventing a new column type).
  • Time-bound classifications (Classification rows are already attached to entities; effective dates could be added if needed).

Tradeoffs

Pros

  • No contortion of the column model for conditional/composite cases.
  • Reuses an existing universal pattern (polymorphic attachment), no new tables.
  • Lets institutions carry their own classification vocabulary in Value_Code without spec changes.
  • Plays well with the existing AllowedValues mechanism for sites that want a controlled vocabulary.

Cons

  • Consumers have to join through Classification to know what's sensitive, instead of reading a column flag. More expensive at query time, more complex for naive consumers.
  • The column-level flag is gone or duplicated. Either we keep both (drift hazard) or drop the boolean (breaking change).
  • Population is harder. Column-level flags are static, declared once in the schema. Row-level Classification rows have to be created at insert time and maintained as data changes (e.g., a Personnel record going from external to internal might change the classification).
  • Doesn't help the "free text column might contain PII" case (Communication.Body_Text, Action.Description, ActivityLog.Old_Value). Those would still need a column-level signal.

What this issue is asking

Not a decision. Just a parking spot for the idea.

Two reasonable interim positions:

  1. Keep the column-level boolean, widen its coverage to the obvious omissions (Home_Organization_Identifier, ContactDetails.Contact_Value, PersonnelCredential.Credential_Value, OtherSupport amounts, ConflictOfInterest financial fields, Effort charged amounts). Cheapest, no model change.
  2. Hybrid: keep the column flag for the "always PII" cases (names, ORCID, email), use Classification rows for conditional/composite cases. Documents that the column flag is the floor, not the ceiling.

If we ever do (2) or pure row-level, it's likely a v3 conversation because it changes the consumer contract.

Related

  • The polymorphic Classification table itself is at vignettes/udm-v2-schema.md (see Classification section).
  • Current PII flags: Personnel.First_Name, Middle_Name, Last_Name, Primary_Email, ORCID.
  • The flag is a signal only; what consumers do with it (mask, redact, RBAC) is institutional per scope statement.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions