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
[bug] Deploy notification doesn't deep-link — the target is cross-origin, so client.navigate silently fails and only focuses the tab #338
I got this notification, but when I click on it it doesn't deep link: "fws/platform — failure. Open the run to see why."
Cause: the URL is cross-origin, and client.navigate cannot go there
lib/deploy-watch.ts:64 sets the notification's click target to the GitHub Actions run URL:
body: ok ? `${repoName} is live.` : `${repoName} — ${run.conclusion||"failed"}. Open the run to see why.`,url: run.url,// https://github.com/<owner>/<repo>/actions/runs/<id>
store/sw.js:51:
consttarget=(event.notification.data&&event.notification.data.url)||"/console/";for(constclientofclientList){if(client.url.includes("/console")&&"focus"inclient){try{awaitclient.navigate(target);}catch(_e){/* navigation may be blocked cross-origin; focus anyway */}returnclient.focus();// ← happens either way}}if(self.clients.openWindow)returnself.clients.openWindow(target);
WindowClient.navigate() is same-origin only by spec. Navigating a proagentstore.online
tab to github.com rejects, the catch swallows it, and focus() runs regardless. With a
console tab open, clicking the notification just brings that tab forward and nothing else
happens.
The comment shows the failure was anticipated — but "focus anyway" is not a fallback, it is a
no-op that looks identical to a broken notification.
It also makes the bug intermittent: with no console tab open, openWindow(target) is reached
and cross-origin is allowed there, so the GitHub page opens. Same click, different outcome,
depending on whether the app is already open — which is precisely when a user is most likely to
click it.
And it links to the wrong place anyway
"Open the run" should open the run in the product. RunDetail exists — App.tsx:52, instances/:id/tasks/:taskId — and is where the objective, steps, outcome and
timings live. The notification instead sends the user to GitHub Actions, which shows the CI job but
not the agent run that caused it.
Fix
Send a same-origin deep link — the console run route — so client.navigate succeeds and an
open tab actually moves. Keep the GitHub URL as a link inside that page, where it belongs.
Make the fallback real. If navigate throws, fall through to openWindow(target) instead of
focusing a tab that did not move. As written, the only branch that can handle a cross-origin
target is the one taken when the app is closed.
While there: notificationclick matches on client.url.includes("/console"), so a user sitting
on /usage or /preferences is matched and silently navigated elsewhere; and any non-console
tab is ignored entirely. Worth confirming that is intended.
Verification
Clicking the notification with the console open lands on the run page — not just a focused tab.
Clicking with the console closed opens the same page.
The GitHub Actions link is reachable from the run page in one click.
A notification for an instance the user no longer owns degrades to /console/ rather than a 404.
Ordering: there is no per-deploy page in the product, and deferring the notification until one existed would not help — the only per-run artifact IS the GitHub Actions run, which is exactly the thing that cannot be navigated to. So it links the stable parent that already exists when the sweep runs: the repo. deployDeepLink(instanceId, repoId) → /console/instances/:id/coding?builds=<repoId>, which opens the Coding surface on Builds showing that repo's run history — with "View run" out to GitHub one click away, where the CI log belongs. The repo id is already in hand in runDeployWatch (the row it is sweeping), so nothing about the first-sight watermark rule changes.
decideDeployNotification takes the link as a parameter and a test asserts the URL is never off-origin.
store/sw.js: an off-origin target now skips the open-tab loop entirely (WindowClient.navigate is same-origin by spec), and a navigate that rejects falls THROUGH to openWindow instead of focusing a tab that did not move. The "focus anyway" comment described a no-op indistinguishable from a broken notification, and it is what made the bug intermittent.
A stale repo id degrades to the Builds fleet list (resolveBuildsView already handled it); a stale instance id degrades through the existing surface gate.
On the third point: client.url.includes("/console") matching /usage and /preferences is correct — those ARE the console SPA, and moving whichever tab holds it is the intent of a deep link. Left as is, with a comment saying so.
Activity
fix(console): stop the transcript moving under the reader, and give a…
serge-ivo commentedon Aug 7, 2026
Fixed in 1e4c7f1.
Ordering: there is no per-deploy page in the product, and deferring the notification until one existed would not help — the only per-run artifact IS the GitHub Actions run, which is exactly the thing that cannot be navigated to. So it links the stable parent that already exists when the sweep runs: the repo.
deployDeepLink(instanceId, repoId)→/console/instances/:id/coding?builds=<repoId>, which opens the Coding surface on Builds showing that repo's run history — with "View run" out to GitHub one click away, where the CI log belongs. The repo id is already in hand inrunDeployWatch(the row it is sweeping), so nothing about the first-sight watermark rule changes.decideDeployNotificationtakes the link as a parameter and a test asserts the URL is never off-origin.store/sw.js: an off-origin target now skips the open-tab loop entirely (WindowClient.navigateis same-origin by spec), and a navigate that rejects falls THROUGH toopenWindowinstead of focusing a tab that did not move. The "focus anyway" comment described a no-op indistinguishable from a broken notification, and it is what made the bug intermittent.url(stored since migration 0026, returned bySELECT *, never read). A deploy row has noinstanceId, so clicking one previously did nothing at all.notificationRoutestrips the/consolebase so react-router can add its own basename back; a pre-[bug] Deploy notification doesn't deep-link — the target is cross-origin, so client.navigate silently fails and only focuses the tab #338 row pointing at github.com opens in a new tab instead of routing to a 404.resolveBuildsViewalready handled it); a stale instance id degrades through the existing surface gate.On the third point:
client.url.includes("/console")matching/usageand/preferencesis correct — those ARE the console SPA, and moving whichever tab holds it is the intent of a deep link. Left as is, with a comment saying so.fix(api,console): a notification link is a string nothing checked aga…
fix(api,console): a handoff notification lands on the control that an…
fix(console): seventeen response types stop claiming fields the Worke…
fix(deploy-watch): the watermark only moves forward, so a stale GitHu…
fix(deploy-watch): one deploy is one event, so four workspaces watchi…
fix(notifications): unclickable no-url rows get an agent_id fallback (#…