You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Merge/consolidate entities whose computed id was affected by a data mistake (typo, inconsistent title/name) #182
Every entity in this database takes its id from its own content. A change to the content a hash is
built from therefore produces a different entity rather than a changed one, and nothing re-points the
foreign keys that referred to the old id:
every CharacterId derived from it, plus Quote.SourceId
Character
source id, name, source type
Quote.CharacterId, the Character to Source links
Person
name
Quote.PersonId
Series
name
every SeasonId derived from it, plus Source.SeriesId
Season
series id, number
Source.SeasonId
Universe
name
Series.UniverseId
Two of those cascade two levels: a Series rename moves every Season beneath it, and a Source retitling
moves every Character resolved through it.
The fields a rule may change are exactly the governing ones. A ConflictResolutionRule governs source, date, type, character, author and quoteText; a SourceAliasRule corrects a title
and a date. So an id-moving change is the normal case during seeding, not an edge one.
Nothing in the codebase re-keys a row or follows a foreign key.Sql.Quotes.UpdateOnNewestWins
rewrites QuoteText and SourceId in place while matching on Id, so a row's identity and its
content diverge silently, and no code anywhere assigns a new Id to an existing row.
This is not fixable via the existing Modify/decidability pipeline (#162, #165, #171-#176): that
machinery corrects a field's value on an already-identified single row, matched either by explicit id
or natural key. It has no concept of "these two different ids are actually the same entity, please
consolidate them." A corrective re-import does not help either, since for a Quote the id incorporates
the source title, so correcting that field produces a third, differently-computed id rather than
fixing the original two.
What the mechanism has to decide, carried here because every sub-issue depends on the answers:
How dependents are re-pointed, and how conflicting field values between two rows are resolved.
Check whether FieldMergeResolver's existing FieldResolutionChoice vocabulary is directly
reusable or needs extending.
Whether it needs its own audit and reversibility story, mirroring Admin: targeted soft-reset and restore by import batch #59's reverse-an-applied-batch
precedent. Merging is destructive to one of the two rows, unlike a field-level Modify.
Whether a partial re-key, where some links moved and others did not, is repairable by the same
mechanism or needs its own.
The triggering example remains the one found while scoping #180: NikhilNamal17_popular-movie-quotes.json carries Lord Of The Ring - The Fellowships Of The Ring
alongside the correctly titled The Lord of the Rings: The Fellowship of the Ring, both dated 2001,
both type: movie, unmistakably the same film, computing to two permanently separate Source rows.
A dedupe keeps the row with the lowest rowid rather than the one whose id a curated file declares, so a quote's id depends on whether the install was upgraded or fresh
Background
Every entity in this database takes its id from its own content. A change to the content a hash is
built from therefore produces a different entity rather than a changed one, and nothing re-points the
foreign keys that referred to the old id:
QuoteGenre,QuoteTranslation,ConversationLineCharacterIdderived from it, plusQuote.SourceIdQuote.CharacterId, the Character to Source linksQuote.PersonIdSeasonIdderived from it, plusSource.SeriesIdSource.SeasonIdSeries.UniverseIdTwo of those cascade two levels: a Series rename moves every Season beneath it, and a Source retitling
moves every Character resolved through it.
The fields a rule may change are exactly the governing ones. A
ConflictResolutionRulegovernssource,date,type,character,authorandquoteText; aSourceAliasRulecorrects a titleand a date. So an id-moving change is the normal case during seeding, not an edge one.
Nothing in the codebase re-keys a row or follows a foreign key.
Sql.Quotes.UpdateOnNewestWinsrewrites
QuoteTextandSourceIdin place while matching onId, so a row's identity and itscontent diverge silently, and no code anywhere assigns a new
Idto an existing row.This is not fixable via the existing Modify/decidability pipeline (#162, #165, #171-#176): that
machinery corrects a field's value on an already-identified single row, matched either by explicit id
or natural key. It has no concept of "these two different ids are actually the same entity, please
consolidate them." A corrective re-import does not help either, since for a Quote the id incorporates
the source title, so correcting that field produces a third, differently-computed id rather than
fixing the original two.
What the mechanism has to decide, carried here because every sub-issue depends on the answers:
Minimal per-source conflict-resolution rule file + curated field-override preload #181's per-source rule files, a rule applied during seeding, or some combination.
Check whether
FieldMergeResolver's existingFieldResolutionChoicevocabulary is directlyreusable or needs extending.
precedent. Merging is destructive to one of the two rows, unlike a field-level Modify.
mechanism or needs its own.
Character: migrate to global identity via new Series/Universe schema (ADR + migration) #174 merges by design, same Name, Type-anchored and Series-scoped; this merges by correction,
recognising an existing content mistake. Do not assume the answer before Character: migrate to global identity via new Series/Universe schema (ADR + migration) #174 ships.
The triggering example remains the one found while scoping #180:
NikhilNamal17_popular-movie-quotes.jsoncarriesLord Of The Ring - The Fellowships Of The Ringalongside the correctly titled
The Lord of the Rings: The Fellowship of the Ring, both dated 2001,both
type: movie, unmistakably the same film, computing to two permanently separate Source rows.Sub-issues
rowidrather than the one whose id a curated file declares, so a quote's id depends on whether the install was upgraded or freshStableIdhashed the raw pre-correction source text, so no later correction can merge themScope boundary
rather than part of this body of work (developer, 2026-10-09 and 2026-10-10). It is wanted and must
be defined, but deliberately after the mechanism, because the mechanics decide what its options even
are: a routine that repairs a partial rename has to know both the original key's items and the new
key's, and that shape is settled by Consolidate and re-key entities whose derived id moved, and carry every dependent with it #440's requirements 3 and 6. Filed from Consolidate and re-key entities whose derived id moved, and carry every dependent with it #440's findings rather
than scoped speculatively now.
variants that want an alias rather than a correction) are data cases this mechanism could eventually
serve. They are not sub-issues: each owns its own data decision.
otherwise.
deliver it.
Definition of done