Skip to content

Fix missing GRDB background suspension handling - #241

Merged
torlando-tech merged 2 commits into
mainfrom
fix/grdb-suspension
Oct 27, 2026
Merged

torlando-tech merged 2 commits into
mainfrom
fix/grdb-suspension

Conversation

@torlando-tech

@torlando-tech torlando-tech commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

Problem

TestFlight's October 24 crash on 0.2.5 (146) records RUNNINGBOARD 0xdead10cc while thread 3 is executing MessageRepository.applyAnnouncedDisplayName inside a GRDB write transaction. The earlier node-lease fix (#230) is already in that release.

The app's GRDB pools set observesSuspensionNotifications = true, but the application never posted GRDB's explicit suspend/resume notifications. That flag does not observe UIKit lifecycle automatically. This is a confirmed protection gap; the captured stack does not establish conclusively which lock caused the production kill.

Changes

  • Install a process-level UIKit-to-GRDB suspension bridge at app initialization, independent of SwiftUI scene creation or inactivity.
  • Permit app database access only while foregrounded or holding an authorized background-task grant. Notifications remain process-local and do not suspend the NE.
  • Give overlapping background deliveries independent grants, including retained-before-handler deliveries. Revoke grants on completion and immediately on expiration, without waiting for Python cancellation cleanup.
  • Track expiration across the launch handoff so expired tasks cannot start queued work.
  • Gate synchronous repository creation on permitted access: newly opened pools otherwise miss suspension notifications posted before they exist.
  • Preserve GRDB interruption errors and the atomic announced-name/custom-nickname predicate; no blind retries or fabricated success.
  • Document the lifecycle contract in ARCHITECTURE.md.

Verification

  • Failing before: ran the real repository regression against unchanged main (c99ae584) with only the test added. UIKit background notification did not stop the announced-name write; the lifecycle test failed with 3 assertions. Direct GRDB suspension already passed, isolating the missing bridge.
  • Passing after: 50 targeted tests passed (repository suspension, background coordination, call history).
  • Full hosted suite: 664 tests passed, 0 failures, on the Mac mini / iPhone 17 iOS 27 simulator. App and embedded Network Extension built successfully.
  • Runtime smoke: launched the actual app, backgrounded via Safari for 150 seconds, reopened with the same PID (68124), and visually confirmed the Chats screen. Simulator had no configured network interfaces.

The smoke validates lifecycle/reopen behavior, not production crash elimination under announce traffic. Physical-device 0xdead10cc prevention and NE background message delivery have not been reverified in this change.

Greptile correction (38869e6)

  • Protected every queued call-history write with a bounded UIKit background task and matching database-access token, including insertions and final outcomes during background audio.
  • Completion and expiration release both resources; denied grants never resume GRDB, and expired queued writes do not start.
  • Real-repository regression reproduced missing background call history before the correction. Added denial/expiration coverage; all 664 hosted tests pass afterward.
  • Executable app smoke (temporary launch-only harness, removed afterward): PASS persistedEnd=true failedOutcome=true accessRevoked=true. This exercises actual CallManager/repository writes while app database access is suspended; it is not a real remote audio-call test.

@greptile-apps

greptile-apps Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[High impact] The PR appears safe to merge; the background call-history gap is addressed.

Summary

The PR connects UIKit lifecycle events to GRDB suspension and protects authorized background database work.

  • App database access follows foreground state or a live background grant.
  • Propagation tasks keep database access only while their grants are live.
  • Call-history writes use bounded grants while they wait and run.

The previous call-history finding is addressed. No new actionable issues were found. Tests were not run during this review.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[App lifecycle or background grant changes] --> B{Foreground or live grant?}
    B -->|Yes| C[Resume app GRDB pools]
    B -->|No| D[Suspend app GRDB pools]
    E[Call-history write] --> F[Acquire bounded UIKit task and access token]
    F --> G[Wait for earlier writes]
    G --> H{Grant still active?}
    H -->|Yes| I[Write call history]
    H -->|No| J[Skip expired write]
    I --> K[Release token before ending UIKit task]
    L[Task expires] --> K
    K --> A
Loading

Reviews (2) · Last reviewed commit: "fix: protect background call-history wri..." · Reviewed by Greptile

@torlando-tech
torlando-tech merged commit ba3f652 into main Oct 27, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant