Finding
The pattern fixed for the stats/user store in #450 (a repo UPDATE/DELETE that ignores rows_affected() and returns Ok(()) even when zero rows matched) recurs across the apotheke media-entity and quality repos that were outside that cluster's scope. A caller cannot distinguish "updated" from "no such row", so a stale or wrong id silently no-ops.
Evidence
Surfaced during the #450 sweep. The same unchecked write pattern appears in crates/apotheke/src/repo/ across audiobook.rs, book.rs, comic.rs, movie.rs, music.rs (four entity levels each), news.rs, podcast.rs, and quality.rs (update_profile / delete_profile) — roughly 20+ functions. #450 fixed user.rs, renderer.rs, and the play-history/streak paths; these remain.
Why this matters
Silent no-op writes hide bugs and make PATCH/DELETE endpoints built on them return success for operations that did nothing — the same class the zone.rs delete_zone/remove_member pattern (the canonical fix) was written to prevent. It is a correctness and API-honesty gap that spans the crate.
Desired correction
Apply the capture-rows_affected-then-NotFound-on-zero pattern uniformly to the remaining entity and quality repo functions (excluding genuinely-bulk user-scoped operations where zero rows is legitimate, e.g. revoke_api_keys_for_user). Consider a basanos/kanon lint rule that flags a repo UPDATE/DELETE whose QueryResult is discarded, so the class cannot regrow.
Done when: every single-row apotheke UPDATE/DELETE reports NotFound on a zero-row match (or is explicitly documented as an idempotent bulk op), with tests; a lint guards the pattern.
Finding
The pattern fixed for the stats/user store in #450 (a repo
UPDATE/DELETEthat ignoresrows_affected()and returnsOk(())even when zero rows matched) recurs across the apotheke media-entity and quality repos that were outside that cluster's scope. A caller cannot distinguish "updated" from "no such row", so a stale or wrong id silently no-ops.Evidence
Surfaced during the #450 sweep. The same unchecked write pattern appears in
crates/apotheke/src/repo/acrossaudiobook.rs,book.rs,comic.rs,movie.rs,music.rs(four entity levels each),news.rs,podcast.rs, andquality.rs(update_profile/delete_profile) — roughly 20+ functions.#450fixeduser.rs,renderer.rs, and the play-history/streak paths; these remain.Why this matters
Silent no-op writes hide bugs and make PATCH/DELETE endpoints built on them return success for operations that did nothing — the same class the zone.rs
delete_zone/remove_memberpattern (the canonical fix) was written to prevent. It is a correctness and API-honesty gap that spans the crate.Desired correction
Apply the capture-
rows_affected-then-NotFound-on-zero pattern uniformly to the remaining entity and quality repo functions (excluding genuinely-bulk user-scoped operations where zero rows is legitimate, e.g.revoke_api_keys_for_user). Consider a basanos/kanon lint rule that flags a repoUPDATE/DELETEwhoseQueryResultis discarded, so the class cannot regrow.Done when: every single-row apotheke
UPDATE/DELETEreportsNotFoundon a zero-row match (or is explicitly documented as an idempotent bulk op), with tests; a lint guards the pattern.