Update Live Activity agent counts from hook pushes in the background - #433
o-borovets wants to merge 1 commit into
Conversation
|
Did you test this on device? I am not observing the live activity update from a push notification in any manual tests yet. I know the OS scheduling of updates isn't exactly application controlled so that may be a part of it. |
|
Yes, I tested it and received notifications that updated the Live Activity, but it seems like that was some kind of edge case, since now I can't consistently reproduce it I'll keep investigating |
|
I wonder if we need |
|
Follow-up on the question about the device. I could not reproduce an update either, so I went to find out why. What I found is below, along with a replacement design and the parts I cannot do alone. What we foundI read the ActivityKit documentation and the forums, then added a probe to the widget extension. The probe logs That is why this PR does nothing.
I checked the alternatives too. None of them work. An ActivityKit push replaces the whole Proposed designThe phone keeps aggregating. The relay only triggers a re-render and stores nothing.
The relay sees the activity token per request and when refreshes happen. No counts, routes or tags reach it. On the client side this means:
From this PR the census entries with route keys, What I cannot do alone
What do you think? If the design works for you, I split this PR into the parts that stay. Then I start on the client side. |
|
I took a look at this today and there are some challenges in making this work with the Apple ActivityKit restrictions here because I have been trying to keep the server side part of this 100% stateless in addition to the end to end encrypted nature of the push notifications. ActivityKit AFAIK cannot write back to the group container, so maintaining the state over time becomes a problem. This is all easy if I break down and actually store state… If I appreciate that everything widget related doesn’t drain your battery, but it ends up a nightmare every time. |
Follow-up to #423. This PR applies the review note from that PR.
What it does
Agent hook pushes from rootshell-notify now update the Live Activity counts while the app is in the background.
How it works
On background entry, the app writes a per-pane agent census to the app group, next to the frozen publish. The type is
AgentActivityLedgerin RootshellPushKit. The file islive-activity-agents.json.Each entry stores the push-route keys of one pane:
When the notification service extension decrypts an agent push with status
blocked,done, orfailed, it:PushNotificationRouter.resolve.The frozen flag stays set. The extension stamps a new
agentPushUpdatedAt. The widget then shows "Updated 14:05" instead of "Updates paused" and keeps its colors.On the next foreground reconcile, the app deletes the ledger and publishes counts from live detection again.
Design decisions — review these
The PR counts only panes that the app already tracks. A push that matches no ledger entry changes nothing. Agents outside a rootshell pane stay uncounted. The notification itself is not affected. If we counted those agents, the widget would disagree with the sidebar after the app returns.
The hooks report only
blocked,done, andfailed. A push can move a pane into "needs attention", but never back to "working". The ledger encodes this rule.The extension drops ambiguous matches. A count on the wrong pane is worse than a stale count.
The extension waits for the ActivityKit update before it completes. The system can suspend the extension right after completion. The wait has a 3-second cap on a
DispatchGroup. Thusliveactivitiesdcannot hold the notification.The wait applies only to agent pushes with one of the three statuses. It also applies only when a ledger exists. A foreground app does no extra work. The same is true for a user with the Coding Agents toggle off.
Build and test changes
SessionActivityAttributes.swiftis now part of the PushNotificationService target. I added a new exception to the project for this. The widget target already uses the same mechanism.apply, store expiry after 24 h.Storage
PushConfiguration.appGroup. The China build therefore gets its own group.PushSharedStatediscipline: atomic writes, no file coordination. The code ignores failures.