fix(e24): the review's low findings — dead constants, honest labels, a 500 that should be a 404

`add_pin`'s IntegrityError handler assumed the unique constraint was the only way in and
re-raised otherwise, so a target row pruned between `resolve_ref` confirming it and the
INSERT (a Plex sync dropping a removed item) tripped the FK and handed the user a 500.
Semantically that is the same "no such thing" the pre-check already returns. Untested: the
race needs two concurrent transactions.

The five per-kind unique constraints had no test at all — the route's own dedupe returns
before reaching the DB, and the migration drift check filters constraint diffs out as noise,
so they could go missing with every lane green.

Frontend, all from the same review: the shelf's unpin row is now `danger` like every other
destructive row in the app; the hero's artwork button says what it will actually do and
names the item instead of always announcing "Play"; the empty-budget message offers a reroll
rather than `StateMessage`'s "Retry", which promised a re-run of something that had not
failed; `setState` and the watch-later toggle catch their rejections instead of leaving an
unhandled promise and a click that silently does nothing.

`StateMessage` gained a `compact` rung, which retires the frontend's only two `!important`
overrides — they existed to beat the primitive's own padding from the call site, and were
the copyable idiom for the next surface that wanted a compact empty state.

Dead vocabulary removed or made load-bearing: `DISCOVER_BUDGETS` was exported, re-exported
and consumed by nothing while `Home.tsx` re-typed the same four bands (a `readonly
DiscoverBudget[]` accepts any subset, so a fifth band would have typechecked green and
rendered no chip); `PIN_KINDS`' value export had no consumer at all; `useComfortPin` no
longer publishes `pinIdOf`/`toggle`, which nothing outside the module calls.

Three comments corrected against the code they describe: a pointer to a `resolve.py` that
was never written, the file header's claim that Home defines no card component of its own,
and the `seen` window still quoting the 8 that was measured and abandoned for 32.

The rest of the review's survivors are logged on ROADMAP.md under "Deferred — E24 epic-end
deep-review (2026-09-08)" — the ones whose dissent was substantive, and the ones whose fix
is wider than the finding.
This commit is contained in:
2026-09-08 03:23:59 +02:00
parent cedebe0ffb
commit 3709b71a94
9 changed files with 81 additions and 30 deletions
+4 -6
View File
@@ -75,11 +75,9 @@ export function shelfMoveTarget(from: number, to: ShelfMove, length: number): nu
const targetKey = (target: PinTarget): string => `${target.kind}:${target.ref}`;
export interface ComfortPin {
/** The pin's id when the target is on the shelf, `null` when it is not, `undefined` while the
* shelf is still being fetched — three states, because "not pinned" and "not known yet" must not
* render as the same row. */
pinIdOf: (target: PinTarget) => number | null | undefined;
toggle: (target: PinTarget, title: string) => void;
/** `pinIdOf` and `toggle` stay module-local until a second consumer needs them: publishing them
* invites the next surface to re-derive the three-state loading dance (pinned / not pinned /
* not known yet) that `item` already encapsulates, which is what this file exists to prevent. */
remove: (pinId: number, title: string) => void;
/** The pin/unpin row, ready to spread into any `ActionMenuItem[]`. Empty for a null target. */
item: (target: PinTarget | null, title: string) => ActionMenuItem[];
@@ -201,5 +199,5 @@ export function useComfortPin(open: boolean): ComfortPin {
[pinIdOf, t, toggle],
);
return { pinIdOf, toggle, remove, item };
return { remove, item };
}