Description
Migration009 (#374) deletes duplicate (QuoteText, SourceId) rows, keeping the lowest rowid. On a
database seeded by 1.8.2 that removes the curated "Hello. My name is Inigo Montoya…" (da53310a, the
id quotinator-curated.json states explicitly) and keeps the NikhilNamal17 copy (f3557c41). A fresh
install keeps da53310a instead. The same quote is therefore served under two different ids depending on
how the install began, and a consumer that stored an id breaks on one of them. The migration's own
comment says the bundled corpus holds exactly one duplicate pair; the 1.8.2 upgrade removes four (799 →
795 quotes).
Reproduction steps
create a container from ghcr.io/dutchjafo/quotinator:1.8.2 with --bind; wait for ready.
reenter it with quotinator:local.
GET /api/v1/quotes/da53310a-21c1-4742-a0b6-bcd981f926ca and …/f3557c41-4d2e-4145-82f5-7f0ea4df5d7f.
- Repeat 3 on a fresh
create of quotinator:local.
Expected behaviour
One id for the quote whichever way the install began — the curated file's explicit id, which a curated
conversation (a11bc923-…) also references.
Actual behaviour
Upgraded: da53310a → 404, f3557c41 → 200. Fresh: da53310a → 200, f3557c41 → 404. The
conversation still renders on both (its line was repointed to the surviving row on the upgrade), so the
visible damage is the id itself. Measured 2026-09-22.
And every reseed on an upgraded database reports one curated quote it could not write:
[Database - Seed] quotinator-curated.json report: … Quote[incoming=13 new=0 unchanged=12 … blocked=1 …]
with a Blocked Quote Add for da53310a waiting in GET /api/v1/import/actions. Observed on the
developer's own database during #411's T1 pass and reproduced on a 1.8.2 upgrade; after a Reset the same
file reads new=13, and every reseed after that unchanged=13. So the curated quote is not merely
unreachable by its id — it is blocked on every reseed, permanently.
Knowledgebase
A quote id that worked before an upgrade answers 404
(docs/knowledgebase/a-quote-id-stops-working-after-an-upgrade.md), status QTN-KNOWN.
Update it as part of this issue. An install already upgraded keeps whichever id it kept, so the entry
may outlive the fix: say which versions are affected, what a fix does to an install that already
upgraded, and drop the status code. Delete it only once no affected install can exist — no release ever
carried this condition, so there is nothing to retire, and docs/knowledgebase.md's retention rule
governs.
Failing tests
| Test class |
Test method |
Status before fix |
| (to be written, Core migration tests) |
Migration009_DuplicateWithAnExplicitCuratedId_KeepsTheCuratedId |
❌ |
| (live, to be written) |
Upgrade from 1.8.2 and a fresh install serve the curated Inigo quote under the same id |
❌ |
Definition of done
Description
Migration009 (#374) deletes duplicate
(QuoteText, SourceId)rows, keeping the lowestrowid. On adatabase seeded by 1.8.2 that removes the curated "Hello. My name is Inigo Montoya…" (
da53310a, theid
quotinator-curated.jsonstates explicitly) and keeps the NikhilNamal17 copy (f3557c41). A freshinstall keeps
da53310ainstead. The same quote is therefore served under two different ids depending onhow the install began, and a consumer that stored an id breaks on one of them. The migration's own
comment says the bundled corpus holds exactly one duplicate pair; the 1.8.2 upgrade removes four (799 →
795 quotes).
Reproduction steps
createa container fromghcr.io/dutchjafo/quotinator:1.8.2with--bind; wait for ready.reenterit withquotinator:local.GET /api/v1/quotes/da53310a-21c1-4742-a0b6-bcd981f926caand…/f3557c41-4d2e-4145-82f5-7f0ea4df5d7f.createofquotinator:local.Expected behaviour
One id for the quote whichever way the install began — the curated file's explicit id, which a curated
conversation (
a11bc923-…) also references.Actual behaviour
Upgraded:
da53310a→404,f3557c41→200. Fresh:da53310a→200,f3557c41→404. Theconversation still renders on both (its line was repointed to the surviving row on the upgrade), so the
visible damage is the id itself. Measured 2026-09-22.
And every reseed on an upgraded database reports one curated quote it could not write:
with a
BlockedQuote Addforda53310awaiting inGET /api/v1/import/actions. Observed on thedeveloper's own database during #411's T1 pass and reproduced on a 1.8.2 upgrade; after a Reset the same
file reads
new=13, and every reseed after thatunchanged=13. So the curated quote is not merelyunreachable by its id — it is blocked on every reseed, permanently.
Knowledgebase
A quote id that worked before an upgrade answers 404
(
docs/knowledgebase/a-quote-id-stops-working-after-an-upgrade.md), statusQTN-KNOWN.Update it as part of this issue. An install already upgraded keeps whichever id it kept, so the entry
may outlive the fix: say which versions are affected, what a fix does to an install that already
upgraded, and drop the status code. Delete it only once no affected install can exist — no release ever
carried this condition, so there is nothing to retire, and
docs/knowledgebase.md's retention rulegoverns.
Failing tests
Definition of done