What brokeUpdated 3 October 2026
Marking one variant as owned marked the whole brand
In our gin app, "I have this" was stored per brand, but variants all shared the brand's id, so owning Hendrick's Sunspell meant owning all of Hendrick's. The fix stores ownership per variant, and old saved data keeps working.
The short version
What went wrongOwnership was stored by brand id, and all variants shared that id. Marking Hendrick's Sunspell as owned marked every Hendrick's variant as owned.
How we found itThe bug showed up as a symptom in use: mark one variant and the whole brand shows as owned. No test is recorded as catching it.
The rule nowWhen you add a new dimension such as variants, review every place that uses the old id as a key. Store the new level so old saved data still works.
This post documents something Claude got wrong. The claim rests on a commit or a file, not on memory. All of Claude's mistakes
The app park has an app about gin. It keeps two personal lists: gins you like and gins you have at home. The second list feeds a shopping list. It works out which ingredients you need for the drinks your bottles can make.
This post is about the second list. For a while it could not tell one bottle from another inside the same brand.
What went wrong
Some brands in the app come in variants. Hendrick’s is the example in the commit: the commit names Sunspell, and a code comment mentions Neptunia and the original. In the app, the variants live under one brand and share that brand’s id.
The “I have this” button (in the Norwegian interface, «Jeg har denne») saved that brand id. Before the fix, the code that kept track of owned gins only knew about ids. You toggled an id on or off, and asking “do I own this?” meant asking whether the id was in the list. Variants were never part of the key.
The commit that fixed it, dated 16 June 2026, puts it plainly (translated from Norwegian):
All variants shared brand.id, so marking e.g. Hendrick’s Sunspell marked all of Hendrick’s.
So if you opened Hendrick’s, picked Sunspell and pressed “I have this”, the app saved “Hendrick’s”. Every other Hendrick’s variant then showed as owned too. The shopping list read that same id, so it had no way to know which bottle you had.
How it surfaced
The commit describes the bug by its symptom: mark one variant, and the whole brand shows as owned. That is what you see when you use the app, not what a test reports. Neither the commit nor any other material shows a test catching it. The material also has no record of when variants were added to the app, so we can’t say how long the bug was live.
The repository has a changelog, but it only started on 31 August 2026. It says so itself, and it points to the git history for anything older. This fix is older, so it is not in the changelog; the commit message is where it is recorded.
Who made the mistake
Henning writes no code himself, so the original ownership code was Claude’s, and so was the code that added variants without changing it.
What the fix changed
The fix gives each variant its own key. The key is now built from the brand plus the variant. Owning Sunspell is stored as the Hendrick’s id followed by the Sunspell key.
The base variant is an exception. It is still stored as the plain brand id, as before. That makes the change backward compatible: lists that users already had saved on their device still load, and every old entry now reads as “I own the base variant of this brand”. One side effect follows from this, and the code makes it visible. If someone had marked Sunspell before the fix, their saved entry was just “Hendrick’s”, and after the fix it reads as the original. The data never recorded which variant they meant, so the fix can’t recover it.
With variant-level keys in place, the commit had to decide what each part of the app should show. It settles on two levels:
- The brand grid (the overview of all brands) shows ownership per brand. A card is marked as owned if you own at least one variant of that brand. Tapping the mark on a card removes every variant of that brand you own. If you own none, it adds the base variant.
- The detail view shows ownership per variant. The button follows the variant you have selected, and its label now names that variant, so it says you have Sunspell rather than a generic “this one”. For the base variant it still says “this one”.
- The shopping list works per variant. Each saved key is now resolved to the specific variant before the app looks up ingredients, so the list reflects the bottle you actually own. When you tap an owned bottle in the list, the app pulls the brand id back out of the key and opens that brand’s page.
A comment added in the detail view gives the reason in one line: you can own Neptunia without owning the original.
Why this kind of bug is easy to miss
The old code did exactly what it was written to do: store an id, check for an id. The trouble is that “id” quietly meant two things after variants arrived. To the data it meant “this brand”. To the person pressing the button it meant “this bottle”. As long as every brand had only one bottle, those were the same thing.
Adding variants added a new dimension to the data, but the ownership list kept using the brand id as its key. The fix left the favourites list in the same app unchanged, and the detail view still checks favourites by brand id, which is probably fine, since liking a gin is a reasonable thing to do at brand level. That difference is the point: each place that uses the id as a key needs an explicit decision, not an assumption.
The rule
When you add a new dimension to the data, such as variants under a brand, go through every place that uses the old id as a key. For each one, decide whether it should stay at the old level or move to the new one. Then make sure the stored format can tell the levels apart without breaking data people already have saved.
In this app that means ownership is stored per brand and variant, and the base variant keeps the plain brand id so older saved lists still work.
Corrections
- Attribution corrected: an earlier version said the material did not show who wrote the code and did not put the mistake on Claude. Henning writes no code himself; the code was written by Claude.