Repository navigation
fix(mobile): give the PR Review paste control a visible result on every tap - #9475
Conversation
…ry tap The paste control runs as a fire-and-forget press handler, and it read the clipboard with no error handling. A read the platform refuses therefore disappeared: no text in the field, no inline validation, no toast, nothing. Read the clipboard through one helper that never rejects. A refused text read, or a refused text probe on the empty path, now reports the paste as denied, so the tap always ends in the field being replaced or a toast that names why nothing was pasted.
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Executive SummaryThe formatter pass fixes both prior findings (over-width import and missing trailing newline) and the only other change collapses an equivalent ternary; no new defects found in the paste-read hardening. Assumptions: none. Files Reviewed (5 files)
Previous Review Summary (commit 5ad6197)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 5ad6197)Status: 2 Issues Found | Recommendation: Address before merge Executive SummaryThe clipboard-read hardening is sound; the new test file fails the repo's blocking Assumptions: none. Overview
Issue Details (click to expand)WARNING
Files Reviewed (5 files)
Reviewed by deepseek-flash-latest · Input: 34.8K · Output: 2.7K · Cached: 221.7K Review guidance: REVIEW.md from base branch |
|
bot: Android emulator proof of the PR Review paste control, on the local dev stack with an owned non-production test account. Each capture is a real tap on the paste control in the PR link field row:
|
Reformat the new test's import and the toast selection to the shared print width, and end the file with a newline.
|
bot: Android emulator proof re-run at the current head (local dev stack, owned non-production test account, fresh Metro bundle). Every capture is a real tap on the paste control in the PR link field row:
|
|
bot: Android emulator proof re-run at head a178640 (local dev stack, non-production test account, dev client on Metro serving this checkout). Each capture is one real tap on the paste control in the PR Review link row, with the
No app source, |


















Evidence
Evidence: demonstrated — on the Android emulator with the local dev stack, a non-production test account and the dev client at head a178640, every tap of the PR Review paste control ended in a visible result, including the refused-read case, which Android itself produced by denying this app its clipboard-read app-op: on the merge base the same tap left the screen untouched, at this head it shows the denied toast.
What the reported tap actually hit
The reported state — empty clipboard, tap, nothing — already ends in the
Clipboard is emptytoast on the merge base (19c6e8f2f), re-measured here. The tap that still ended in nothing was a refused clipboard read: the merge-basehandlePasteawaitedClipboard.getStringAsync()andhasStringAsync()with no handling while the press handler calls it asvoid handlePaste(), so a rejection produced no field write, no inline state, no toast and no navigation.Change
apps/mobile/src/lib/pr-review/pr-link-paste.tsgainsreadPrLinkPasteClipboard(reader), which never rejects: a rejected text read, or a rejectedhasStringAsync()probe, becomes{ kind: 'empty', reason: 'denied' }, while the existing decisions are unchanged.pr-review-entry-screen.tsxcalls it instead of the raw clipboard API. So a PR link fills the field and opens, other text fills it with the existing inline invalid notice, an empty clipboard showsprReview.linkPasteClipboardEmpty, and a refused read showsprReview.linkPasteDeniedinstead of nothing. No new i18n key.End-to-end on the emulator (real stack, no stubs)
Real device path: emulator
omp36at the default font scale and density, the CI dev-client APK, Metro serving this checkout, the local dev stack on this VM, a non-production test account. Every assertion below is auiautomatornode from a dump taken with the screenshot of the same tap.Clipboard is empty; field empty, nothing opensNot a pull request or merge request link, nothing opensPaste access was denied. Allow paste and try again.; field empty, nothing opensThe refusal is the platform's, not a stub:
appops set com.kilocode.kiloapp READ_CLIPBOARD denymakes Android refuse this app's clipboard read —appops getrecords a new rejection at tap time, and logcat showsjava.lang.SecurityException: … not allowed to perform READ_CLIPBOARDraised insideClipboardService. No app source,node_modulesfile or bundle was modified for any step. That app-op also breaksTextView.canPasteduring an accessibility dump, so it is reset right after the tap and before the dump; the toast outlives that.Before/after used the same denial: the merge-base versions of the two changed sources were staged for Metro, the app force-stopped and relaunched, the denial applied, then the head sources restored with
git status --porcelainempty.Tests
pr-link-paste.test.tscovers each decision plus both rejection paths.pr-review-entry-screen-paste-read.test.tscovers the press handler for a rejected read and for a rejected probe; both fail at the merge base (no toast) and pass here.Limits
Android's clipboard service normally answers the focused app, so a refusal needs the platform app-op denial used above. The declined-paste-prompt heuristic (an empty read while the text probe still sees text) is unchanged and is not reachable on this VM.
Design gate: 4 cells, 0 errors, 0 warnings — links
/pr-reviewon the base, dark, compact and rtl cells; the merge-base run intobase/is also 4 cells, 0 errors, 0 warnings.Production prerequisites
None.
Proxy answers
The owner's proxy gave these answers in the owner's place.
void handlePaste(). What should this work item do?Answer: Make every read failure end in a toast
Confidence: 0.80 · Precedent: Autonomy: apply the reasonable fix when a defect does not reproduce; speed: choose the simplest design
Answer: prReview.linkPasteDenied
Confidence: 0.60 · Precedent: Speed and least work: the brief asks to reuse the existing toast copy