Skip to content

feat(mobile): deliver glanceable snapshot updates - #5620

Merged
iscekic merged 65 commits into
audit-w8b-live-activities-3cf8-s3from
audit-w8b-live-activities-3cf8-s4
Sep 3, 2026
Merged

iscekic merged 65 commits into
audit-w8b-live-activities-3cf8-s3from
audit-w8b-live-activities-3cf8-s4

Conversation

@iscekic

@iscekic iscekic commented Aug 27, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

The iOS app now keeps the Active Agents Live Activity and the Lock Screen and Home Screen widgets current while the app is backgrounded or closed. They show how many agents are running, need input, or are reconnecting, a status line such as "No work in progress", and how long eligible work has run.

The Android Active Agents notification now updates in the background instead of only while the app is open.

If the device cannot show Live Activities, the Agents tab shows a one-time prompt to open Settings and enable them.

Signing out now also removes the device's background-update registration, so the signed-out account stops updating the Live Activity and widgets.


Reviewer Notes

A user_activity_tokens table stores device activity tokens for the glanceable surfaces. It holds the iOS push-to-start, iOS activity, and Android ongoing tokens, keyed by user and an optional organization, with a unique constraint on the token and cascade deletion with the user. Account soft-delete now also deletes these rows so a deleted user stops receiving glanceable deliveries.

Files
  • packages/db/src/schema.ts — adds the user_activity_tokens table, its unique token index, and its user-plus-organization index.
  • packages/db/src/migrations/0233_square_daimon_hellstrom.sql — creates the table, the user foreign key, and both indexes.
  • apps/web/src/lib/user/index.ts — deletes the user's activity-token rows in the soft-delete path.

Two new user mutations register and unregister activity tokens. registerActivityToken upserts on the token so a re-registration replaces the row, and unregisterActivityToken deletes only a row owned by the caller.

Files
  • apps/web/src/routers/user-router.ts — adds registerActivityToken and unregisterActivityToken and imports the new table.

The active-sessions core moves into a reusable server function. The tRPC procedure now delegates to listActiveSessions, which keeps the worker fetch, the enrichment, and the cloud-agent merge, so the snapshot builder can reuse it without a tRPC context.

Files
  • apps/web/src/lib/active-sessions-list.ts — new module holding the fetch, parse, enrich, and cloud-merge logic.
  • apps/web/src/routers/active-sessions-router.ts — the list procedure now calls listActiveSessions and re-exports its types.

A new internal route builds the privacy-minimal glanceable snapshot for a user. It requires the internal secret, re-checks organization membership when an organization is named, and returns the same versioned shape the mobile publisher derives locally. The builder reads only each session's status, so titles, ids, and other raw fields never leave the server.

Files
  • apps/web/src/app/api/internal/glanceable-agents-snapshot/route.ts — the secret-authenticated route with the organization re-check.
  • apps/web/src/lib/glanceable-agents-snapshot-server.ts — builds the snapshot from the active-sessions list through the shared builder.

A new push data variant active_agents_glanceable carries the aggregate snapshot. It has a versioned revision, an opaque scope key, counts, status, and safe timestamps, and never a title or id. A new active-agents Android channel and count-free generic copy route this payload.

Files
  • packages/notifications/src/push-data.ts — adds the variant and the GlanceableLiveActivityContentState type.
  • packages/notifications/src/push-presentation.ts — adds the channel and the routing and generic-copy cases.
  • packages/notifications/src/locales/en.json — adds the generic body string for the glanceable push.

The notifications worker now sends Live Activity APNs pushes and aggregate glanceable deliveries. A token-based APNs client signs a provider JWT and sends start or update pushes, and a pure orchestrator builds the snapshot and pushes it to iOS activity tokens and Expo tokens. After each cloud-agent session push, the worker delivers the fresh snapshot best-effort.

Files
  • services/notifications/src/lib/apns-live-activity.ts — new APNs client with JWT signing, request building, and parallel sending.
  • services/notifications/src/lib/glanceable-delivery.ts — new orchestrator whose IO is injected for tests.
  • services/notifications/src/index.ts — wires the dependencies, reads the credentials, and delivers after the session push.

The worker gains optional configuration for the snapshot and APNs delivery. KILO_WEB_API_BASE_URL names the web origin, and the four APNs values name the signing key and the topic. A missing value skips that part of delivery instead of failing the push.

Files
  • services/notifications/src/bindings.d.ts — declares the new optional bindings.
  • services/notifications/wrangler.jsonc — sets KILO_WEB_API_BASE_URL to the production origin.
  • ENVIRONMENT.md — documents the five new values.

The mobile app applies active_agents_glanceable pushes in the foreground and in a headless background task. A scope-key fence and an updatedAt check discard stale or foreign pushes, and the applied snapshot rebases its revision onto the local sequence and its local account epoch. The foreground handler suppresses these pushes as banners, and taps land on the Agents tab.

Files
  • apps/mobile/src/lib/notifications.ts — adds the apply logic, the background task, and the foreground suppression.
  • apps/mobile/src/lib/notification-path.ts — routes a glanceable tap to the Agents tab.
  • apps/mobile/src/app/_layout.tsx — registers the background handler at module scope.
  • apps/mobile/app.config.ts — enables background remote notifications.
  • apps/mobile/package.json — adds expo-task-manager.

An iOS delivery registrar registers and unregisters the Live Activity and push-to-start tokens. Registration waits for any in-flight logout unregister to settle, and unregistration reports the tokens it attempted so logout can tombstone them.

Files
  • apps/mobile/src/lib/glanceable/delivery-registration.ts — new registrar wiring the two token mutations.
  • apps/mobile/src/lib/glanceable/sink-registry.ts — adds the delivery hook and the token-only context.
  • apps/mobile/src/lib/auth/push-registration-reconciliation.ts — imports the registrar as a side effect.

The iOS Live Activity renders status, counts, and an elapsed timer from the shared content-state. The sink adopts an existing activity after a process restart, guards revisions, marks the surface denied only on a permanent ActivityKit failure, and triggers a one-time Settings prompt from the Agents tab.

Files
  • apps/mobile/src/glanceable-ios/active-agents-live-activity.tsx — the Live Activity layout with inlined English copy.
  • apps/mobile/src/glanceable-ios/ios-sink.ts — start, update, adopt, end, and token registration.
  • apps/mobile/src/glanceable-ios/view-props.ts — builds the surface props and the content-state.
  • apps/mobile/src/lib/glanceable/activity-kit-prompt.ts — the one-time Settings alert.
  • apps/mobile/src/app/(app)/(tabs)/(2_agents)/index.tsx — shows the alert when the tab regains focus.

Logout now awaits activity-token unregistration and tombstones a failed attempt. Reconciliation retries only the recorded tokens, marks the activity part done when it succeeds, and orders registration against the unregister so a new session's tokens are never re-deleted.

Files
  • apps/mobile/src/lib/auth/logout-cleanup.ts — extends the tombstone with activity tokens and awaits unregistration.
  • apps/mobile/src/lib/auth/logout-reconciliation.ts — retries the recorded tokens and marks the activity part done.

Tests: 14 test files updated across the mobile, web, and notifications suites.
Generated: 1 lockfile updated and 2 Drizzle migration metadata files regenerated.


Verification

Manual, on a real Android emulator and an iOS simulator, against the local stack. Two live cloud-agent sessions were seeded (one needs input, one working), so every screenshot below carries real data, not a forced snapshot.

iOS 26.5 simulator:

  • Home Screen systemSmall and systemMedium, Lock Screen Live Activity, and the Dynamic Island all show the same counts.
  • The Dynamic Island shows the mark and one number, coloured by the top-ranked state.
  • Only systemMedium draws the needs-input wait beside its label.
  • Clearing the work switches every surface to "No work in progress" and ends the Live Activity.

Android emulator, kilo_glanceable_api361:

  • Widget at 4x2, 2x2, and 2x1. Happy, stale, empty, expired, signed-out, privacy, and waiting.
  • Live Update in the shade reads "Active agents / 1 Needs input, 1 Working".
  • English and Arabic, including right-to-left mirroring and the widget picker.

Automated: 660 test files and 8928 tests pass. Typecheck, lint, i18n catalogs, and knip are clean.

Visual Changes

iOS — Home Screen widget, both families

The medium and small Active Agents widgets on the iOS Home Screen, each showing 1 Needs input, 1 Working, and 0 Idle

iOS — Dynamic Island

The Dynamic Island showing the Kilo mark and an orange 1 for the waiting agent

iOS — Lock Screen Live Activity

The Lock Screen Live Activity showing the mark, 1 Needs input with a 7 hr 24 min wait, 1 Working, and 0 Idle

Android — widget at 4x2 and 2x1

The 4x2 Android widget showing the mark above 1 Needs input, 1 Working, and 0 Idle

The 2x1 Android widget showing the mark beside 1 Needs input, 1 Working, and 0 Idle

Android — Live Update

The Android notification shade showing Active agents, 1 Needs input, 1 Working, and an Open agents action

Both platforms — no work in progress

The iOS widgets showing No work in progress after the work clears

The Android widget showing No work in progress

Human steps:

  • after merge — set APNS_TEAM_ID, APNS_KEY_ID, APNS_PRIVATE_KEY, and APNS_TOPIC in the notifications Worker. Without them, iOS Live Activity delivery silently skips.

Notes: none.

Stacked PRs — merge bottom to top. Each level shows only its own diff.

Runtime verification (E2E, user advocacy, simplify) runs on the tip PR over every level.
Every level keeps its own checks, its own bot review, and its own threads; each one is answered on its own PR.
Each level is its own deliverable: it builds and passes its own checks alone.
A finding on a level is repaired on that level, then carried upward with stack.sh forward.

  1. audit-w8b-live-activities-3cf8 — feat(mobile): add glanceable agents snapshot contract #5535
  2. audit-w8b-live-activities-3cf8-s2 — feat(mobile): add iOS Live Activity and widgets #5553
  3. audit-w8b-live-activities-3cf8-s3 — feat(mobile): add Android agents widget and live update #5585
  4. audit-w8b-live-activities-3cf8-s4 — feat(mobile): deliver glanceable snapshot updates #5620 ← this PR (tip)

Add activity-token registration and background delivery so eligible work updates the iOS Live Activity, Android ongoing notification, and Home widgets without opening the app. Logout unregisters tokens. Old clients ignore the new payload type.
Comment thread apps/mobile/src/lib/notifications.ts Resolved
@kilo-code-bot

kilo-code-bot Bot commented Aug 27, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

Live Activity start alerts now take caller-resolved, locale-aware copy from iOS Expo token rows; no new high-confidence issues.

Files Reviewed (5 files)
  • services/notifications/src/lib/apns-live-activity.ts
  • services/notifications/src/lib/apns-live-activity.test.ts
  • services/notifications/src/lib/glanceable-delivery.ts
  • services/notifications/src/lib/glanceable-delivery-deps.ts
  • services/notifications/src/lib/glanceable-delivery.test.ts
Previous Review Summaries (19 snapshots, latest commit cc3fb2d)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit cc3fb2d)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The glanceable-delivery test now asserts the concurrent personal/org scope set rather than completion order; no new high-confidence issues.

Files Reviewed (1 file)
  • services/notifications/src/lib/glanceable-delivery.test.ts

Previous review (commit 87f2be0)

Status: No Issues Found | Recommendation: Merge

Executive Summary

APNs start alerts, Live Activity switch token cleanup, and lost-org-only publication latching look correct; no new high-confidence issues.

Files Reviewed (12 files)
  • apps/mobile/src/glanceable-ios/register.ts
  • apps/mobile/src/lib/glanceable/cleanup.test.ts
  • apps/mobile/src/lib/glanceable/cleanup.ts
  • apps/mobile/src/lib/glanceable/delivery-registration.test.ts
  • apps/mobile/src/lib/glanceable/delivery-registration.ts
  • apps/mobile/src/lib/glanceable/live-activity-switch.ts
  • apps/mobile/src/lib/glanceable/mount.tsx
  • apps/mobile/src/lib/glanceable/org-fence.ts
  • apps/mobile/src/lib/glanceable/publisher.ts
  • apps/mobile/src/lib/glanceable/token-mutation-queue.ts
  • services/notifications/src/lib/apns-live-activity.test.ts
  • services/notifications/src/lib/apns-live-activity.ts

Previous review (commit 70b84a1)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Android widget stale snapshots now draw counts without the delayed-warning line, matching the iOS card; no new high-confidence issues.

Files Reviewed (3 files)
  • apps/mobile/src/glanceable-android/active-agents-widget.test.ts
  • apps/mobile/src/glanceable-android/active-agents-widget.tsx
  • apps/mobile/src/glanceable-android/widget-props.ts

Previous review (commit 97c7ead)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Session-ingest tokens now use generateBoundedInternalServiceToken with SESSION_INGEST_AUDIENCE and a 1-hour TTL, matching other Session Ingest callsites; no new high-confidence issues.

Files Reviewed (2 files)
  • apps/web/src/lib/active-sessions-list.ts
  • apps/web/src/routers/active-sessions-router.ts

Previous review (commit a7e6b31)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Android widget iOS-card layout, headless language apply, lazy widget-task load, and work-runtime alignment look correct; no new high-confidence issues in the incremental diff.

Files Reviewed (14 files)
  • apps/mobile/index.js
  • apps/mobile/plugins/withActiveAgentsAndroidWidget.js
  • apps/mobile/src/glanceable-android/active-agents-widget.test.ts
  • apps/mobile/src/glanceable-android/active-agents-widget.tsx
  • apps/mobile/src/glanceable-android/android-sink.test.ts
  • apps/mobile/src/glanceable-android/android-sink.ts
  • apps/mobile/src/glanceable-android/count-format.ts
  • apps/mobile/src/glanceable-android/register.test-helpers.ts
  • apps/mobile/src/glanceable-android/register.test.ts
  • apps/mobile/src/glanceable-android/register.ts
  • apps/mobile/src/glanceable-android/widget-props.test.ts
  • apps/mobile/src/glanceable-android/widget-props.ts
  • apps/mobile/src/lib/hooks/secure-store-preference.ts
  • apps/mobile/src/lib/hooks/use-language-preference.ts

Previous review (commit de0d279)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Android widget cell-size layout, headless index.js registration, and Live Update compile changes look correct; no new high-confidence issues in the incremental diff.

Files Reviewed (9 files)
  • apps/mobile/index.js
  • apps/mobile/package.json
  • apps/mobile/plugins/withAndroidWidgetLocalizations.js
  • apps/mobile/modules/active-agents-live-update/android/src/main/java/com/kilocode/activeagentsliveupdate/ActiveAgentsLiveUpdateModule.kt
  • apps/mobile/src/glanceable-android/active-agents-widget.test.ts
  • apps/mobile/src/glanceable-android/active-agents-widget.tsx
  • apps/mobile/src/glanceable-android/android-sink.ts
  • apps/mobile/src/glanceable-android/register.test.ts
  • apps/mobile/src/glanceable-android/register.ts

Previous review (commit 007225c)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Android glanceable surfaces now match iOS layout, Live Update settings, and localized widget-picker copy; no new high-confidence issues in the incremental diff.

Files Reviewed (101 files)
  • apps/mobile/app.config.ts
  • apps/mobile/plugins/withActiveAgentsAndroidWidget.js
  • apps/mobile/plugins/withAndroidWidgetLocalizations.js
  • apps/mobile/src/components/notifications-screen.tsx
  • apps/mobile/src/glanceable-android/active-agents-widget.test.ts
  • apps/mobile/src/glanceable-android/active-agents-widget.tsx
  • apps/mobile/src/glanceable-android/android-sink.test.ts
  • apps/mobile/src/glanceable-android/android-sink.ts
  • apps/mobile/src/glanceable-android/count-format.ts
  • apps/mobile/src/glanceable-android/register.test.ts
  • apps/mobile/src/glanceable-android/register.ts
  • apps/mobile/src/glanceable-android/widget-config.json
  • apps/mobile/src/glanceable-android/widget-props.test.ts
  • apps/mobile/src/glanceable-android/widget-props.ts
  • apps/mobile/src/i18n/locales/*.json

Previous review (commit 8d324e1)

Status: 1 Issue Found | Recommendation: Address before merge

Executive Summary

Headless glanceable pushes can restart the Live Activity because the in-app switch never hydrates from SecureStore outside the React auth tree.

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/mobile/src/glanceable-ios/ios-sink.ts 302 Headless starts ignore a stored off switch
Files Reviewed (106 files)
  • apps/mobile/src/app/(app)/(tabs)/(2_agents)/index.mounted.test.tsx
  • apps/mobile/src/app/(app)/(tabs)/(2_agents)/index.tsx
  • apps/mobile/src/components/notifications-screen.mounted.test.tsx
  • apps/mobile/src/components/notifications-screen.tsx
  • apps/mobile/src/components/preferences-screen.mounted.test.tsx
  • apps/mobile/src/glanceable-ios/ios-sink.test.ts
  • apps/mobile/src/glanceable-ios/ios-sink.ts - 1 issue
  • apps/mobile/src/glanceable-ios/register.ts
  • apps/mobile/src/glanceable-ios/system-switch.ts
  • apps/mobile/src/lib/auth/auth-context.test.tsx
  • apps/mobile/src/lib/auth/auth-context.tsx
  • apps/mobile/src/lib/auth/credentials.test.ts
  • apps/mobile/src/lib/glanceable/activity-kit-prompt.ts
  • apps/mobile/src/lib/glanceable/live-activity-switch.test.ts
  • apps/mobile/src/lib/glanceable/live-activity-switch.ts
  • apps/mobile/src/lib/hooks/use-live-activity-preference.ts
  • apps/mobile/src/lib/storage-keys.ts
  • patches/expo-widgets@57.0.11.patch
  • pnpm-lock.yaml
  • apps/mobile/src/i18n/locales/*.json

Fix these issues in Kilo Cloud

Previous review (commit 590a349)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Widget and Live Activity layouts now map baked locale digits onto raw counts so non-Latin numbering matches SwiftUI waits; English still skips the table.

Files Reviewed (4 files)
  • apps/mobile/src/glanceable-ios/active-agents-live-activity.tsx
  • apps/mobile/src/glanceable-ios/active-agents-widget.tsx
  • apps/mobile/src/glanceable-ios/layout-copy.test.ts
  • apps/mobile/src/glanceable-ios/layout-copy.ts

Previous review (commit a22fc31)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Session-ingest glanceable tests now expect idle/needsInput counts (retry → needs-input, idle-only → happy) and match the snapshot builder.

Files Reviewed (2 files)
  • services/session-ingest/src/dos/UserConnectionDO.test.ts
  • services/session-ingest/src/ingest/metadata.test.ts

Previous review (commit 6c998ce)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Incremental widget-gallery localization, privacy-copy, locale-underscore, and Hermes Intl.Locale polyfill changes look correct; previous needsInputSince finding is not in this diff and is not reproduced.

Files Reviewed (99 files)
  • apps/mobile/app.config.ts
  • apps/mobile/plugins/widget-gallery-copy.json
  • apps/mobile/plugins/withWidgetLocalizations.js
  • apps/mobile/src/glanceable-android/android-sink.test.ts
  • apps/mobile/src/glanceable-android/register.test.ts
  • apps/mobile/src/glanceable-android/widget-props.test.ts
  • apps/mobile/src/glanceable-ios/ios-sink.test.ts
  • apps/mobile/src/glanceable-ios/layout-copy.test.ts
  • apps/mobile/src/glanceable-ios/layout-copy.ts
  • apps/mobile/src/lib/glanceable/presentation.test.ts
  • apps/mobile/src/lib/intl-cache.test.ts
  • apps/mobile/src/lib/intl-cache.ts
  • apps/mobile/src/i18n/locales/*.json

Previous review (commit b526537)

Status: 1 Issue Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 0
Issue Details (click to expand)

WARNING

File Line Issue
apps/mobile/src/lib/glanceable/publisher.ts 105 In-app needsInputSince clears after the first live-cache merge
Files Reviewed (48 files)
  • apps/mobile/app.config.ts
  • apps/mobile/knip.json
  • apps/mobile/package.json
  • apps/mobile/plugins/withWidgetLocalizations.js
  • apps/mobile/src/app/(app)/(tabs)/(2_agents)/index.tsx
  • apps/mobile/src/components/app-unlock-screen.test-helpers.tsx
  • apps/mobile/src/components/preferences-screen.tsx
  • apps/mobile/src/glanceable-android/active-agents-widget.test.ts
  • apps/mobile/src/glanceable-android/register.test.ts
  • apps/mobile/src/glanceable-android/widget-props.test.ts
  • apps/mobile/src/glanceable-android/widget-props.ts
  • apps/mobile/src/glanceable-ios/active-agents-live-activity.tsx
  • apps/mobile/src/glanceable-ios/active-agents-widget.tsx
  • apps/mobile/src/glanceable-ios/ios-sink.native.test.ts
  • apps/mobile/src/glanceable-ios/ios-sink.test.ts
  • apps/mobile/src/glanceable-ios/ios-sink.ts
  • apps/mobile/src/glanceable-ios/layout-copy.test.ts
  • apps/mobile/src/glanceable-ios/layout-copy.ts
  • apps/mobile/src/glanceable-ios/register.ts
  • apps/mobile/src/glanceable-ios/view-props.ts
  • apps/mobile/src/lib/auth/auth-context.test.tsx
  • apps/mobile/src/lib/auth/auth-context.tsx
  • apps/mobile/src/lib/glanceable/activity-kit-prompt.ts
  • apps/mobile/src/lib/glanceable/cleanup.test.ts
  • apps/mobile/src/lib/glanceable/cleanup.ts
  • apps/mobile/src/lib/glanceable/enabled.test.ts
  • apps/mobile/src/lib/glanceable/enabled.ts
  • apps/mobile/src/lib/glanceable/mount.tsx
  • apps/mobile/src/lib/glanceable/presentation.ts
  • apps/mobile/src/lib/glanceable/publisher.test.ts
  • apps/mobile/src/lib/glanceable/publisher.ts - 1 issue
  • apps/mobile/src/lib/hooks/use-glanceable-preference.ts
  • apps/mobile/src/lib/notification-path.test.ts
  • apps/mobile/src/lib/notifications.test.ts
  • apps/mobile/src/lib/notifications.ts
  • apps/mobile/src/lib/storage-keys.ts
  • apps/web/src/lib/active-sessions-list.ts
  • apps/web/src/lib/glanceable-agents-snapshot-server.test.ts
  • packages/app-shared/src/glanceable-agents-snapshot.test.ts
  • packages/app-shared/src/glanceable-agents-snapshot.ts
  • packages/notifications/src/push-data.ts
  • packages/notifications/src/push-presentation.test.ts
  • services/cloud-agent-next/src/telemetry/report-consumer.glanceable.test.ts
  • services/notifications/src/lib/glanceable-delivery.test.ts
  • services/notifications/src/lib/glanceable-delivery.ts
  • services/notifications/src/lib/glanceable-refresh.ts
  • apps/mobile/src/i18n/locales/*.json
  • apps/mobile/src/lib/auth/auth-context.tsx

Fix these issues in Kilo Cloud

Previous review (commit d94ad7b)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Glanceable surfaces now rank needs-input, working, and idle from a shared snapshot; widget writes omit nulls, and the reconnecting → idle rename is consistent across mobile, APNs content-state, and delivery.

Files Reviewed (126 files)
  • apps/mobile/app.config.ts
  • apps/mobile/assets/images/logo-widget.png
  • apps/mobile/src/glanceable-android/active-agents-widget.test.ts
  • apps/mobile/src/glanceable-android/android-sink.test.ts
  • apps/mobile/src/glanceable-android/register.test.ts
  • apps/mobile/src/glanceable-android/widget-props.test.ts
  • apps/mobile/src/glanceable-android/widget-props.ts
  • apps/mobile/src/glanceable-ios/active-agents-live-activity.tsx
  • apps/mobile/src/glanceable-ios/active-agents-widget.tsx
  • apps/mobile/src/glanceable-ios/ios-sink.native.test.ts
  • apps/mobile/src/glanceable-ios/ios-sink.test.ts
  • apps/mobile/src/glanceable-ios/ios-sink.ts
  • apps/mobile/src/glanceable-ios/register.ts
  • apps/mobile/src/glanceable-ios/view-props.ts
  • apps/mobile/src/glanceable-ios/widget-logo.test.ts
  • apps/mobile/src/glanceable-ios/widget-logo.ts
  • apps/mobile/src/i18n/locales/af.json
  • apps/mobile/src/i18n/locales/am.json
  • apps/mobile/src/i18n/locales/ar.json
  • apps/mobile/src/i18n/locales/az.json
  • apps/mobile/src/i18n/locales/be.json
  • apps/mobile/src/i18n/locales/bg.json
  • apps/mobile/src/i18n/locales/bn.json
  • apps/mobile/src/i18n/locales/bs.json
  • apps/mobile/src/i18n/locales/ca.json
  • apps/mobile/src/i18n/locales/ckb.json
  • apps/mobile/src/i18n/locales/cs.json
  • apps/mobile/src/i18n/locales/cy.json
  • apps/mobile/src/i18n/locales/da.json
  • apps/mobile/src/i18n/locales/de.json
  • apps/mobile/src/i18n/locales/el.json
  • apps/mobile/src/i18n/locales/en.json
  • apps/mobile/src/i18n/locales/es.json
  • apps/mobile/src/i18n/locales/et.json
  • apps/mobile/src/i18n/locales/eu.json
  • apps/mobile/src/i18n/locales/fa.json
  • apps/mobile/src/i18n/locales/fi.json
  • apps/mobile/src/i18n/locales/fil.json
  • apps/mobile/src/i18n/locales/fr.json
  • apps/mobile/src/i18n/locales/ga.json
  • apps/mobile/src/i18n/locales/gl.json
  • apps/mobile/src/i18n/locales/gu.json
  • apps/mobile/src/i18n/locales/ha.json
  • apps/mobile/src/i18n/locales/he.json
  • apps/mobile/src/i18n/locales/hi.json
  • apps/mobile/src/i18n/locales/hr.json
  • apps/mobile/src/i18n/locales/ht.json
  • apps/mobile/src/i18n/locales/hu.json
  • apps/mobile/src/i18n/locales/hy.json
  • apps/mobile/src/i18n/locales/id.json
  • apps/mobile/src/i18n/locales/ig.json
  • apps/mobile/src/i18n/locales/is.json
  • apps/mobile/src/i18n/locales/it.json
  • apps/mobile/src/i18n/locales/ja.json
  • apps/mobile/src/i18n/locales/ka.json
  • apps/mobile/src/i18n/locales/kk.json
  • apps/mobile/src/i18n/locales/km.json
  • apps/mobile/src/i18n/locales/kn.json
  • apps/mobile/src/i18n/locales/ko.json
  • apps/mobile/src/i18n/locales/lo.json
  • apps/mobile/src/i18n/locales/lt.json
  • apps/mobile/src/i18n/locales/lv.json
  • apps/mobile/src/i18n/locales/mg.json
  • apps/mobile/src/i18n/locales/mi.json
  • apps/mobile/src/i18n/locales/mk.json
  • apps/mobile/src/i18n/locales/ml.json
  • apps/mobile/src/i18n/locales/mn.json
  • apps/mobile/src/i18n/locales/mr.json
  • apps/mobile/src/i18n/locales/ms.json
  • apps/mobile/src/i18n/locales/mt.json
  • apps/mobile/src/i18n/locales/my.json
  • apps/mobile/src/i18n/locales/nb.json
  • apps/mobile/src/i18n/locales/ne.json
  • apps/mobile/src/i18n/locales/nl.json
  • apps/mobile/src/i18n/locales/om.json
  • apps/mobile/src/i18n/locales/or.json
  • apps/mobile/src/i18n/locales/pa.json
  • apps/mobile/src/i18n/locales/pl.json
  • apps/mobile/src/i18n/locales/ps.json
  • apps/mobile/src/i18n/locales/pt-BR.json
  • apps/mobile/src/i18n/locales/pt.json
  • apps/mobile/src/i18n/locales/ro.json
  • apps/mobile/src/i18n/locales/ru.json
  • apps/mobile/src/i18n/locales/si.json
  • apps/mobile/src/i18n/locales/sk.json
  • apps/mobile/src/i18n/locales/sl.json
  • apps/mobile/src/i18n/locales/so.json
  • apps/mobile/src/i18n/locales/sq.json
  • apps/mobile/src/i18n/locales/sr.json
  • apps/mobile/src/i18n/locales/sv.json
  • apps/mobile/src/i18n/locales/sw.json
  • apps/mobile/src/i18n/locales/ta.json
  • apps/mobile/src/i18n/locales/te.json
  • apps/mobile/src/i18n/locales/th.json
  • apps/mobile/src/i18n/locales/tr.json
  • apps/mobile/src/i18n/locales/uk.json
  • apps/mobile/src/i18n/locales/ur.json
  • apps/mobile/src/i18n/locales/uz.json
  • apps/mobile/src/i18n/locales/vi.json
  • apps/mobile/src/i18n/locales/yo.json
  • apps/mobile/src/i18n/locales/zh-Hans.json
  • apps/mobile/src/i18n/locales/zh-Hant.json
  • apps/mobile/src/i18n/locales/zu.json
  • apps/mobile/src/lib/glanceable/activity-kit-prompt.test.ts
  • apps/mobile/src/lib/glanceable/activity-kit-prompt.ts
  • apps/mobile/src/lib/glanceable/cleanup.test.ts
  • apps/mobile/src/lib/glanceable/cleanup.ts
  • apps/mobile/src/lib/glanceable/presentation.test.ts
  • apps/mobile/src/lib/glanceable/presentation.ts
  • apps/mobile/src/lib/glanceable/publisher.test.ts
  • apps/mobile/src/lib/glanceable/publisher.ts
  • apps/mobile/src/lib/glanceable/sink-registry.ts
  • apps/mobile/src/lib/notification-path.test.ts
  • apps/mobile/src/lib/notifications.test.ts
  • dev/local/mobile-open-routes.ts
  • dev/local/mobile-open.test.ts
  • dev/local/mobile-open.ts
  • package.json
  • packages/app-shared/src/glanceable-agents-snapshot.test.ts
  • packages/app-shared/src/glanceable-agents-snapshot.ts
  • packages/notifications/src/push-data.ts
  • packages/notifications/src/push-presentation.test.ts
  • services/cloud-agent-next/src/telemetry/report-consumer.glanceable.test.ts
  • services/notifications/src/lib/glanceable-delivery.test.ts
  • services/notifications/src/lib/glanceable-delivery.ts
  • services/notifications/src/lib/glanceable-refresh.ts

Previous review (commit 6f9b484)

Status: No Issues Found | Recommendation: Merge

Executive Summary

APNs team ID, key ID, and private-key Secrets Store binding are now declared on the notifications worker; remaining changes are docs, generated types, and import formatting.

Files Reviewed (5 files)
  • ENVIRONMENT.md
  • apps/mobile/src/lib/hooks/use-glanceable-preference.ts
  • services/notifications/worker-configuration.d.ts
  • services/notifications/wrangler.jsonc
  • services/session-ingest/src/notifications-bindings.d.ts

Previous review (commit 33674ef)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Active Agents master switch blanks surfaces and drops headless snapshots when off; APNs topic is declared in worker vars.

Files Reviewed (21 files)
  • ENVIRONMENT.md
  • apps/mobile/src/app/(app)/(tabs)/(2_agents)/index.mounted.test.tsx
  • apps/mobile/src/app/(app)/(tabs)/(2_agents)/index.tsx
  • apps/mobile/src/components/app-unlock-screen.test-helpers.tsx
  • apps/mobile/src/components/preferences-screen.mounted.test.tsx
  • apps/mobile/src/components/preferences-screen.tsx
  • apps/mobile/src/lib/auth/auth-context.test.tsx
  • apps/mobile/src/lib/auth/auth-context.tsx
  • apps/mobile/src/lib/auth/credentials.test.ts
  • apps/mobile/src/lib/glanceable/activity-kit-prompt.ts
  • apps/mobile/src/lib/glanceable/enabled.test.ts
  • apps/mobile/src/lib/glanceable/enabled.ts
  • apps/mobile/src/lib/glanceable/mount.tsx
  • apps/mobile/src/lib/hooks/use-glanceable-preference.ts
  • apps/mobile/src/lib/notifications.test.ts
  • apps/mobile/src/lib/notifications.ts
  • apps/mobile/src/lib/storage-keys.ts
  • services/notifications/src/lib/glanceable-delivery.test.ts
  • services/notifications/wrangler.jsonc
  • services/session-ingest/src/dos/UserConnectionDO.test.ts
  • services/session-ingest/src/notifications-bindings.d.ts

Previous review (commit 421a090)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Credential-shaped glanceable-refresh error fixture was replaced with a generic string; the test still asserts queue ack and logs only the session id.

Files Reviewed (1 file)
  • services/cloud-agent-next/src/telemetry/report-consumer.glanceable.test.ts

Previous review (commit cee6acb)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Terminal Live Activity ends now retire APNs targets under a generation fence before fresh work can start, with failed refresh isolated from queue ack.

Files Reviewed (6 files)
  • services/notifications/src/lib/apns-live-activity.ts
  • services/notifications/src/lib/apns-live-activity.test.ts
  • services/notifications/src/lib/glanceable-delivery-deps.ts
  • services/notifications/src/lib/glanceable-delivery.test.ts
  • services/notifications/src/lib/glanceable-delivery.ts
  • services/notifications/src/lib/glanceable-refresh.ts

Previous review (commit 35cf697)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Committed Cloud Agent run reports now refresh glanceable counts after the report transaction, with failed refresh isolated so the queue still acks.

Files Reviewed (3 files)
  • services/cloud-agent-next/src/notifications-binding.ts
  • services/cloud-agent-next/src/telemetry/report-consumer.glanceable.test.ts
  • services/cloud-agent-next/src/telemetry/report-consumer.ts

Previous review (commit 7428272)

Status: No Issues Found | Recommendation: Merge

Executive Summary

Generation-fenced glanceable refresh now runs from session metadata, heartbeat, and disconnect transitions, with APNs timestamp ordering and Expo supersession checks covering the concurrent-delivery races.

Files Reviewed (18 files)
  • packages/notifications/src/rpc-schemas.test.ts
  • packages/notifications/src/rpc-schemas.ts
  • `servi

[Snapshot truncated.]

Additional previous summary content was truncated to keep this comment within platform limits.


Reviewed by grok-4.6 · Input: 133.9K · Output: 6.9K · Cached: 360.7K

Review guidance: REVIEW.md from base branch audit-w8b-live-activities-3cf8-s3

iscekic added 19 commits August 27, 2026 21:57
…activities-3cf8-s4

# Conflicts:
#	apps/mobile/src/lib/auth/logout-cleanup.ts
#	apps/mobile/src/lib/notifications.test.ts
#	apps/mobile/src/lib/notifications.ts
#	packages/db/src/migrations/meta/0233_snapshot.json
#	packages/db/src/migrations/meta/_journal.json
The Android widget drew three identical grey rows of text. It carried none of
the state vocabulary the iOS surfaces settled on, no mark, English-only picker
copy, and no way to turn the Live Update off inside the app.

Each row now leads with its state: filled orange for needs-input, filled green
for working, an outline ring for idle. The shapes differ as well as the colors,
so the three states stay apart for a user who cannot tell orange from green.
The colors come from the app's own palette rather than a widget-local one — a
Home Screen card that does not match the app it opens reads as a different
product — and the Kilo mark sits beside them. Three sizes: a narrow cell shows
the ranked count, a short one runs the states in a row, a tall one stacks them
under the mark. The default placement is now 4x2 so the layout that shows all
three states is the one a user gets.

Counts are drawn in the language's own digits. Unlike the iOS widget extension
these surfaces render in the app's own runtime, so `Intl` is already there and
the formatter is injected rather than baked into a layout.

The widget picker is translated into all 87 languages. The library already
wraps the description in a string resource and writes the label straight into
the receiver, so the label becomes a resource reference and a new plugin writes
both keys into one `values-b+<tag>` folder per language. The copy comes from
widget-gallery-copy.json, the file the iOS gallery already reads, so the two
pickers cannot drift.

The notifications screen's Live Activity row is now on both platforms, named
the way each OS names the surface: a Live Activity on iOS, a Live Update on
Android. Android reads the notification permission as its system state and
leaves the Open Settings button to the master gate directly below, which
enables the same permission. Turning the switch off ends the Live Update
already in the shade. The widget is deliberately not gated: placing one is the
opt-in and removing it is the opt-out.
The widget library writes it as translatable="false", which aapt2 reads as a
promise that no values-<tag> override exists. The 86 the localization plugin
writes are exactly that.
expo-modules-core's AppContext exposes only the React context, so
appContext.applicationContext never resolved and the module failed to compile.
Every entry point here runs from a JS call, so a lost React context means the
module cannot work at all.

The Android native build had never run: the workflow gate skipped it on every
dispatch because an artifact for the unchanged native hash already existed.
A placed widget reports its cell in dp, and the two bucket thresholds were
too tight: a 2x1 cell reported about 150-190 dp and took the three-state row,
which clipped the last state, and a 4x1 cell reported up to about 110 dp and
took the stacked layout, which clipped the last row. Move the splits between
the real buckets and let a short row keep the label only on the ranked state.

Also register the widget task handler from the app entry. Android redraws a
placed widget from a headless JS task with no Activity, so an expo-router
route never evaluates and the handler was never registered.
A widget redraw runs as a headless JS task with no Activity, so the app root
that applies the language never mounts and the placed card rendered English
whatever the user picked. Resolve the stored preference in the widget task
itself, which needs an awaitable read on the preference store.

Mirror the layout for a right-to-left language too. The widget library's flex
engine has no reading direction, so each row reverses its own children and
each column flips its alignment.
expo-widgets pulls androidx.glance, which depends on work-runtime-ktx 2.7.1,
while react-native-android-widget depends on work-runtime 2.8.1. Version 2.8.0
folded the ktx classes into the main artifact, so the two together fail
:app:checkDebugDuplicateClasses. Pin both to 2.8.1, where ktx is an empty shim.
The app entry registered the headless widget task by importing the whole
Android glanceable slice, which starts i18n and SecureStore before
expo-router sets the app up. Register a thin handler instead and require the
slice from inside it, so the entry touches one module.
Drop the "Open agents" line: the whole card already opens the agents
screen, so the words only took the space the counts needed.

Move the stale warning under the mark, above the rows. A fourth line
below three counts read as a fourth state.

Draw every state, zeros included, so the rows hold still as work moves
between them.

Stack the rows whenever the cell is tall enough, however narrow it is.
Beside the mark a two-cell-wide card truncated its labels.
…activities-3cf8-s4

# Conflicts:
#	apps/web/src/routers/active-sessions-router.ts
The iOS card draws its counts and no warning when the data is stale, so
the Android card now does the same. The line cost the rows their width
and read as a fourth state. The ongoing notification still says the
counts are delayed.
Apple rejects a Live Activity start payload that carries no alert. Only
Apple Watch renders its title and body; the phone shows the activity.

Claude-Session: https://claude.ai/code/session_01MHf6WEQu5qVG8yYN7g2G3Y
Turning Live Activities off ended the activity but kept the push-to-start
subscription, so a remote start still reopened the surface. The switch now
retires that token and blocks every re-registration of it while it is off.

The FIFO token-mutation queue moves to its own module. It shares no state
with the rest of the file, which sat on the max-lines limit. No behavior
change.

Claude-Session: https://claude.ai/code/session_01MHf6WEQu5qVG8yYN7g2G3Y
143 Gradle and Kotlin build artifacts entered the tree with the Live Update
module fix. `apps/mobile/.gitignore` already ignores the directory.

Claude-Session: https://claude.ai/code/session_01MHf6WEQu5qVG8yYN7g2G3Y
`refreshGlanceableSessions` fans the personal and organization scopes out
with `Promise.allSettled`, so the order the snapshot route observes them in
is scheduling-dependent. The exact-array assertion failed on CI.

Claude-Session: https://claude.ai/code/session_01MHf6WEQu5qVG8yYN7g2G3Y
The push-to-start alert was hardcoded English. `generic.title` and
`generic.body.activeAgentsGlanceable` already exist in all 87 push
catalogs, so no new copy is needed.

`user_activity_tokens` carries no locale, but the same delivery already
reads the iOS Expo rows, which do. Read them first and resolve the alert
from the first row that has one; an absent locale falls back to English.
`apns-live-activity` takes the resolved alert and stays a pure transport.

Claude-Session: https://claude.ai/code/session_01MHf6WEQu5qVG8yYN7g2G3Y
@iscekic
iscekic merged commit 0897edd into main Sep 3, 2026
73 checks passed
@iscekic
iscekic deleted the audit-w8b-live-activities-3cf8-s4 branch September 3, 2026 13:19
lambertjosh added a commit to Kilo-Org/kilocode that referenced this pull request Sep 14, 2026
Account for #14101 and #13951 after the original sync. Verify notification channels against #13771 and widget layouts against Kilo-Org/cloud#5620. Clarify partial change summaries from cloud#5810 and remove implementation-heavy prose.
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.

2 participants