Commit 67e5e954b4

67e5e954b48c3203eac43ca31e975332b6439a49

parent: 00360ca116

Verified · cmc

cmc <hello@cleberg.net> · 2026-09-20 21:20 UTC

docs: preflight corrections to the push plan

UserDefaults has no Int64 overload, so the stored device id round
trips through a conditional NSNumber bridge; store it as Int. And a
test closure stored on the session captured it strongly.

Ref krz/gitbay#89

Layout: unified · split

docs/superpowers/plans/2026-09-20-push-notifications.md +14 −4
@@ -572,8 +572,11 @@ private func twoAccounts() async throws -> (SessionStore, StubProtocol.Box) {
572572 let (session, _) = try await twoAccounts()
573573 let victim = try #require(session.accounts.first)
574574 var sawTokenDuringHook: Bool?
575 session.willRemoveAccount = { account in
576 sawTokenDuringHook = session.client(for: account) != nil
575 // [weak session] because this closure is stored ON session; the
576 // production wiring in gitbayApp captures the registrar weakly for
577 // the same reason.
578 session.willRemoveAccount = { [weak session] account in
579 sawTokenDuringHook = session?.client(for: account) != nil
577580 }
578581 await session.remove(victim)
579582 #expect(sawTokenDuringHook == true)
@@ -753,7 +756,12 @@ final class PushRegistrar {
753756 ["notifications", "device", "add", "--label", Self.deviceLabel],
754757 stdin: token, as: Registered.self)
755758 if let id = out?.id {
756 defaults.set(id, forKey: Self.idKey(account))
759 // Stored as Int, which UserDefaults handles natively —
760 // it has no Int64 overload, so an Int64 goes in as a
761 // boxed NSNumber and comes back out through a
762 // conditional bridge. Int is 64-bit on every device
763 // this ships to.
764 defaults.set(Int(id), forKey: Self.idKey(account))
757765 }
758766 } catch {
759767 // A side channel. The account still works and the next
@@ -767,7 +775,9 @@ final class PushRegistrar {
767775 /// wired, and that hook is awaited before the token is discarded.
768776 func deregister(_ account: Account) async {
769777 let key = Self.idKey(account)
770 guard let id = defaults.object(forKey: key) as? Int64,
778 // object(forKey:) rather than integer(forKey:), which cannot
779 // tell a stored 0 from an absent key.
780 guard let id = defaults.object(forKey: key) as? Int,
771781 let client = session.client(for: account) else { return }
772782 try? await client.run(["notifications", "device", "remove", String(id)])
773783 defaults.removeObject(forKey: key)