Commit c091e39943

c091e399437be5536da5e5047265b1491c24da0b

parent: c08f54fe1f

Verified · cmc

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

docs: correct the spec on identity and the privacy label

The .id is on the TabView inside ContentView, not on ContentView
itself, so ContentView's own state survives an account switch. Both
the spec and PushRouter's doc comment said otherwise, and that claim
was the stated reason for hoisting the router — the hoist is right,
the reason was not.

The device token is not declared under Identifiers. Records the
decision and its reasoning so a later pass does not read it as an
outstanding to-do.

Layout: unified · split

docs/superpowers/specs/2026-09-20-push-notifications-design.md +16 −5
@@ -105,9 +105,14 @@ call rather than being duplicated; a repository root or an unknown kind
105105yields no route, and such a tap opens the app without navigating.
106106
107107`PushRouter` is owned by `gitbayApp` beside `SessionStore`. That
108placement is the design: `ContentView` is `.id(account.id)`, so
109anything held inside it is destroyed by the account switch a
110cross-account tap performs.
108placement is the design: the `TabView` inside `ContentView` carries
109`.id(account.id)`, so a cross-account tap tears that subtree down and
110rebuilds it. A target held inside would go with it; held above, the
111rebuilt tree drains it.
112
113Note the `.id` is on the `TabView`, not on `ContentView` itself —
114`ContentView`'s own `@State` survives an account switch, which is why
115the Dashboard path has to be reset explicitly rather than by identity.
111116
112117## Registration
113118
@@ -225,8 +230,14 @@ an APNs token, and it does so itself.
225230- The **Push Notifications** capability on `org.gitbay.gitbay`, already
226231 enabled in the developer portal, needs its matching entitlement in
227232 the Xcode project.
228- The **privacy nutrition label** gains the device token under
229 Identifiers: not linked to the user, not used for tracking.
233- The **privacy nutrition label** does not gain the device token. It is
234 per-app, it rotates, it is not the advertising identifier, and it is
235 not used for tracking under any reading Apple's rules care about —
236 nothing is shared with a data broker and nothing is linked to
237 third-party data. Decided, not skipped; do not add it on a later pass
238 without raising it again. Note the token *is* stored against the
239 account (`push_devices.user_id`), so if it were ever declared it would
240 be "linked to the user", not unlinked.
230241- An App Store resubmission. If Apple asks what push is for, it is
231242 activity on repositories the account follows.
232243
gitbay/Push/PushRouter.swift +10 −4
@@ -2,10 +2,16 @@ import Observation
22
33/// Carries a tapped notification's destination into the view tree.
44///
5/// Owned by `gitbayApp`, above `ContentView` — which is `.id(account.id)`
6/// and is therefore destroyed and rebuilt by the account switch a
7/// cross-account tap performs. A target held inside that subtree would
8/// go with it; held here, the rebuilt tree drains it on appear.
5/// Owned by `gitbayApp`, above `ContentView`. The `TabView` inside
6/// `ContentView` carries `.id(account.id)`, so the account switch a
7/// cross-account tap performs tears that subtree down and rebuilds it.
8/// A target held inside would go with it; held here, the rebuilt tree
9/// drains it.
10///
11/// The `.id` is on the `TabView`, not on `ContentView` itself, so
12/// `ContentView`'s own `@State` survives the switch — which is why the
13/// Dashboard path is reset explicitly in the drain rather than by
14/// identity.
915@Observable
1016@MainActor
1117final class PushRouter {