This branch extracts all branch-inbox persistence logic from the monolithic storage.rs file into a dedicated inbox_store.rs module within the pika-news crate. The refactor moves 12 public methods on Store, 2 private helper functions, and the BranchInboxStateRow struct without changing any behavior or SQL queries. The only functional change is widening the visibility of artifact_user_state_rank from private to pub(crate) so the new module can reuse the shared ranking function, and converting merge_branch_inbox_state_alias from a file-private function to a pub(crate) function imported back into storage.rs.
Tutorial Steps
Create the new `inbox_store.rs` module
Intent: Provide a dedicated file to house all branch-inbox persistence logic, reducing the size and cognitive load of `storage.rs`.
A new file crates/pika-news/src/inbox_store.rs is created with 634 lines. It imports the shared types InboxItem, InboxReviewContext, and Store from crate::storage, as well as the ranking helper artifact_user_state_rank. All branch-inbox logic will live here as impl Store blocks and module-private helpers.
The module is structured as:
An impl Store block containing all public branch-inbox methods.
A BranchInboxStateRow struct (moved from storage.rs).
A pub(crate) function merge_branch_inbox_state_alias (moved from storage.rs).
Two private helper functions: merge_branch_inbox_state_rows and insert_branch_inbox_rows_for_artifact.
Move `populate_branch_inbox` and `backfill_branch_inbox_for_npub`
Intent: Relocate the two write-path methods that insert or refresh branch inbox rows for recipients.
populate_branch_inbox inserts inbox rows for a list of npubs when a new branch artifact becomes ready. backfill_branch_inbox_for_npub scans all current ready artifacts and creates inbox entries for a single newly-added npub. Both methods are moved verbatim, including their transactional wrappers and delegation to the private insert_branch_inbox_rows_for_artifact helper.
Move branch inbox query methods
Intent: Relocate the read-path methods that list, count, and provide review context for branch inbox items.
This method queries the chat_allowlist table for npubs where active = 1 OR can_forge_write = 1. It was previously located among the chat-allowlist methods in storage.rs but logically belongs with the branch inbox population logic, since its sole purpose is to determine inbox recipients.
Move `BranchInboxStateRow` struct and alias-merge functions
Intent: Relocate the internal data structure and merge logic used during npub alias consolidation.
The BranchInboxStateRow struct and two functions are moved:
BranchInboxStateRow — a module-private struct with 9 fields tracking per-npub branch inbox state.
merge_branch_inbox_state_alias — upgraded from fn to pub(crate) fn so storage.rs can import and call it. Handles re-keying or merging inbox rows when two npubs are discovered to be the same user.
merge_branch_inbox_state_rows — the pure merge logic comparing two rows by artifact ID and state rank, using artifact_user_state_rank from the parent module.
The merge strategy: when artifact IDs differ, the row with the most recent updated_at wins; when they match, the higher-ranked state wins (dismissed > inbox > other), with ties broken by updated_at.
This private function implements the three-way logic for inserting branch inbox rows:
Same artifact already exists — no-op (skip).
Different artifact exists — update to new artifact, reset state to 'inbox' with reason 'new_commits', clear dismissed_at.
No existing row — insert a fresh inbox entry.
It first validates that the given artifact_id corresponds to a ready, current branch_artifact_versions row and resolves the branch_id before iterating over recipients.
Widen visibility of `artifact_user_state_rank`
Intent: Allow the new `inbox_store` module to call the shared state-ranking function without duplicating it.
The only non-move change in storage.rs: artifact_user_state_rank is promoted from fn (file-private) to pub(crate) fn. This function maps state strings to numeric ranks (dismissed = 3, inbox = 2, default = 1) and is used by both the PR inbox merge logic (still in storage.rs) and the branch inbox merge logic (now in inbox_store.rs).
Register the module and wire the import
Intent: Make the new module visible to the compiler and ensure `storage.rs` can call the extracted alias-merge function.
main.rs — adds mod inbox_store; to the module declarations, alphabetically between github and live.
storage.rs — adds use crate::inbox_store::merge_branch_inbox_state_alias; so the existing alias-consolidation transaction in storage.rs can continue to call this function after it was moved out.
No other callers needed updating because all public Store methods remain on the same Store type — Rust resolves impl blocks across modules transparently.