83 lines
4.5 KiB
Markdown
83 lines
4.5 KiB
Markdown
# Scholarr rewrite - identity model **[live 2026-07-23]**
|
|
|
|
This is an agreed direction from the 2026-07-21 discussion with Justin. It is now formalized in the
|
|
review-ready, unfrozen draft at `/opt/stacks/scholarr-rewrite/docs/specs/data-model.md`. Part of the
|
|
[scholarr-rewrite](README.md) runbook; read the doctrine there first.
|
|
|
|
This document records how the rewrite decides *who an author is* and how it keeps the same person
|
|
from turning into two records. It is a decision record, not the final spec: it feeds the identity /
|
|
identifier-first-dedup spec when that spec is frozen.
|
|
|
|
## Root principle
|
|
|
|
Identity is anchored to **source identifiers**; names are **labels only**. Nothing is ever keyed
|
|
on a name. Two records are the same author because they share a resolved identifier, never because
|
|
they share a string.
|
|
|
|
## Same name, different people
|
|
|
|
Supported **by construction**: distinct external IDs are distinct `FollowedAuthor` records, so two
|
|
genuinely different people who happen to share a name simply resolve to two records and never
|
|
collide.
|
|
|
|
UX around this:
|
|
|
|
- **Disambiguation evidence at add time.** When a user is picking a candidate, show the deciding
|
|
evidence side by side: affiliation, fields, active years, sample works, and the identifiers
|
|
themselves. The user confirms a specific person, not a name.
|
|
- **One soft, non-blocking notice** when confirming a candidate whose name matches an
|
|
already-followed author with a **different** ID: "same person or different?" It does not block
|
|
the confirm; it just makes the collision visible.
|
|
- **The answer persists as a "confirmed different" tombstone**, so that exact pair is never
|
|
re-flagged again.
|
|
|
|
## Duplicate prevention: three mechanisms for three causes
|
|
|
|
Duplicates have three distinct causes, and each gets its own mechanism.
|
|
|
|
1. **Exact re-follows** (the same identifier followed again). Killed by a **uniqueness constraint on
|
|
`(source, external_id)`** plus the shared-identity model: there is one canonical author record
|
|
and per-user follow rows, so a second user following the same person **attaches to the existing
|
|
resolution** rather than creating a new author.
|
|
|
|
2. **The same human reached via different identifiers.** `AuthorSourceIdentity` is a **list on one
|
|
author**: a Scholar-imported shell, an OpenAlex ID, and an ORCID can all attach to a single
|
|
`FollowedAuthor` record. When a shell is later identified as an already-followed ID, the
|
|
resolution becomes a **MERGE**, **never a new
|
|
author**. The survivor is not assumed to be the already-followed record: the spec's
|
|
deterministic target rule governs every author merge (resolved beats shell, more strong
|
|
identities, older `created_at`, lowest `public_id`), regardless of which record was the review
|
|
subject. Merges are **audited and undoable**, because a wrong merge (collapsing two real people
|
|
into one) is the worst mistake the system can make.
|
|
|
|
3. **OpenAlex split profiles** (one real person holding more than one OpenAlex ID). One author may
|
|
hold multiple OpenAlex IDs via an **"also this profile"** action. The **works-overlap scoring**
|
|
used for identity calibration doubles as the duplicate detector here: it raises a **"possible
|
|
duplicate" review card** that resolves to either **merge** or **keep-separate** (keep-separate
|
|
writes the **confirmed-different** tombstone described above).
|
|
|
|
## Review-queue hygiene
|
|
|
|
The review queue is keyed by the **author entity**, never by the raising event. This keeps the
|
|
queue honest instead of flooding it.
|
|
|
|
- A shell that fails to resolve across ten syncs is **one card**, not ten.
|
|
- **Pairwise duplicate cards spawn once per pair**, unless genuinely new evidence appears (for
|
|
example, a newly shared identifier changes the picture).
|
|
- **Four card types:**
|
|
- **identify shell** - a Scholar-imported shell that has no resolved ID yet.
|
|
- **confirm ambiguous match** - the soft "same person or different?" from add time.
|
|
- **possible duplicate** - the works-overlap detector flagged two records as maybe the same
|
|
person.
|
|
- **not-this-person fallout** - cleanup when a prior resolution turns out to be wrong.
|
|
|
|
## Honest limit
|
|
|
|
Shells with **no name and no works** cannot be proven distinct. They stay **human-decided**, and
|
|
the UI **says so plainly** rather than guessing. The system does not pretend to resolve identity it
|
|
has no evidence for.
|
|
|
|
## Related
|
|
|
|
- The works-overlap scoring reused here as a duplicate detector: [identity-calibration.md](identity-calibration.md).
|
|
- Root entity and `AuthorSourceIdentity` decision: [README.md](README.md) (Locked decisions).
|