Skip to content

Phone push: Channel title, preview and approval buttons, plus registration retry and Simulator flow timeout - #1874

Merged
DoodleBears merged 9 commits into
mainfrom
claude/mobile-push-1655-tf813s
Oct 15, 2026
Merged

DoodleBears merged 9 commits into
mainfrom
claude/mobile-push-1655-tf813s

Conversation

@DoodleBears

@DoodleBears DoodleBears commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Vain

Why the change

Two-line notifications (Host)

Before: a phone notification only named the PersonaBot and the kind of request, for example "Ada needs your approval". You couldn't tell from the lock screen what it was about.

After: the title is the Channel (#ops), or the PersonaBot's name for its DM. The body says what's needed plus a short preview, for example Ada needs your approval: bash · date > now in a Channel, or Has a question for you: Which branch? in a DM.

  • Mentions and replies show the message text.
  • Private input requests still show no content.
  • Previews collapse whitespace and stop at 140 characters.
  • ADR-0213 replaces ADR-0202's content-free payload rule, and the Notifications settings hint (en/zh) now says what a notification shows.

Decide from the notification

Before: to decide a Tool approval you tapped the notification, waited for the conversation and then tapped the card.

After: a Tool approval notification has Always allow, Allow once and Reject buttons (long-press on iOS, expand on Android).

  • Always allow covers this exact command only, the same as on the card.
  • Reject is marked destructive and comes last.
  • secret_cli gets only Allow once and Reject, because its card has no Always allow. The same applies when the Host can't read the request.
  • Every button requires the phone to be unlocked.
  • A button opens the app, which sends the decision to the Host named in the push, trying each saved route in turn. It then shows the card in its conversation, already decided. If the decision can't be sent, the card stays pending there.
  • Swiping the notification away decides nothing.

The buttons open the app because iOS doesn't run the app's JavaScript for a background action once the app has been closed. Deciding without opening the app would need native code.

Push registration retry (mobile app)

Before: if a computer was unreachable when the app launched, Notifications kept showing "还没能把这台手机告诉 " until the app was relaunched.

After: the app retries the computers that failed when the current one's connection comes back, and when the app returns to the foreground.

Simulator flow timeout (scripts/e2e-mobile-sim.mjs)

Before: each Maestro flow was killed after 300 s, which is also how long Maestro gets just to start its iOS driver.

After: each flow gets the driver startup time plus five minutes.

Issue / Discussion: Refs #1655. Follows #1837.

Special things to note

  • Merge risk: low to medium.
    • Notification text now goes through Expo Push Service and APNs/FCM. That was the user's explicit request, and each kind can still be turned off per phone. The data payload and the push developer log are unchanged and still hold ids only.
    • The buttons use expo-notifications' category API, which is already in the app, so no native rebuild is needed.
  • Verification:
    • packages/core/test/push-delivery.test.ts covers titles, bodies, the tool-approval and tool-approval-once categories, approval previews and truncation, and private input staying content-free.
    • apps/mobile/test/push.test.ts covers each button's decision: the right Host, falling back across routes, ignoring other buttons and kinds, and an unpaired or unreachable Host. It also covers the retry for a computer that was offline at launch.
    • The mobile tests, tsc and pnpm lint (including the CLI parity check) are clean.
    • Merged now at Vain's request so the real-iPhone checks run on main. The Simulator timeout is still unproven on the Mac.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Eg4d3a4zKNVhnV2ke5b7sX

The runner killed each Maestro flow after 300 s, the same time Maestro may
spend starting its iOS driver. On a busy Mac the driver start and the app
reinstall took about 4.5 minutes, so the first flow was killed before the
app loaded.

Issue: #1655
Agent-Task: claude/cloud/session_01Eg4d3a4zKNVhnV2ke5b7sX
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eg4d3a4zKNVhnV2ke5b7sX
@coderabbitai

coderabbitai Bot commented Oct 15, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

The app sent its push token to each paired Host only at launch, on pair,
and from the off-state button. A Host that was unreachable at launch stayed
in the failed list until the app was relaunched. The push session now
retries the failed Hosts when the current Host's connection comes up and
when the app returns to the foreground.

Issue: #1655
Agent-Task: claude/cloud/session_01Eg4d3a4zKNVhnV2ke5b7sX
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eg4d3a4zKNVhnV2ke5b7sX
@DoodleBears DoodleBears changed the title Give each Simulator flow its driver startup time plus five minutes Register the phone with a Host that was offline at launch, and give Simulator flows driver startup time Oct 15, 2026
A push now has a title and a body. The title is the Channel name
(#name), or the PersonaBot's name for its DM. The body says what the
PersonaBot needs, followed by a short preview: the tool and its main
argument for an approval, the question text, or the message text.
Private input requests stay content-free, and previews stop at 140
characters. ADR-0212 replaces ADR-0202's content-free payload rule, and
the Notifications settings hint now says what a notification shows.

Issue: #1655
Agent-Task: claude/cloud/session_01Eg4d3a4zKNVhnV2ke5b7sX
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eg4d3a4zKNVhnV2ke5b7sX
@DoodleBears DoodleBears changed the title Register the phone with a Host that was offline at launch, and give Simulator flows driver startup time Phone push: show the Channel and a preview, retry registration, and give Simulator flows driver startup time Oct 15, 2026
Main took ADR-0212 for PersonaBot creation, so the push preview record
moves to the next free number.

Issue: #1655
Agent-Task: claude/cloud/session_01Eg4d3a4zKNVhnV2ke5b7sX
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eg4d3a4zKNVhnV2ke5b7sX
A Tool approval push now carries the tool-approval category. The app
registers Allow once and Reject buttons for it, both requiring unlock.
A button opens the app, which sends the decision to the Host named in
the push (trying each saved route), then shows the card in its
conversation. If the decision cannot be sent, the card stays pending
there. The buttons open the app because iOS does not run JavaScript for
a background action when the app was closed.

Issue: #1655
Agent-Task: claude/cloud/session_01Eg4d3a4zKNVhnV2ke5b7sX
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eg4d3a4zKNVhnV2ke5b7sX
@DoodleBears DoodleBears changed the title Phone push: show the Channel and a preview, retry registration, and give Simulator flows driver startup time Phone push: Channel title, preview and approval buttons, plus registration retry and Simulator flow timeout Oct 15, 2026
Swiping a notification away already leaves the approval undecided, so
the buttons now lead with the two ways to allow: Always allow (this
exact command, as on the card), Allow once, then Reject, marked
destructive. The Host picks tool-approval-once, without Always allow,
for secret_cli or when it cannot read the request.

Issue: #1655
Agent-Task: claude/cloud/session_01Eg4d3a4zKNVhnV2ke5b7sX
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Eg4d3a4zKNVhnV2ke5b7sX
@DoodleBears
DoodleBears marked this pull request as ready for review October 15, 2026 14:34
@DoodleBears
DoodleBears merged commit 214148f into main Oct 15, 2026
9 checks passed
@DoodleBears

Copy link
Copy Markdown
Contributor Author

Batch QA — push (heads 223ccde → ad30e66)

Real iPhone (Release build of the batch with #1884's projectId, APNs key on the EAS project) + QA Host.

Step Result
Registration ✅ after relaunch the phone registered with the Host (the first launch hit a moment when the Host was unreachable and did not retry — fixed by this PR's retry, not re-tested on the phone)
Approval push from a Bot DM ✅ Host sent it ~0.5 s after the approval was raised, Expo ticket ok
Approval push from a group Channel (@bot) ✅ sent ~0.5 s after, Expo ticket ok
Notification text / action buttons on the phone reported by Vain on the phone; buttons test on ad30e66 pending
Simulator retry check untested — the Simulator build has no EAS projectId, so it shows "cannot receive notifications" instead
Android untested

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