You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Push taps route to the current connection, even when the notification came from another saved server #174
Filing category ①. This is a user-visible defect with a named producer. It becomes reachable once saved connections (#152, PR #173) land.
The defect
hooks/usePushRouting.ts takes a notification's route (routeFromPushData) and opens it on the current connection's Console. It never checks which server sent the notification.
With several saved servers, every server registers the same device token. When a user taps a notification from server B while server A is current, B's route opens on A's Console. The page is wrong, and the record may even exist on A.
reach: named producer, hooks/usePushRouting.ts (route taken without a server check). PR #173 documents it as a Known limit and lists it in its Acceptance notes.
Claim: PM loop round 1
Session: session_01QSeKYnW1Xf6HDSWz1eGAqv
Account: os-justin
Branch: claude/issue-174-push-per-connection
Worktree: mobile-issue-174
Domain: n/a — single-lane repo (repo:mobile)
Seat: repo:mobile#1
File surface: hooks/usePushRouting.ts, lib/push-notifications.ts, app/console.tsx (switch before routing), lib/connections/** (lookup by host only), __tests__/**, docs/HYBRID-SHELL.md, docs/PUSH-NOTIFICATIONS.md, .changeset/174-push-per-connection.md (stop on breach; explain in the report)
Container & model: M, mode:subagent, model: default tier (opus) — dispatch-gates --tier --repo objectstack-ai/mobile: no path-derived mandate, effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: none
Premise: holds. usePushRouting(serverUrl, …) at hooks/usePushRouting.ts:29 routes on the current connection with no server check.
Serial constraints cleared: #157 may touch lib/connections/** (ordinary concurrency). #155 may touch app/console.tsx (excursion bar host): the PR that lands second reconciles.
{
"issue": 174,
"status": "blocked",
"branch": "claude/issue-174-push-per-connection",
"pr": null,
"session": "session_01QSeKYnW1Xf6HDSWz1eGAqv",
"premise_still_valid": false,
"summary": "Stopped on the card's own stop condition: the platform push payload carries no server identity, so no PR. The mobile defect itself is real (hooks/usePushRouting.ts:29 routes every tap on the current serverUrl), but there is nothing in the payload to route by. Measured on objectstack origin/main aca58cdb: no push channel exists (service-messaging/src has email, inbox, sms and http/webhook channels only; channel.ts:7 names APNs/FCM in prose), and packages/spec/src/api/push-delivery.zod.ts is ABSENT (control leg: packages/spec/src/system/email-config.zod.ts present on the same ref). The only push payload contract is in objectstack-ai/objectstack#29218, an open draft PR (head aff751de, spec slice of objectstack-ai/objectstack#29067 / objectstack-ai/objectstack#29041): push-delivery.zod.ts:90-99 PushDeliveryDataSchema = z.object with exactly two keys, notificationId (line 91, required) and route (line 92, optional, PushRouteSchema lines 72-85 = same-server relative path only). No host, origin, server URL or tenant key; ADR-0012 line 426 gives the push renderer output as title, body, data, sound, badge with no identity either. APNs and FCM tell the app nothing about the sender, and every saved server registers the same device token, so the shell cannot derive the sender locally; per the ruling no route-shape heuristic was attempted. The platform side is a new objectstack card for the seat to file (options in open_questions); the cheapest moment is before objectstack-ai/objectstack#29218 merges. Branch was pushed empty as the probe (push succeeded, no 403) and carries no commits; worktree removed.",
"tests": "none run: no code written (stop condition). Evidence reads only: git show origin/main and git cat-file -e on objectstack (shallow checkout; absent-file reading paired with a present control file on the same ref), git show aff751de:packages/spec/src/api/push-delivery.zod.ts, REST reads of objectstack PR 29218 and issues 29041, 29067, 29068.",
"gates": "NOT MEASURED: tsc, eslint, jest, reason: no diff (stop condition, no PR). pnpm install in the worktree exit 0 (it was started backgrounded with an ampersand, a deviation from the foreground rule; it finished before any later step).",
"dogfood_battery": "n/a: no PR",
"mcp_calls": "0",
"api_writes": "1: POST repos/objectstack-ai/mobile/issues/174/comments (this report, via post-stamped.mjs); git push of the empty branch is not a REST write",
"open_questions": [
{
"question": "How should a push name the server that sent it, so the shell can match it against saved connections by host (lib/connections/model.ts normalizes to host[:port])? This is a public contract name in packages/spec, so it is the spec seat's and maintainer's call, filed as an objectstack card by the mobile seat.",
"options": [
"A: the push channel adds a server-origin key to PushDeliveryDataSchema from the server's configured public URL. Cost: one optional key in objectstack-ai/objectstack#29218 plus one line in the objectstack-ai/objectstack#29041 channel; a server reachable under several hostnames or behind a rewriting proxy emits an origin the shell did not save, so the tap is dropped (safe, but the feature silently fails for those deployments).",
"B: the Console sends the origin it was loaded from (window.location.origin, the host the shell saved) in RegisterDeviceRequest, the server stores it on the UserDevice row, and the push channel echoes that row's value into the push data. Cost: one optional member each on RegisterDeviceRequestSchema, UserDeviceSchema and PushDeliveryDataSchema, all in objectstack-ai/objectstack#29218 before merge, plus the Console bridge registration call; matches exactly the host the shell knows, independent of proxy config.",
"C: no platform change; the shell keeps routing on the current connection and documents the limit. Cost: zero; the card's defect stays."
],
"recommendation": "B, because it echoes the exact host the shell saved for that device row, so matching needs no knowledge of the server's public URL, and all three members are widenings that fit inside objectstack-ai/objectstack#29218 before it merges (one PR instead of a second widening later). Then mobile #174 resumes with a host lookup and the #152 switch path."
}
],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: objectstack-ai/objectstack#29041 · noted, not filed: the planned contract (objectstack-ai/objectstack#29218 push-delivery.zod.ts:35-48) says the shell never opens route verbatim and resolves app-relative values like /opportunity/123 through resolveNotificationTarget, while mobile lib/push-notifications.ts routeFromPushData opens route verbatim after safeConsoleRoute and docs/PUSH-NOTIFICATIONS.md documents a url key the planned contract does not carry. Observation only: the contract is unmerged and no producer exists. Dedupe words: push route resolve, actionUrl app-relative, routeFromPushData verbatim, push payload url key"
]
}
The platform decision is filed as objectstack-ai/objectstack#29642 (recommendation B: round-trip the Console's loaded origin through device registration).
State: pm:dispatched → pm:blocked, assignee cleared. Resume from the existing branch once #29642 is ruled and its contract change is installable from a platform release.
Activity
objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
Unblocked:
Blocked-by#152 merged (678fbe9).Claim: PM loop round 1
Session:
session_01QSeKYnW1Xf6HDSWz1eGAqvAccount:
os-justinBranch:
claude/issue-174-push-per-connectionWorktree:
mobile-issue-174Domain:
n/a — single-lane repo (repo:mobile)Seat:
repo:mobile#1File surface:
hooks/usePushRouting.ts,lib/push-notifications.ts,app/console.tsx(switch before routing),lib/connections/**(lookup by host only),__tests__/**,docs/HYBRID-SHELL.md,docs/PUSH-NOTIFICATIONS.md,.changeset/174-push-per-connection.md(stop on breach; explain in the report)Container & model:
M,mode:subagent,model: default tier (opus) — dispatch-gates --tier --repo objectstack-ai/mobile: no path-derived mandate,effort: highClause-②: no
Metadata-protocol: none
Responsibility:
n/a — not a defect cardThread-read: none
Premise: holds.
usePushRouting(serverUrl, …)athooks/usePushRouting.ts:29routes on the current connection with no server check.Serial constraints cleared: #157 may touch
lib/connections/**(ordinary concurrency). #155 may touchapp/console.tsx(excursion bar host): the PR that lands second reconciles.objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
os-dev-report
{ "issue": 174, "status": "blocked", "branch": "claude/issue-174-push-per-connection", "pr": null, "session": "session_01QSeKYnW1Xf6HDSWz1eGAqv", "premise_still_valid": false, "summary": "Stopped on the card's own stop condition: the platform push payload carries no server identity, so no PR. The mobile defect itself is real (hooks/usePushRouting.ts:29 routes every tap on the current serverUrl), but there is nothing in the payload to route by. Measured on objectstack origin/main aca58cdb: no push channel exists (service-messaging/src has email, inbox, sms and http/webhook channels only; channel.ts:7 names APNs/FCM in prose), and packages/spec/src/api/push-delivery.zod.ts is ABSENT (control leg: packages/spec/src/system/email-config.zod.ts present on the same ref). The only push payload contract is in objectstack-ai/objectstack#29218, an open draft PR (head aff751de, spec slice of objectstack-ai/objectstack#29067 / objectstack-ai/objectstack#29041): push-delivery.zod.ts:90-99 PushDeliveryDataSchema = z.object with exactly two keys, notificationId (line 91, required) and route (line 92, optional, PushRouteSchema lines 72-85 = same-server relative path only). No host, origin, server URL or tenant key; ADR-0012 line 426 gives the push renderer output as title, body, data, sound, badge with no identity either. APNs and FCM tell the app nothing about the sender, and every saved server registers the same device token, so the shell cannot derive the sender locally; per the ruling no route-shape heuristic was attempted. The platform side is a new objectstack card for the seat to file (options in open_questions); the cheapest moment is before objectstack-ai/objectstack#29218 merges. Branch was pushed empty as the probe (push succeeded, no 403) and carries no commits; worktree removed.", "tests": "none run: no code written (stop condition). Evidence reads only: git show origin/main and git cat-file -e on objectstack (shallow checkout; absent-file reading paired with a present control file on the same ref), git show aff751de:packages/spec/src/api/push-delivery.zod.ts, REST reads of objectstack PR 29218 and issues 29041, 29067, 29068.", "gates": "NOT MEASURED: tsc, eslint, jest, reason: no diff (stop condition, no PR). pnpm install in the worktree exit 0 (it was started backgrounded with an ampersand, a deviation from the foreground rule; it finished before any later step).", "dogfood_battery": "n/a: no PR", "mcp_calls": "0", "api_writes": "1: POST repos/objectstack-ai/mobile/issues/174/comments (this report, via post-stamped.mjs); git push of the empty branch is not a REST write", "open_questions": [ { "question": "How should a push name the server that sent it, so the shell can match it against saved connections by host (lib/connections/model.ts normalizes to host[:port])? This is a public contract name in packages/spec, so it is the spec seat's and maintainer's call, filed as an objectstack card by the mobile seat.", "options": [ "A: the push channel adds a server-origin key to PushDeliveryDataSchema from the server's configured public URL. Cost: one optional key in objectstack-ai/objectstack#29218 plus one line in the objectstack-ai/objectstack#29041 channel; a server reachable under several hostnames or behind a rewriting proxy emits an origin the shell did not save, so the tap is dropped (safe, but the feature silently fails for those deployments).", "B: the Console sends the origin it was loaded from (window.location.origin, the host the shell saved) in RegisterDeviceRequest, the server stores it on the UserDevice row, and the push channel echoes that row's value into the push data. Cost: one optional member each on RegisterDeviceRequestSchema, UserDeviceSchema and PushDeliveryDataSchema, all in objectstack-ai/objectstack#29218 before merge, plus the Console bridge registration call; matches exactly the host the shell knows, independent of proxy config.", "C: no platform change; the shell keeps routing on the current connection and documents the limit. Cost: zero; the card's defect stays." ], "recommendation": "B, because it echoes the exact host the shell saved for that device row, so matching needs no knowledge of the server's public URL, and all three members are widenings that fit inside objectstack-ai/objectstack#29218 before it merges (one PR instead of a second widening later). Then mobile #174 resumes with a host lookup and the #152 switch path." } ], "security_findings": "none", "out_of_scope_findings": [ "carrier: objectstack-ai/objectstack#29041 · noted, not filed: the planned contract (objectstack-ai/objectstack#29218 push-delivery.zod.ts:35-48) says the shell never opens route verbatim and resolves app-relative values like /opportunity/123 through resolveNotificationTarget, while mobile lib/push-notifications.ts routeFromPushData opens route verbatim after safeConsoleRoute and docs/PUSH-NOTIFICATIONS.md documents a url key the planned contract does not carry. Observation only: the contract is unmerged and no producer exists. Dedupe words: push route resolve, actionUrl app-relative, routeFromPushData verbatim, push payload url key" ] }objectstack-fleet commentedon Nov 2, 2026
repo:mobile#1 · os-justin · session_01QSeKYnW1Xf6HDSWz1eGAqv
Release: claim 6581414167 (session
session_01QSeKYnW1Xf6HDSWz1eGAqv, branchclaude/issue-174-push-per-connection, which has no commits).The premise is falsified, the card's own stop condition. The dev's report is comment 6581547684, re-checked by the seat:
PushDeliveryDataSchema(feat(spec): push channel protocol face — device registration widening, stored device shape, device contract members, push route, APNs/FCM config by credentialRef objectstack#29218 headaff751de,push-delivery.zod.ts:90-99) carries onlynotificationIdandroute.main.The platform decision is filed as objectstack-ai/objectstack#29642 (recommendation B: round-trip the Console's loaded origin through device registration).
State:
pm:dispatched→pm:blocked, assignee cleared. Resume from the existing branch once #29642 is ruled and its contract change is installable from a platform release.