Improve live activity routing and diagnostics#3685
Conversation
- Surface device push/live activity diagnostics in Settings - Add APNs environment routing and foreground Live Activity refresh - Tighten widget deep linking and row rendering for active agents
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.
Bugbot Autofix prepared fixes for all 3 issues found in the latest run.
- ✅ Fixed: Missing database migration columns
- Added the drizzle-kit-generated Postgres migration (migration.sql + snapshot.json) adding bundle_id and aps_environment to relay_mobile_devices, chained onto the previous snapshot.
- ✅ Fixed: Last delivery ignores HTTP status
- The Last Delivery row now treats a non-2xx lastDeliveryStatus as a failure even when lastDeliveryError is null, showing "Delivery failed (status)" instead of "Delivered".
- ✅ Fixed: Diagnostics use global attempt window
- Replaced the global 100-row window with a DISTINCT ON (device_id) query ordered by created_at DESC so each device's true latest delivery attempt is always returned.
Or push these changes by commenting:
@cursor push a97e0b6881
Preview (a97e0b6881)
diff --git a/apps/mobile/src/features/agent-awareness/deviceDiagnostics.test.ts b/apps/mobile/src/features/agent-awareness/deviceDiagnostics.test.ts
--- a/apps/mobile/src/features/agent-awareness/deviceDiagnostics.test.ts
+++ b/apps/mobile/src/features/agent-awareness/deviceDiagnostics.test.ts
@@ -69,6 +69,28 @@
]);
});
+ it("treats a non-2xx delivery status without a reason as a failure", () => {
+ const rows = formatDeviceDiagnosticsRows(
+ makeDevice({
+ bundleId: "com.t3tools.t3code.preview",
+ apsEnvironment: "production",
+ hasPushToken: true,
+ hasPushToStartToken: true,
+ hasLiveActivityToken: true,
+ lastDeliveryAt: "2026-06-05T01:02:59.566Z",
+ lastDeliveryKind: "live_activity_end",
+ lastDeliveryStatus: 400,
+ lastDeliveryError: null,
+ }),
+ );
+
+ expect(rows[4]).toEqual({
+ label: "Last Delivery",
+ value: "Delivery failed (400)",
+ tone: "warn",
+ });
+ });
+
it("reports healthy registrations with a successful delivery", () => {
const rows = formatDeviceDiagnosticsRows(
makeDevice({
diff --git a/apps/mobile/src/features/agent-awareness/deviceDiagnostics.ts b/apps/mobile/src/features/agent-awareness/deviceDiagnostics.ts
--- a/apps/mobile/src/features/agent-awareness/deviceDiagnostics.ts
+++ b/apps/mobile/src/features/agent-awareness/deviceDiagnostics.ts
@@ -73,14 +73,21 @@
});
}
+ // APNs can fail with a non-2xx status without a reason body, so a null
+ // error alone does not mean the delivery succeeded.
+ const deliveryFailed =
+ diagnostics.lastDeliveryError !== null ||
+ (diagnostics.lastDeliveryStatus !== null &&
+ (diagnostics.lastDeliveryStatus < 200 || diagnostics.lastDeliveryStatus >= 300));
+
if (diagnostics.lastDeliveryAt === null) {
rows.push({ label: "Last Delivery", value: "None yet", tone: "muted" });
- } else if (diagnostics.lastDeliveryError !== null) {
+ } else if (deliveryFailed) {
const status =
diagnostics.lastDeliveryStatus === null ? "" : ` (${diagnostics.lastDeliveryStatus})`;
rows.push({
label: "Last Delivery",
- value: `${diagnostics.lastDeliveryError}${status}`,
+ value: `${diagnostics.lastDeliveryError ?? "Delivery failed"}${status}`,
tone: "warn",
});
} else {
diff --git a/infra/relay/migrations/postgres/20260704031500_add_mobile_device_apns_route/migration.sql b/infra/relay/migrations/postgres/20260704031500_add_mobile_device_apns_route/migration.sql
new file mode 100644
--- /dev/null
+++ b/infra/relay/migrations/postgres/20260704031500_add_mobile_device_apns_route/migration.sql
@@ -1,0 +1,3 @@
+ALTER TABLE "relay_mobile_devices" ADD COLUMN "bundle_id" varchar(255);
+--> statement-breakpoint
+ALTER TABLE "relay_mobile_devices" ADD COLUMN "aps_environment" varchar(16);
diff --git a/infra/relay/migrations/postgres/20260704031500_add_mobile_device_apns_route/snapshot.json b/infra/relay/migrations/postgres/20260704031500_add_mobile_device_apns_route/snapshot.json
new file mode 100644
--- /dev/null
+++ b/infra/relay/migrations/postgres/20260704031500_add_mobile_device_apns_route/snapshot.json
@@ -1,0 +1,1479 @@
+{
+ "dialect": "postgres",
+ "id": "e00cc7df-a31e-4b8b-b258-d04e7db7758a",
+ "prevIds": ["385d476b-d4f6-48a3-99e6-0a95af4ee4e4"],
+ "version": "8",
+ "ddl": [
+ {
+ "isRlsEnabled": false,
+ "name": "relay_agent_activity_rows",
+ "entityType": "tables",
+ "schema": "public"
+ },
+ {
+ "isRlsEnabled": false,
+ "name": "relay_delivery_attempts",
+ "entityType": "tables",
+ "schema": "public"
+ },
+ {
+ "isRlsEnabled": false,
+ "name": "relay_dpop_proofs",
+ "entityType": "tables",
+ "schema": "public"
+ },
+ {
+ "isRlsEnabled": false,
+ "name": "relay_environment_credentials",
+ "entityType": "tables",
+ "schema": "public"
+ },
+ {
+ "isRlsEnabled": false,
+ "name": "relay_environment_links",
+ "entityType": "tables",
+ "schema": "public"
+ },
+ {
+ "isRlsEnabled": false,
+ "name": "relay_live_activities",
+ "entityType": "tables",
+ "schema": "public"
+ },
+ {
+ "isRlsEnabled": false,
+ "name": "relay_managed_endpoint_allocations",
+ "entityType": "tables",
+ "schema": "public"
+ },
+ {
+ "isRlsEnabled": false,
+ "name": "relay_mobile_devices",
+ "entityType": "tables",
+ "schema": "public"
+ },
+ {
+ "type": "varchar(191)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "environment_id",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_agent_activity_rows"
+ },
+ {
+ "type": "text",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "environment_public_key",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_agent_activity_rows"
+ },
+ {
+ "type": "varchar(191)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "thread_id",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_agent_activity_rows"
+ },
+ {
+ "type": "jsonb",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "state_json",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_agent_activity_rows"
+ },
+ {
+ "type": "varchar(64)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "updated_at",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_agent_activity_rows"
+ },
+ {
+ "type": "varchar(64)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_agent_activity_rows"
+ },
+ {
+ "type": "varchar(36)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "id",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_delivery_attempts"
+ },
+ {
+ "type": "varchar(64)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_delivery_attempts"
+ },
+ {
+ "type": "varchar(255)",
+ "typeSchema": null,
+ "notNull": false,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "user_id",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_delivery_attempts"
+ },
+ {
+ "type": "varchar(191)",
+ "typeSchema": null,
+ "notNull": false,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "environment_id",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_delivery_attempts"
+ },
+ {
+ "type": "varchar(191)",
+ "typeSchema": null,
+ "notNull": false,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "thread_id",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_delivery_attempts"
+ },
+ {
+ "type": "varchar(255)",
+ "typeSchema": null,
+ "notNull": false,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "device_id",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_delivery_attempts"
+ },
+ {
+ "type": "varchar(64)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "kind",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_delivery_attempts"
+ },
+ {
+ "type": "varchar(64)",
+ "typeSchema": null,
+ "notNull": false,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "source_job_id",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_delivery_attempts"
+ },
+ {
+ "type": "varchar(16)",
+ "typeSchema": null,
+ "notNull": false,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "token_suffix",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_delivery_attempts"
+ },
+ {
+ "type": "integer",
+ "typeSchema": null,
+ "notNull": false,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "apns_status",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_delivery_attempts"
+ },
+ {
+ "type": "text",
+ "typeSchema": null,
+ "notNull": false,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "apns_reason",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_delivery_attempts"
+ },
+ {
+ "type": "varchar(128)",
+ "typeSchema": null,
+ "notNull": false,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "apns_id",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_delivery_attempts"
+ },
+ {
+ "type": "text",
+ "typeSchema": null,
+ "notNull": false,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "transport_error",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_delivery_attempts"
+ },
+ {
+ "type": "varchar(128)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "thumbprint",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_dpop_proofs"
+ },
+ {
+ "type": "varchar(255)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "jti",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_dpop_proofs"
+ },
+ {
+ "type": "integer",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "iat",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_dpop_proofs"
+ },
+ {
+ "type": "varchar(64)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "expires_at",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_dpop_proofs"
+ },
+ {
+ "type": "varchar(64)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_dpop_proofs"
+ },
+ {
+ "type": "varchar(64)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "credential_id",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_credentials"
+ },
+ {
+ "type": "varchar(191)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "environment_id",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_credentials"
+ },
+ {
+ "type": "text",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "environment_public_key",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_credentials"
+ },
+ {
+ "type": "varchar(191)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "credential_hash",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_credentials"
+ },
+ {
+ "type": "varchar(64)",
+ "typeSchema": null,
+ "notNull": false,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "revoked_at",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_credentials"
+ },
+ {
+ "type": "varchar(64)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_credentials"
+ },
+ {
+ "type": "varchar(64)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "updated_at",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_credentials"
+ },
+ {
+ "type": "varchar(191)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "user_id",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_links"
+ },
+ {
+ "type": "varchar(191)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "environment_id",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_links"
+ },
+ {
+ "type": "text",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": "'T3 Environment'",
+ "generated": null,
+ "identity": null,
+ "name": "environment_label",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_links"
+ },
+ {
+ "type": "text",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "environment_public_key",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_links"
+ },
+ {
+ "type": "text",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "endpoint_http_base_url",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_links"
+ },
+ {
+ "type": "text",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "endpoint_ws_base_url",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_links"
+ },
+ {
+ "type": "varchar(32)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "endpoint_provider_kind",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_links"
+ },
+ {
+ "type": "boolean",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": "true",
+ "generated": null,
+ "identity": null,
+ "name": "notifications_enabled",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_links"
+ },
+ {
+ "type": "boolean",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": "true",
+ "generated": null,
+ "identity": null,
+ "name": "live_activities_enabled",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_links"
+ },
+ {
+ "type": "boolean",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": "false",
+ "generated": null,
+ "identity": null,
+ "name": "managed_tunnels_enabled",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_links"
+ },
+ {
+ "type": "varchar(191)",
+ "typeSchema": null,
+ "notNull": false,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "created_by_device_id",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_links"
+ },
+ {
+ "type": "varchar(64)",
+ "typeSchema": null,
+ "notNull": false,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "revoked_at",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_links"
+ },
+ {
+ "type": "varchar(64)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "created_at",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_links"
+ },
+ {
+ "type": "varchar(64)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "updated_at",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_environment_links"
+ },
+ {
+ "type": "varchar(255)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "user_id",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_live_activities"
+ },
+ {
+ "type": "varchar(255)",
+ "typeSchema": null,
+ "notNull": true,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "device_id",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_live_activities"
+ },
+ {
+ "type": "text",
+ "typeSchema": null,
+ "notNull": false,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "activity_push_token",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_live_activities"
+ },
+ {
+ "type": "varchar(64)",
+ "typeSchema": null,
+ "notNull": false,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "remote_start_queued_at",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_live_activities"
+ },
+ {
+ "type": "varchar(64)",
+ "typeSchema": null,
+ "notNull": false,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "remote_started_at",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_live_activities"
+ },
+ {
+ "type": "varchar(64)",
+ "typeSchema": null,
+ "notNull": false,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
+ "name": "ended_at",
+ "entityType": "columns",
+ "schema": "public",
+ "table": "relay_live_activities"
+ },
+ {
+ "type": "jsonb",
+ "typeSchema": null,
+ "notNull": false,
+ "dimensions": 0,
+ "default": null,
+ "generated": null,
+ "identity": null,
... diff truncated: showing 800 of 1681 linesYou can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit f81c2ca. Configure here.
| iosMajorVersion: integer("ios_major_version").notNull(), | ||
| appVersion: varchar("app_version", { length: 64 }), | ||
| bundleId: varchar("bundle_id", { length: 255 }), | ||
| apsEnvironment: varchar("aps_environment", { length: 16 }).$type<"sandbox" | "production">(), |
There was a problem hiding this comment.
Missing database migration columns
High Severity
The Drizzle schema for relay_mobile_devices was updated with bundle_id and aps_environment columns, but no corresponding Postgres migration was added. This causes deployed relays to fail with missing-column errors during device registration and listing.
Reviewed by Cursor Bugbot for commit f81c2ca. Configure here.
| label: "Last Delivery", | ||
| value: timestamp ? `Delivered ${timestamp}` : "Delivered", | ||
| tone: "ok", | ||
| }); |
There was a problem hiding this comment.
Last delivery ignores HTTP status
Medium Severity
The "Last Delivery" status in device diagnostics may incorrectly report failed push deliveries as successful. This happens because the display logic checks only lastDeliveryError for failure, but APNs can return non-2xx statuses without populating apnsReason, leaving lastDeliveryError null. This obscures actual delivery issues.
Reviewed by Cursor Bugbot for commit f81c2ca. Configure here.
| for (const attempt of attemptRows) { | ||
| if (attempt.deviceId && !lastAttemptByDevice.has(attempt.deviceId)) { | ||
| lastAttemptByDevice.set(attempt.deviceId, attempt); | ||
| } |
There was a problem hiding this comment.
Diagnostics use global attempt window
Medium Severity
listForUser loads only the 100 most recent delivery attempts for the entire user, then picks each device’s “last” attempt from that slice. On busy accounts, a device’s latest attempt may fall outside those 100 rows, so Push Delivery can show an older attempt, “None yet”, or another device’s era of failures as this device’s last delivery.
Reviewed by Cursor Bugbot for commit f81c2ca. Configure here.
| platform: varchar("platform", { length: 16 }).notNull().$type<"ios">(), | ||
| iosMajorVersion: integer("ios_major_version").notNull(), | ||
| appVersion: varchar("app_version", { length: 64 }), | ||
| bundleId: varchar("bundle_id", { length: 255 }), |
There was a problem hiding this comment.
🟠 High persistence/schema.ts:27
The new bundle_id and aps_environment columns are added to the relay_mobile_devices schema, but no SQL migration is included to create them. After deploy, the code in Devices.ts immediately inserts and selects these columns, so PostgreSQL rejects every device registration and list query with column "bundle_id" does not exist (and aps_environment likewise). Add the corresponding ALTER TABLE relay_mobile_devices ADD COLUMN ... migration under infra/relay/migrations/ so the columns exist before the new code runs.
Also found in 1 other location(s)
infra/relay/src/agentActivity/LiveActivities.ts:201
The new
relayMobileDevices.bundleId/relayMobileDevices.apsEnvironmentcolumns are referenced bylistTargets(bundle_id,aps_environment) and other relay code, but this PR does not add a matching SQL migration forrelay_mobile_devices. After deploying the code to an existing database, anylistTargetsquery will start failing withcolumn relay_mobile_devices.bundle_id does not exist(and likewise foraps_environment), breaking live-activity target lookup until the schema is manually patched.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @infra/relay/src/persistence/schema.ts around line 27:
The new `bundle_id` and `aps_environment` columns are added to the `relay_mobile_devices` schema, but no SQL migration is included to create them. After deploy, the code in `Devices.ts` immediately inserts and selects these columns, so PostgreSQL rejects every device registration and list query with `column "bundle_id" does not exist` (and `aps_environment` likewise). Add the corresponding `ALTER TABLE relay_mobile_devices ADD COLUMN ...` migration under `infra/relay/migrations/` so the columns exist before the new code runs.
Also found in 1 other location(s):
- infra/relay/src/agentActivity/LiveActivities.ts:201 -- The new `relayMobileDevices.bundleId` / `relayMobileDevices.apsEnvironment` columns are referenced by `listTargets` (`bundle_id`, `aps_environment`) and other relay code, but this PR does not add a matching SQL migration for `relay_mobile_devices`. After deploying the code to an existing database, any `listTargets` query will start failing with `column relay_mobile_devices.bundle_id does not exist` (and likewise for `aps_environment`), breaking live-activity target lookup until the schema is manually patched.
There was a problem hiding this comment.
🟡 Medium
The stale-job check in processSignedJob only compares the token via isCurrentSignedJobToken, so a queued job created before the device registered bundleId/apsEnvironment (or before those values changed) still passes the staleness guard when the token is unchanged. The job is then sent with the stale or missing routing metadata from the signed payload instead of the device's current values, causing APNs to reject the delivery with BadDeviceToken/DeviceTokenNotForTopic even though the registration has already been corrected. Consider comparing bundleId and apsEnvironment against the current target in the staleness check so jobs with outdated routing are treated as stale.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @infra/relay/src/agentActivity/ApnsDeliveries.ts around line 506:
The stale-job check in `processSignedJob` only compares the token via `isCurrentSignedJobToken`, so a queued job created before the device registered `bundleId`/`apsEnvironment` (or before those values changed) still passes the staleness guard when the token is unchanged. The job is then sent with the stale or missing routing metadata from the signed payload instead of the device's current values, causing APNs to reject the delivery with `BadDeviceToken`/`DeviceTokenNotForTopic` even though the registration has already been corrected. Consider comparing `bundleId` and `apsEnvironment` against the current target in the staleness check so jobs with outdated routing are treated as stale.
ApprovabilityVerdict: Needs human review 2 blocking correctness issues found. This PR adds new database columns (bundle_id, aps_environment) without SQL migrations, which would break deployment. Additionally, it introduces significant new runtime behavior including per-device APNs routing, activity expiration TTLs, and foreground reconciliation logic. Multiple unresolved review comments identify substantive issues. You can customize Macroscope's approvability policy. Learn more. |



Summary
Testing
vp checkvp run typecheckvp testNote
Medium Risk
Changes APNs topic/environment routing and delivery suppression logic on the relay plus mobile token lifecycle; mistakes could break pushes for some build variants, though behavior is covered by expanded tests.
Overview
Improves iOS agent Live Activity and push reliability end-to-end: mobile registers per-build APNs routing (
bundleId, sandbox vs production), the relay sends using that routing, and Settings adds a Push Delivery section that surfaces relay-side token and last-delivery diagnostics.Mobile: Device registration includes bundle ID and APS environment; foreground re-registers Live Activity tokens for relay replay/reconciliation; sign-out ends orphaned local activities. The Agent Activity widget gets per-row status colors, deep links to threads needing attention, overflow text, and device-local “Updated” times. Expo widgets enable
frequentUpdatesto reduce iOS update throttling.Relay: Persists routing on devices and threads it through the APNs queue; aggregates drop stale activity rows (TTL: ~2h running, ~24h waiting) so dead environments don’t inflate counts forever; Live Activity stale-after stretches from 2 to 10 minutes; suppressed/throttled Live Activity updates no longer fall back to alert pushes on unchanged “waiting” aggregates.
Reviewed by Cursor Bugbot for commit f81c2ca. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Improve live activity APNs routing and add device delivery diagnostics to settings
bundleIdandapsEnvironmentper device inrelay_mobile_devicesand propagates them through the APNs delivery pipeline so pushes are routed to the correct APNs environment and topic for each device.shouldUpdateLiveActivitygating function that suppresses redundant Live Activity updates and prevents fallback alert pushes when the Live Activity owns the state but no update is due.AppStatelistener, and ends all local live activities on cloud sign-out.📊 Macroscope summarized f81c2ca. 15 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted
🗂️ Filtered Issues
No issues evaluated.