Skip to content

fix(catalog): keep namesakes with different provider IDs apart - #2082

Merged
Quick104 merged 1 commit into
Silo-Server:mainfrom
blurbery:fix/catalog-person-name-fallback-namesakes
Oct 8, 2026
Merged

Quick104 merged 1 commit into
Silo-Server:mainfrom
blurbery:fix/catalog-person-name-fallback-namesakes

Conversation

@blurbery

@blurbery blurbery commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Related issue: N/A
Validation tasks: changes #1143 C1 and C2; changes #1144 C1 and C3; changes Silo-Server/silo-apple#307 C1 and C3; changes Silo-Server/silo-apple#308 C1 and C3; changes Silo-Server/silo-android#317 C1 and C3; changes Silo-Server/silo-android#318 C1 and C3 (all passed on build 941)

Two different people with the same name can end up as one person in Silo. When a credit comes in and neither its TMDB ID nor its IMDb ID is stored yet, FindOrCreate and BatchFindOrCreate fall back to a case-insensitive name match and attach the credit to whoever already has that name, even when the two carry different provider IDs. One actor's films then appear on a namesake's person page, and the second person's provider IDs are thrown away.

This change lets the name fallback match only when the two share a provider ID, or when neither has any.

The person filmography and name search cases listed above passed with provider-identified fixture people. Those resolve by stored TMDB or IMDb ID before the name fallback runs, so their outcomes should still meet the criteria. Only namesakes with different provider IDs and name-only placeholders now resolve differently, which can change a filmography or add a same-name search result. Those cases should be re-checked after merge.

I've run this on my server since August. It has 1,127 names that belong to more than one TMDB person (2,271 people in total). Under the old fallback, each of those people whose provider IDs weren't stored yet when its first credit arrived would have been merged into the first person with that name.

Root cause: phase 3 of BatchFindOrCreate, and the last lookup in FindOrCreate, select by LOWER(name) alone and take the first match without looking at provider IDs.

Approach

  • The name lookups now read the candidates' provider IDs (TMDB, IMDb, TVDB, Plex GUID). canResolvePersonByName accepts a candidate only when the names match, no provider ID conflicts, and either the two share at least one provider ID or neither has any.
  • This changes what happens to name-only placeholders. A person created from a credit without IDs, for example from an NFO file, is a placeholder.
    • An identified person no longer takes over a placeholder with the same name; it gets its own row.
    • A name-only credit no longer attaches to an identified person; it reuses an existing placeholder or creates one.
    • So a library with both kinds of credit for one person now has two rows for them, where before it had one. On my server that's 34 placeholders that share a name with an identified person, out of 152,101 name-only people.
  • No API or client change. The native API and jellycompat read the same people rows, so the apps need nothing and no docs/*-api.md changes.
  • Lookups by stored TMDB or IMDb ID run first and are unchanged. A shared TVDB ID or Plex GUID still resolves through the name fallback.

Validation

  • go test ./internal/catalog/ -count=1: pass.

  • TestCanResolvePersonByName is a unit test of the rule. It covers a shared TMDB or TVDB ID, two placeholders, disjoint IDs, IDs from different providers, an identified person meeting a placeholder, and different names with a shared ID.

  • TestPersonNameFallbackKeepsNamesakesApartPostgres is a new database contract, listed in scripts/ci/db-contracts.txt. Through FindOrCreate and BatchFindOrCreate, it checks that:

    • a namesake with another TMDB or IMDb ID gets its own person;
    • a shared TVDB ID resolves to the stored person;
    • a second name-only credit reuses the placeholder.

    Against a migrated local PostgreSQL 18 database it passes on this branch, along with the other person database tests in internal/catalog. With main's person_repo.go it fails: "FindOrCreate merged a namesake with another TMDB ID into person …".

  • go vet ./internal/catalog/ and make lint-changed: clean.

  • On my server, the same change has run since August as part of my fork.

  • CI on b7c75b314: passed, including Go DB pins, which runs the new contract and fails on a listed test that is missing or skipped.

Benchmarks

Not applicable. The name lookups read four more columns from rows they already select, and still use the same LOWER(name) predicate.

Evidence

Which person a credit lands on is visible on item and person pages, but I have no before-and-after capture from an upstream build: that needs a deploy of main and of this branch against the same data. What I can show is the database test above, which fails on main and passes here, and the counts from my server, which runs my fork at 6ce7fb5cc with this change.

Evidence: https://evidence.siloserver.org/r/silo-server/pr-2082/

Risks

Checklist

  • I read and can explain the complete diff.
  • This pull request addresses one concern.
  • The Evidence section shows every change a user can see, or says there is none.

AI Disclosure

  • Harness: Claude Code (desktop app); the change itself first came from an OpenAI Codex session on my fork
  • Tool(s): Claude Code, OpenAI Codex
  • Model(s): claude-opus-5-5 (port, database test and description); the Codex model used on the fork commit was not recorded
  • Involvement: AI-assisted
  • Adversarial review: n/a. This ports a change my fork has run since August. I checked that main's ID and name lookups in both functions are unchanged since then (only the enrichment step after them changed), and added the database contract.

AI-assisted with Claude Opus. I directed the task and designed the work.

FindOrCreate and BatchFindOrCreate fall back to a case-insensitive name
match when no stored TMDB or IMDb ID matches. That fallback merged an
incoming person into any stored person with the same name, even when the
two carried different provider IDs, so one actor's credits could land on
a namesake.

The name fallback now resolves only when the two share a provider ID, or
when neither has any. An identified person no longer takes over a
name-only placeholder of the same name, and a name-only credit no longer
attaches to an identified person; each keeps its own row.
@silo-kody

silo-kody Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Silo Kody — review complete

Review finished. Check the inline comments for findings and verify each suggestion against the code and tests.

Reviewing changes in Silo
  • Include the related issue, expected behavior, and validation steps in the PR description.
  • For API changes, describe the effect on Apple and Android clients and Jellyfin compatibility.
  • For plugin changes, identify the affected SDK contract, plugin, and catalog entry.
  • Follow this repository's AGENTS.md and CONTRIBUTING.md.
  • Request another review with @kody start-review in a PR comment.
  • React with 👍 or 👎 to give feedback on individual suggestions.
Review settings
Review Options

The following review options are enabled or disabled:

Options Enabled
Bug ✅
Performance ✅
Security ✅
Business Logic ❌

@coderabbitai

coderabbitai Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 1 minute.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 4d2d56cf-b29f-4c28-83c7-31239aa69e0a
📥 Commits

Reviewing files that changed from the base of the PR and between ca186fe and b7c75b3.

📒 Files selected for processing (4)
  • internal/catalog/person_merge_test.go
  • internal/catalog/person_name_fallback_db_test.go
  • internal/catalog/person_repo.go
  • scripts/ci/db-contracts.txt
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Quick104 Quick104 added priority: P1 Core-path bug; painful workaround — next after P0 impact: data-loss User data deleted, corrupted, or unrecoverable labels Oct 8, 2026 — with Cursor
@macroscopeapp

macroscopeapp Bot commented Oct 8, 2026

Copy link
Copy Markdown

The changed fallback alters the person_id written to item_people for same-name credits with disjoint provider IDs. Person filmographies (C1) and name-search results (C3) can therefore change across Web, Apple, and Android clients as well as Server/API. The PR must explain why these previously validated outcomes are intended to change; the affected cases need re-validation after merge.

The current Validation tasks: line omits those client results. Suggested fix — replace it with:
Validation tasks: changes #1143 C1 and C2; changes #1144 C1 and C3; changes Silo-Server/silo-apple#307 C1 and C3; changes Silo-Server/silo-apple#308 C1 and C3; changes Silo-Server/silo-android#317 C1 and C3; changes Silo-Server/silo-android#318 C1 and C3

Automated check: Macroscope check run agent (gpt-6-luna). No validation was performed.

Posted via Macroscope — v1 validation impact

@Quick104
Quick104 merged commit 707dabb into Silo-Server:main Oct 8, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

impact: data-loss User data deleted, corrupted, or unrecoverable priority: P1 Core-path bug; painful workaround — next after P0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants