Skip to content

Research first: audit every push notification so each opens the exact subject it is about, not the generic console #894

Open

Description

@proagentstore

Status 2026-10-08 (#972 audit) — labelled needs-human. Per the owner's own status comment
on this issue (after c533ac7d): the audit and the code work are done — every producer is
listed in code and NotificationSubject (workers/api/src/lib/console-links.ts) has 13 kinds
covering all 20 notifyUser call sites. One item needs a person with devices to verify
deep-link opening on a real phone/desktop install, which is why this cannot be finished
autonomously. Earlier comments also hold open questions addressed to the owner.

Request from Serge: check ALL recent push notifications and make sure each opens the real details it is about in the UI, not just the generic app.

Known cause

store/sw.js falls back to /console/ when a payload has no url (data: { url: data.url || "/console/" } and again in notificationclick). Several producers send no url at all, so the click lands on the generic console. Confirmed callers passing undefined for the link: workers/api/src/lib/engine-reauth-expiry.ts (~line 159) and workers/api/src/routes/coding-reauth.ts (~line 184, 'Coding engine signed in'). Others (triggers.ts, instances.ts, agent-loop.ts, coding-watch.ts, deploy-watch.ts, job-apply.ts, browser-task.ts, coding-session-sweeper.ts) must be checked one by one.

Do

  1. Audit: list every notifyUser / sendPushToUser call site and the notification types actually sent over the last weeks (use the notifications table/history). For each, record what it is about (run, session, instance, ticket/issue, deploy, engine sign-in, approval, résumé...) and what URL it opens today.
  2. Fix: every notification must carry a same-origin deep link to the exact page for its subject (e.g. the specific run, coding session, instance, board card/issue, deploy, or the engine sign-in screen for that instance), with the right tab/anchor/query so the relevant details are visible without hunting. Add a deepLinkFor(type, subject) helper so producers cannot forget.
  3. Make a missing url a hard error in dev/tests (and log + alert in prod) instead of silently defaulting to /console/.
  4. Cover the in-app Notifications page too: clicking an item must open the same target, and an item whose subject was deleted must show a clear 'no longer exists' state, not a blank page.
  5. Verify cold-open paths: PWA closed, console tab already open, signed-out (login then continue to the target), iOS and Android.
  6. Tests: a unit test per notification type asserting the url resolves to a real console route, plus a service-worker click test.

Commit direct to main (no PR); closes this issue when done.

Activity

  1. changed the title [-]Audit every push notification: each one must open the exact thing it is about, never the generic console[/-] [+]Research first: audit every push notification so each opens the exact subject it is about, not the generic console[/+] on Oct 1, 2026
  2. serge-ivo commented on Oct 1, 2026

    @serge-ivo
    Contributor

    Research findings for #894: where every notification click lands today

    Research only. Nothing in the code has been changed. Base: main @ c69a16cb. Everything below comes from reading the source, plus the last 200 rows of the owner's own notification feed (read through the ProAgentStore MCP list_notifications, 2026-09-30 04:24 → 2026-10-01 12:18 UTC).

    Summary. Most of this already exists: a single link builder (workers/api/src/lib/console-links.ts, #344) checked by a test against the console's route table. All 200 recent notifications carry a specific link. The real defects are narrower than "generic console":

    1. Three producers send no url and fall back to /console/.
    2. On console.proagentstore.online, every push click lands on the home screen, because producers emit /console/… paths and that host's router has no /console prefix.
    3. Signing in drops the query string, so a signed-out deploy tap loses ?builds=.
    4. The OS notification tag is the type ("coding"), so a newer coding notification silently replaces an older one still in the tray, along with its link.
    5. Missing urls are silently allowed: url is optional and nothing fails.

    1. Every notifyUser / sendPushToUser call site

    notifyUser (workers/api/src/routes/push.ts:242) writes the notifications row (createNotification, push.ts:281), then calls sendPushToUser(env, userId, { title, body, url, tag: type }) (push.ts:285). url is optional in both (push.ts:144, push.ts:248).

    # File:line type / kind What it's about URL it opens today Specific?
    1 workflows/coding-session.ts:1037 coding / update Coding run ended ("✅ Coder finished" / "⚠️ Coder stopped") codingSessionLink(instanceId, sessionId) → /console/instances/:id/coding/:sessionId ✅ the run's session
    2 workflows/coding-session.ts:614 (pause notify) coding / alert or update Run parked: needs a human ("🙋 Coder needs you"), usage limit, engine sign-in codingSessionLink(instanceId, sessionId) ✅ session. For an engine_auth park the sign-in itself has no console page (§2)
    3 workflows/coding-watch.ts:106 coding / update Watched session finished ("✅ Coder finished") codingSessionLink(instanceId, sessionId) ✅
    4 lib/coding-session-sweeper.ts:414 coding / update Idle engines put to sleep ("😴 … went to sleep") sleepNotification().url (:289): codingSessionLink(instance) when one instance, else undefined ⚠️ one instance → its Coding tab (no session). Several → none → /console/
    5 routes/coding-reauth.ts:184 coding / update "🔑 Coding engine signed in" (+ runs to continue) undefined → /console/ ❌ generic
    6 lib/engine-reauth-expiry.ts:159 coding / alert "⏳ sign-in expires in ~5 min" (#890, mine) undefined → /console/ ❌ generic
    7 workflows/agent-loop.ts:371 loop / alert Chat-driver run stopped, needs a human instanceLink(instanceId) → /console/instances/:id (Assistant) ⚠️ the instance's chat, not the run
    8 workflows/browser-task.ts:184 apply / alert Browser task handoff: needs input / captcha / stuck instanceRunLink(instanceId, taskId) → /console/instances/:id/tasks/:taskId ✅ the run, with takeover controls (#349)
    9 workflows/job-apply.ts:397 apply / alert Job application handoff instanceRunLink(instanceId, taskId) ✅
    10 lib/deploy-watch.ts:510 deploy / update "✅ Deployed sha" / "❌ Deploy failed sha" deployDeepLink() (:137) → codingBuildsLink → /console/instances/:id/coding?builds=:repoId ✅ that repo's Builds view (there's no per-deploy page; GitHub's run page is cross-origin by design, #338)
    11 lib/triggers.ts:295 trigger / update "⏭️ Scheduled run skipped" (runner offline / busy) instanceBoardLink() → /console/instances/:id/board ⚠️ instance Board, not the trigger
    12 routes/instances.ts:302 subscribe / update New subscriber (to the agent's creator) agentLink(agent.id) → /console/agents/:id ✅
    13 lib/resume-parse.ts:54 apply Résumé not a PDF instanceKnowledgeLink() → /console/instances/:id/knowledge ✅
    14 lib/resume-parse.ts:129 apply "✅ Résumé parsed" profileLink() → /console/profile ✅
    15 lib/resume-parse.ts:134 apply No API key to auto-fill profileLink() ✅
    16 lib/resume-parse.ts:142 apply Auto-fill error profileLink() ✅
    17 lib/resume-parse.ts:148 apply "⚠️ Couldn't parse your résumé" instanceKnowledgeLink() ✅
    18 routes/push.ts:123 (sendPushToUser direct) test push "Push notifications are working ✅" consoleHomeLink() → /console/ n/a, generic on purpose

    There's no other writer: INSERT INTO notifications only appears in lib/notifications.ts (createNotification), and notifyUser is its only production caller.

    What was actually sent (last 200 rows, about 32h):

    Count type Title URL shape kind
    73 deploy ✅ Deployed sha /console/instances/:id/coding?builds=:repo update
    39 deploy ❌ Deploy failed sha same update
    68 coding ✅ Coder finished /console/instances/:id/coding/:session update
    10 coding ⚠️ Coder stopped same update
    7 coding 🙋 Coder needs you same alert
    3 coding 😴 … Coder went to sleep /console/instances/:id/coding update

    Every row in that window carries a specific url. The no-url producers (#4 multi-instance, #5, #6) fired 0 times in the window, which is why use hasn't surfaced them. The feed also shows 6,161 unread, mostly deploy noise. Not this issue, but worth knowing.

    2. Console routes per subject

    The route table is store/console/src/lib/routes.ts (CONSOLE_ROUTES, INSTANCE_TABS). It's held against App.tsx by routes.test.ts, and every console-links.ts builder is checked against it by console-links.test.ts. That test also forbids /console/… literals anywhere else in workers/api/src. InstanceDetail reads only 2 splat segments (tab, sessionId); anything deeper is dropped (MAX_INSTANCE_SPLAT).

    Subject Route that exists Builder in console-links.ts Deep-linkable on a cold open?
    Coding run / session instances/:id/coding/:sessionId codingSessionLink ✅ path params. The session row outlives the run (#338)
    Chat-driver (agent-loop) run none per run, only instances/:id (Assistant) instanceLink ❌ no run page. /v1/instances/:id/loop/:runId exists in the API but has no console page
    Instance (+ any tab) instances/:id/<tab> for each of INSTANCE_TABS instanceLink, instanceBoardLink, instanceKnowledgeLink ✅
    Browser task / approval / takeover instances/:id/tasks/:taskId (RunDetail) instanceRunLink ✅ shows its own "no longer exists" state with a way back to the Board for a deleted task (per the builder's doc)
    Board card / ticket / issue only the Board tab, instances/:id/board instanceBoardLink ⚠️ no per-card route or card query param
    Deploy instances/:id/coding?builds=:repoId codingBuildsLink ✅ read by deepLinkedBuildsRepo() (store/console/src/lib/deepLink.ts), but see the sign-in caveat in §4
    Engine sign-in (re-auth) none. The console has no UI for coding_engine_reauth: no references to engine-reauth, needsReauth or signInExpiry under store/console/src none ❌ there is no screen to link to
    Notifications notifications none ✅
    Usage / billing usage, profile profileLink ✅

    3. In-app Notifications page

    store/console/src/pages/Notifications.tsx:84-103. On click it:

    1. Marks the row read (POST /v1/notifications/:id/read, failure ignored).
    2. Calls notificationRoute(n.url) (lib/deepLink.ts). That strips a leading /console so the in-router path works under either basename, then navigate(route).
    3. Otherwise, for an https:// url (rows from before [bug] Deploy notification doesn't deep-link — the target is cross-origin, so client.navigate silently fails and only focuses the tab #338 point at GitHub Actions), opens it in a new tab.
    4. Otherwise, for a row with agent_id and no url (before [bug] Deploy notification doesn't deep-link — the target is cross-origin, so client.navigate silently fails and only focuses the tab #338), opens /agents/:agent_id.
    5. Otherwise just reloads the list. So a row with no url (Multi-provider repos: private clone + identity for GitLab / Bitbucket #5, Server-side deployment notifications (deploy succeeded/failed → push), not polling-only #6, multi-instance Issues → Kanban: a provider-agnostic task board with GitHub-issue import #4) does nothing visible on click.

    So the in-app list already follows the row's url and works on both hosts. The gaps:

    • A row without a url is a dead click.
    • A deleted subject gets a clear "no longer exists" state only on tasks/:taskId (RunDetail). For coding/:sessionId the builder doc says the Coding tab "falls back to the repo list", which is a real page but doesn't say the session is gone. I haven't verified an unknown instances/:id, and suspect it's a generic error rather than a specific "this agent no longer exists".

    When a console tab is visible, the service worker suppresses the OS banner and posts pags:push-suppressed to the tab (store/sw.js:25-36). Layout.tsx:85 only refreshes the bell badge from that message. There's no in-app toast and nothing to click, so a user looking at another screen sees only the count change.

    4. Service worker and cold open

    store/sw.js:

    • push (:8-48): the payload defaults to url: "/console/" and is merged with the push JSON. showNotification(..., { tag: data.tag || "pags", data: { url: data.url || "/console/" }, requireInteraction: true }). tag = the notification type (push.ts:285).
    • notificationclick (:65-90): target = data.url || "/console/". Then:
      • Same-origin target: it finds a window client whose URL includes /console, calls client.navigate(target) and focuses it. If navigate rejects it falls through to openWindow(target).
      • Cross-origin target: it skips the tab loop and goes straight to openWindow.

    What that means in each situation:

    Situation What happens today
    PWA / browser closed openWindow(target) cold-loads the deep URL. The host worker serves the console shell for any /console/* path (workers/host/src/index.ts:210-213), and the router resolves it. ✅ on proagentstore.online
    Console tab already open (not visible) client.navigate(target) + focus moves the existing tab. ✅
    Console tab visible No OS banner at all; only the badge updates (§3)
    console.proagentstore.online ❌ Every push click lands on the home screen. There the router basename is / (App.tsx:103-107), but producers emit /console/instances/…. The host serves the shell for that path, the router finds no /console/... route, and <Route path="*" element={<DefaultRedirect />} /> (App.tsx:96) redirects to the landing screen. The in-app list is immune, because notificationRoute strips the prefix; the service worker navigates the raw url. (It still finds the open tab, but only by accident: c.url.includes("/console") matches the hostname.)
    Signed out AuthGate renders <Login/> in place, so the URL is kept. signIn() sends return_to = window.location.href (lib/auth.ts:36-39), so you come back to the deep URL. ✅ for paths. ❌ query strings: handleOAuthCallback runs history.replaceState({}, "", window.location.pathname) (auth.ts:50), which removes ?session= and every other param, so a deploy link's ?builds=repo is lost and you land on the plain Coding tab
    Two notifications of the same type ❌ With the same tag ("coding"), the OS replaces the earlier one. A "🙋 Coder needs you" (alert) for session A is overwritten by "✅ Coder finished" for session B, and A's link is gone from the tray. The row is still in the feed
    iOS Web push only, and only from an installed home-screen PWA (iOS 16.4+). The native app ios/PAGS is a SwiftUI API client (APIClient.swift, AuthSession.swift) with no push code at all (no APNs, no UNUserNotificationCenter). Not verified on a device: whether iOS WebKit's WindowClient.navigate() moves an open PWA. If it rejects, the fall-through to openWindow should still land on the target
    Android Web push in Chrome and the installed PWA work the same as desktop. The native app (android/app/.../MainActivity.kt) is an OkHttp API client with no FCM and only the launcher intent filter, so it receives no push and can't open a link

    5. Recommended design

    Principles: every notification names its subject, and the url is derived from the subject rather than chosen at each call site. A missing url can't compile or pass tests. The service worker and the router agree on the base path.

    a) deepLinkFor(subject) in console-links.ts. A discriminated union, so producers pass what it's about and get the link:

    type NotificationSubject =
      | { kind: "coding-session"; instanceId: string; sessionId: string }
      | { kind: "coding-tab"; instanceId: string }                   // sleep with one instance, no session
      | { kind: "deploy"; instanceId: string; repoId: string }
      | { kind: "browser-task"; instanceId: string; taskId: string } // approvals / takeover
      | { kind: "agent-loop-run"; instanceId: string; runId: string }
      | { kind: "engine-sign-in"; instanceId: string }
      | { kind: "trigger"; instanceId: string; triggerId?: string }
      | { kind: "agent"; agentId: string }
      | { kind: "knowledge" | "board"; instanceId: string }
      | { kind: "profile" } | { kind: "account" };

    Then make notifyUser take subject (required) instead of url?, store the url and the subject (subject_kind, subject_id: one migration, nullable, filled for new rows only), and drop the free-form url parameter. Existing builders stay as the implementation.

    b) Target for each producer:

    Producer Target
    #1 #2 #3 coding end/pause/watch unchanged (coding-session). For an engine_auth park, use engine-sign-in
    #4 sleep one instance → coding-tab (today). Several → /console/instances (the list) instead of nothing, or one notification per instance. Your call, see Q3
    #5 "engine signed in" engine-sign-in. Better: the session of the first resumable run (resumableRuns[0] has a session_id), since the next step is "continue that run"
    #6 "sign-in expires" (#890) engine-sign-in
    #7 agent-loop needs you today instanceLink (the Assistant). Fine unless we add a run page (Q2)
    #8 #9 handoffs unchanged
    #10 deploy unchanged, plus fix the sign-in query loss
    #11 trigger skipped /console/instances/:id/settings (or wherever triggers are edited) rather than the Board. Needs confirming which tab hosts triggers
    #12–#17 unchanged

    c) An engine sign-in screen. It doesn't exist, so #5 and #6 have nowhere specific to go. Smallest version: a "Sign-in" panel on instances/:id/coding?signin=1 (query param, like ?builds=). It shows the pending flow (GET …/coding/engine-reauth: url, device code, signInExpiry countdown) and a Start button. Mobile matters here, since that's where the device code gets entered.

    d) Service worker:

    • Normalise the target against the live base. In notificationclick, rewrite /console/… → /… when self.location.hostname === "console.proagentstore.online". Better: share notificationRoute's rule, or have the API emit base-relative paths and the service worker prepend the base.
    • Match console tabs by path under the service worker's scope, not url.includes("/console").
    • Use a per-subject tag (e.g. ${type}:${subject_id}) so different subjects don't overwrite each other, while repeats of the same subject still collapse.
    • In dev, log a missing data.url loudly instead of silently defaulting.

    e) Login continuation: in handleOAuthCallback, delete only session from the search params and keep the rest plus the hash.

    f) Missing-url policy (item 3 of the issue): with subject required by type, missing becomes a compile error. Keep a runtime guard too: in tests/dev throw if deepLinkFor returns nothing. In prod, write the row with /console/notifications (the feed, where the text is at least readable) and logEvent an error so it shows up in Errors. Never use /console/.

    g) Deleted subjects (item 4): give coding/:sessionId, instances/:id and agents/:id the same explicit "this … no longer exists" state that RunDetail has, with a way back to the parent list.

    6. Implementation plan

    1. console-links.ts: add deepLinkFor and the subject union. Test: every kind resolves through checkConsoleLink (extend console-links.test.ts).
    2. notifyUser(…, subject, opts): migrate all 18 call sites. Make url internal. The migration adds subject_kind and subject_id.
    3. Engine sign-in panel (?signin=1 on the Coding tab). Point Fix MCP OAuth sign-in deadlock, add Google option #2 (engine_auth park), Multi-provider repos: private clone + identity for GitLab / Bitbucket #5 and Server-side deployment notifications (deploy succeeded/failed → push), not polling-only #6 at it.
    4. sw.js: base normalisation, scope-based tab matching, per-subject tag. Add a service-worker click test: a Node harness with a fake clients. There's no service-worker test today.
    5. auth.ts: keep the query and hash after the OAuth return. Unit test it.
    6. "No longer exists" states for session, instance and agent pages.
    7. Notifications page: rows with no url (old ones) open the subject if subject_kind is set, otherwise show a plain "nothing to open" toast instead of a silent reload.
    8. Verify on devices: desktop Chrome (closed / open / visible tab), Android Chrome plus installed PWA, iOS installed PWA, console.proagentstore.online and proagentstore.online/console, signed out.

    Risks:

    • Changing notifyUser's signature touches every producer. Do it in one commit, with the type system finding the stragglers.
    • The service worker is cached: users keep the old sw.js until it updates (skipWaiting + clients.claim are already set, so the next load picks it up).
    • Per-subject tags mean more tray entries. That's fine for alerts, but could make deploy pings noisier (112 in 32h). See Q4.
    • I can only verify iOS behaviour on a device.

    Open questions for Serge:

    • Q1. Which host do you (and the PWA install) use day to day, console.proagentstore.online or proagentstore.online/console? If it's the subdomain, the base-path bug in §4 is probably the "always opens the generic app" you're seeing, and it should be fixed first.
    • Q2. Do chat-driver runs need a run page (instances/:id/runs/:runId), or is the Assistant the right landing for "your agent needs you"?
    • Q3. For a sleep batch covering several instances: one notification per instance, or one linking to the Instances list?
    • Q4. Deploy notifications are 56% of the feed. Should per-subject tags apply to them too (one tray entry per repo, replaced by the latest), or keep collapsing all deploys into one?
    • Q5. Engine sign-in screen: a panel on the Coding tab (?signin=1), or a section of instance Settings?
    • Q6. Native apps: is push for the iOS and Android apps in scope? Today they have no push at all, so item 5's "iOS and Android" can only mean the installed web app.
  3. serge-ivo commented on Oct 1, 2026

    @serge-ivo
    Contributor

    Corrections and additions to the findings above, from a second read of the console code:

    • Approvals: the comment above lists approvals under instances/:id/tasks/:taskId. That's only half right. Takeover and needs-input are on RunDetail (RunDetail.tsx:565-576; the takeover overlay opens on a button, not automatically, and ?ask=1 is read at RunDetail.tsx:338-339). But approving a runtime task is a button on the Board card (BoardTab.tsx:179), and RunDetail has no approve control. So an approval notification currently has to land on /instances/:id/board. Fix: add an approve control to RunDetail so a single deep link covers it, or give the Board a card-focus param.
    • agents/:id/:tab ignores :tab: AgentDetail reads only id (AgentDetail.tsx:79), and the tab is useState("chat") (:82). A creator-side link to a specific agent tab would silently open Chat.
    • In-app rows without a url: those rows do carry instance_id (migration 0147_notification_instance.sql), but Notifications.tsx never falls back to it. A cheap interim fix for the three url-less producers is to fall back to /instances/:instance_id.
    • Possible conflict, unverified: on a ?builds= cold open, CodingTab may still auto-open the last repo's active session and rewrite the URL (compare CodingTab.tsx:197,208 with :372,410-424). Worth a device check before the deploy link is called done.
    • Engine sign-in UI: there is an in-session EngineSigninPrompt (agents/coder/web/src/EngineSigninPrompt.tsx, shown at CodingTab.tsx:918), but no standalone screen. For now the session URL is the closest target for the sign-in notifications.
  4. serge-ivo commented on Oct 1, 2026

    @serge-ivo
    Contributor

    Open questions for Serge

    1. Which address do you normally use for the console, and where did you install the phone/desktop app from: console.proagentstore.online or proagentstore.online/console? On the console. address, tapping any notification currently opens the home screen instead of the item it's about.
      Default if unanswered: support both, and fix the console. address first, since it may be the cause of "every notification opens the generic app".

    2. When a chat-style agent run stops and needs you, should the notification open a dedicated page for that run, or the agent's chat (where you'd answer it)?
      Default if unanswered: keep opening the agent's chat. Build a run page only if you find yourself needing the run's history there.

    3. When several coding agents go to sleep at once, do you want one notification per agent, or one notification that opens the list of your instances?
      Default if unanswered: one notification that opens the Instances list, so a batch doesn't buzz several times.

    4. Deploy notifications are over half of what you receive. On your phone or desktop, should each repo keep its own notification (newer deploys of the same repo replace the older one), or should all deploys share a single notification that the latest one replaces?
      Default if unanswered: one notification per repo, with the latest replacing the previous one for that repo. Alerts that need you (e.g. "Coder needs you") always get their own notification and are never replaced.

    5. Signing a coding engine back in needs its own screen to link to. Should that be a panel inside the instance's Coding tab, or a section in the instance's Settings?
      Default if unanswered: a panel in the Coding tab. That's where you are when a run is blocked, and it works well on a phone for entering the sign-in code.

    6. Should the native iOS and Android apps get push notifications as part of this work? Today they receive none, and only the installed web app does.
      Default if unanswered: no. Scope this issue to the web app (browser and installed app), and track native push separately.

  5. added 2 commits that reference this issue on Oct 5, 2026
  6. serge-ivo commented on Oct 7, 2026

    @serge-ivo
    Contributor

    Status after c533ac7 (on top of #897 / 3b3b790). Not closed: one item needs a person with devices.

    Done, against the "Do" list:

    1. Audit: every producer is now listed in code. NotificationSubject in workers/api/src/lib/console-links.ts has 13 kinds covering all 20 notifyUser call sites, plus the test push.
    2. deepLinkFor(subject): returns a branded DeepLink, and notifyUser accepts only that type, so a producer can't forget the link or hand-write one. Two wrong targets were fixed along the way:
      • a coding handoff or sign-in pause with no board task opened the repo list, because the pause had no session id; it now opens the run's session;
      • "Scheduled run skipped" now opens Settings, where triggers live, instead of the Board.
    3. Missing link: a compile error in dev, through the type. At runtime it is logged at error level, so it shows in the error summary, and still opens the instance or the feed. This "alert" is the error log, not a push to the operator.
    4. In-app list and deleted subjects:
    5. Tests:

    Left for a human (item 5): cold-open checks on real devices, which code can't verify. With the installed PWA:

    • closed;
    • a console tab already open;
    • signed out;
    • on iOS (16.4+) and on Android Chrome;
    • on both console.proagentstore.online and proagentstore.online/console.

    Once those check out, this can be closed. The open questions were taken at their stated defaults: chat runs open the Assistant, a multi-instance sleep opens the Instances list, deploys get one tray entry per repo, the sign-in prompt is in the session view, and native apps are out of scope.

  7. added a commit that references this issue on Oct 7, 2026
  8. serge-ivo commented on Oct 14, 2026

    @serge-ivo
    Contributor

    Owner decision on 2026-10-14: A — keep this open as needs-human until real-device verification is done. No further dev is intended from this ticket unless the manual cold-open checks on real installed PWA/device flows find a bug. Simulated/code evidence alone is not enough to close it.

  9. serge-ivo commented on Oct 17, 2026

    @serge-ivo
    Contributor

    Re-audit on main 8ec5248 (2026-10-17, read-only). The code side still holds. Still open per the owner's decision A (real-device cold-open checks).

    • The guard holds:
      • notifyUser accepts only the branded DeepLink (routes/push.ts:302), and a missing link is logged at error (:307-311).
      • The only as DeepLink cast is inside the builder (console-links.ts:345).
      • The 25 producer files added or changed since c533ac7 all go through deepLinkFor; the subject union has grown from 13 to 20 kinds.
    • Live feed, 259 rows over ~12h: every row has a specific deep link. None is empty, none is the generic /console/, and nothing fell back to the error path. By type:
      • deploy 127 → Builds;
      • coding 69 → session;
      • coder_question 30 → the run card;
      • apply 10 → Board;
      • ci 6 → Builds;
      • po_triage_digest 6 → Settings;
      • permission 6 → Permissions, focused;
      • local-browser 4 → the research run or Settings;
      • secure-input 1 → the request.
    • One narrower gap, by design today: apply notifications open the whole Board, not the application's card. The Board is where "Approve & continue" lives, and the link has no card focus. Filed as Application notifications open the whole Board, not the application's card (follow-up to #894) #1136, with the two options for the owner.

    No code changed.

  10. added
    needs-computerNeeds Serge at his computer (manual check, local deploy, sign-in, browser verification)
    and removed on Oct 23, 2026
  11. proagentstore commented on Oct 24, 2026

    @proagentstore
    Author

    ❓ The coder stopped on a question: Issue #894 is fully implemented (fixes in commits 3b3b790 & c533ac7, 122 tests passing), but the CLI refuses to push a "closes #894" commit because a 2026-10-14 comment on the issue says to keep it open until real-device cold-open checks pass. How should I proceed?

    Options: Push the closing commit now — real-device checks are my responsibility; not the CLI's · Leave it open — I'll close #894 myself after device checks · Close the issue via gh issue close without a new commit

    Why it asks: The CLI is refusing to push because of a standing owner ruling on the issue. Only the owner can reverse or confirm that ruling.

    Answer it on the run's card or with answer_instance_input. The run is parked until it is answered — answer any time; it continues from where it stopped.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingneeds-computerNeeds Serge at his computer (manual check, local deploy, sign-in, browser verification)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions