fix: authoritative interaction state, cancellation-safe rollback, bounded locks (P1-1, P2-2, P2-4) #98
No reviewers
Labels
No labels
auth
bug
composer
correctness
database
durability
enhancement
feeds
interactions
notifications
performance
reliability
safety
security
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
starsetbyte/peregrine!98
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/p1-interaction-authority"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes P1-1, P2-2 (interaction half), P2-4 from the 2026-07-11 adversarial delta review.
P1-1 — Interaction mutations no longer depend on incidental Room presence
InteractionRepositorynow returns authoritative updated interaction state (including the server record URI) instead of relying solely on Room row updates that silently affect zero rows for off-Room posts.SearchViewModel) and thread view (ThreadViewModel) merge this authoritative state instead of Room-only writes;ThreadViewModel's"optimistic"sentinel URI is removed (a later unlike/unrepost could previously attempt to parse it as an AT URI).P2-2 (interaction half) — cancellation no longer leaves optimistic state unreverted
catch, with rollback preserved viawithContext(NonCancellable)where it must survive cancellation.P2-4 — bounded per-post lock map
Mutexmap is now bounded via striped locks instead of retaining oneMutexper URI ever mutated.Notable implementation deviations (each independently re-derived and confirmed by task review — see the review doc's "Accepted deviations" note):
setStateuses_interactionStates.update { }rather than the plan's literal.value = .value + …, closing a lost-update race since different URIs use different lock stripes.Gate:
./gradlew testDebugUnitTest lintDebug— 0 failures, 0 lint errors. (2 pre-existing skips inAtProtoOAuthClientTestremain on this branch since it was cut frommainbefore branch A's P1-2 fix landed; resolves once A merges first.)Merge order: this is branch B of four (A → B → C → D — DM Phase A work should not proceed until A and C are merged).
Review pass 1 — revise
P1 — account cleanup is not an interaction-state epoch barrier.
AccountDataCleaner.clear()resets the map after clearing Room (AccountDataCleaner.kt:26-30), but an in-flight interaction can latersetState()on success or rollback (InteractionRepository.kt:176-185,:235-242). The DID is resolved lazily at request construction (:147-149,:169-174), so an A-account operation may even reach the service after B is saved. Bind operations and overlay entries to a session generation/DID, cancel or invalidate active operations at login/logout, and suppress stale completion/rollback writes. Add a deterministic switch/logout race test.P1 — the map wins over every fresh server snapshot for the session. It has unconditional precedence and no per-URI reconciliation (
InteractionRepository.kt:120-145), so ambiguous completion/local DAO failure can keep Search/Thread visibly wrong despite a refresh. Treat it as a pending overlay and retire/reconcile it when an authoritative snapshot supersedes it.P2 — optimistic state and first DAO write are outside the protected rollback region.
updateLike/updateRepostcan throw aftersetState()but before thetry(:156-160,:215-219), stranding a null-URI pending state whose next tap becomes a no-op. Cover DAO failure and restorebeforeunder the lock.Targeted InteractionRepository and ThreadViewModel tests pass locally; they do not cover the above races.
2ac195c0b03b00ac1308Merged to main (
87933e8)Review pass 2 — all P1 findings remediated
11 commits. All targeted tests pass.
Content from
fix/p1-interaction-authorityshipped tomainvia a local--no-ffmerge, which is why Forgejo shows this closed-unmerged rather than merged. Reconciled indocs/reviews/2026-07-25-branch-pr-reconciliation.md, which landed onmainvia #103. Branch deleted as part of that reconciliation.Pull request closed