refactor(db): dedupe cross-bundle wrappers into shared mixins (#1135) - #1283
Conversation
004defe to
02decd1
Compare
🔍 Local review — cycle 1/3 (clean)Reviewed locally via built-in This is a MOVE refactor (byte-identical relocation of pass-through wrappers into repo-scoped plain-class mixins). Both reviewers confirmed semantic equivalence:
No Tests: Totals: 0 FIX, 0 SKIP, 0 UNVERIFIED. Review cycle complete — merge is yours to trigger (local mode does not auto-merge). |
bundles.py duplicated identical repository pass-through methods across bundles (ChannelBundle/CollectionBundle channel+task ops, get_setting/ set_setting in four bundles, account reads in three, message search/batch insert in two, scheduler task reads). jscpd flagged the top clone cluster of issue #1135. Consolidate each duplicated wrapper group into a repo-scoped plain-class mixin (_ChannelOps, _CollectionTaskCreateOps, _CollectionTaskReadOps, _SettingsOps, _NotificationQueriesOps, _MessageOps, _AccountReadOps, _SearchLogReadOps). Mixins are deliberately NOT dataclasses: their attribute annotations do not become dataclass fields, so every bundle's positional constructor order is unchanged. Rebased onto a main where #1287 (vulture #1136) already dropped three dead bundle methods (list_live_usable_accounts, update_premium, get_message_stats) — those were unique to their bundles, so the dedup groups are unaffected; the rebase just no longer carries them. The set_active name collision is handled by resolution: ChannelBundle/ CollectionBundle get _ChannelOps.set_active(pk), while AccountBundle (account_id), SearchQueryBundle (sq_id), PipelineBundle (pipeline_id) keep their own class-body definitions. Public API verified equivalent by inspect.signature over MRO: identical method set per bundle, identical fields, set_active resolves to the correct definition per bundle. Bundle method count 135 -> 108; the test_db_access_conventions ratchet baseline is lowered accordingly. Part of #1135 (axis 5 of #1130). Co-Authored-By: Claude <noreply@anthropic.com>
02decd1 to
50ef5b8
Compare
🔍 Local review — cycle 1/3 (clean, post-rebase)Rebased onto a This is a MOVE refactor (identical pass-through wrappers relocated into 8 repo-scoped plain-class mixins). Both reviewers confirmed semantic equivalence after rebase:
The subtle point both cleared: the No Tests: Totals: 0 FIX, 0 SKIP, 0 UNVERIFIED. Review cycle complete — merge is yours to trigger (local mode does not auto-merge). |
Part of #1135 (axis 5 of #1130). Stays off the parity surface — only genuine within-repo duplication is touched.
What
src/database/bundles.pyduplicated identical repository pass-through methods across bundles:ChannelBundle+CollectionBundleget_setting/set_setting— four bundles (Collection, Notification, Search, Scheduler)list_accounts,list_account_summaries) — three bundlessearch_messages/insert_messages_batch— Collection + Searchget_recent_searches— Search + Schedulerjscpd flagged 5 clone pairs inside the file — the top cluster of #1135.
How
Each duplicated wrapper group moves into a repo-scoped plain-class mixin (
_ChannelOps,_CollectionTaskCreateOps,_CollectionTaskReadOps,_SettingsOps,_NotificationQueriesOps,_MessageOps,_AccountReadOps,_SearchLogReadOps). A bundle mixes in only the groups whose repository field it already carries.Mixins are deliberately NOT dataclasses: their attribute annotations (
channels: ChannelsRepository, …) do not become dataclass fields, so every bundle's positional constructor order is unchanged.Verification
inspect.signaturediff of all 10 bundle classes before/after. The only diffs arePipelineBundle.list_accounts/list_account_summaries, which gain the return-type annotations the duplicates already carried.test_db_access_conventionsratchet baseline is lowered accordingly (it is a downward-only cap by design).tests/test_bundles.py(42) + bundle consumers (scheduler, search_queries, notifier, photo_loader, pipeline_service, regression-services: 178) green.jscpd
bundles.py:19↔facade.py:18import block;bundles.py:734↔photo_loader.py:456repo pass-through) are import-boilerplate / single thin wrappers, not worth a helper.Part of #1135 / #1130. Not for merge — review requested.
🤖 Generated with Claude Code